netsurf: branch master updated. release/3.9-490-g360997c
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/360997c265c10a47a5421...
...commit http://git.netsurf-browser.org/netsurf.git/commit/360997c265c10a47a5421c4...
...tree http://git.netsurf-browser.org/netsurf.git/tree/360997c265c10a47a5421c4c1...
The branch, master has been updated
via 360997c265c10a47a5421c4c1e74ece5a4d46725 (commit)
from f3a0e9f0a19f6b88f13b0ad7a11392f0a9b5269e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=360997c265c10a47a54...
commit 360997c265c10a47a5421c4c1e74ece5a4d46725
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
fs_backing_store: Clear entry memory flags on read
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/content/fs_backing_store.c b/content/fs_backing_store.c
index 71d1c83..272f251 100644
--- a/content/fs_backing_store.c
+++ b/content/fs_backing_store.c
@@ -1194,8 +1194,13 @@ read_entries(struct store_state *state)
nsurl_unref(nsurl);
NSLOG(netsurf, DEBUG, "Successfully read entry for %s", nsurl_access(ent->url));
read_entries++;
+ /* Note the size allocation */
state->total_alloc += ent->elem[ENTRY_ELEM_DATA].size;
state->total_alloc += ent->elem[ENTRY_ELEM_META].size;
+ /* And ensure we don't pretend to have this in memory yet */
+ ent->elem[ENTRY_ELEM_DATA].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
+ ent->elem[ENTRY_ELEM_META].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
+
}
close(fd);
}
-----------------------------------------------------------------------
Summary of changes:
content/fs_backing_store.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/content/fs_backing_store.c b/content/fs_backing_store.c
index 71d1c83..272f251 100644
--- a/content/fs_backing_store.c
+++ b/content/fs_backing_store.c
@@ -1194,8 +1194,13 @@ read_entries(struct store_state *state)
nsurl_unref(nsurl);
NSLOG(netsurf, DEBUG, "Successfully read entry for %s", nsurl_access(ent->url));
read_entries++;
+ /* Note the size allocation */
state->total_alloc += ent->elem[ENTRY_ELEM_DATA].size;
state->total_alloc += ent->elem[ENTRY_ELEM_META].size;
+ /* And ensure we don't pretend to have this in memory yet */
+ ent->elem[ENTRY_ELEM_DATA].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
+ ent->elem[ENTRY_ELEM_META].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
+
}
close(fd);
}
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-489-gf3a0e9f
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/f3a0e9f0a19f6b88f13b0...
...commit http://git.netsurf-browser.org/netsurf.git/commit/f3a0e9f0a19f6b88f13b0ad...
...tree http://git.netsurf-browser.org/netsurf.git/tree/f3a0e9f0a19f6b88f13b0ad7a...
The branch, master has been updated
via f3a0e9f0a19f6b88f13b0ad7a11392f0a9b5269e (commit)
via e6a5c090c9cb877e16fbd8e2034215b8fff42a16 (commit)
via af53312b838b1caca59fb0279daa606089c03b02 (commit)
via 5a5670410b2cf3c90f5ef8534c48feb0c97532bb (commit)
from 494db4cd51a5896778fcda150ed9c29b92ef504a (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=f3a0e9f0a19f6b88f13...
commit f3a0e9f0a19f6b88f13b0ad7a11392f0a9b5269e
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
browser_window: Various little SSL fixes
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 59ab73d..bdf48b3 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -3590,17 +3590,28 @@ navigate_internal_query_ssl(struct browser_window *bw,
struct browser_fetch_parameters *params)
{
bool is_proceed = false, is_back = false;
+ const char *siteurl = NULL;
+ nsurl *siteurl_ns;
assert(params->post_multipart != NULL);
is_proceed = fetch_multipart_data_find(params->post_multipart, "proceed") != NULL;
is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL;
+ siteurl = fetch_multipart_data_find(params->post_multipart, "siteurl");
- if (!(is_proceed || is_back)) {
+ if (!(is_proceed || is_back) || siteurl == NULL) {
/* This is a request, so pass it on */
return navigate_internal_real(bw, params);
}
+ if (nsurl_create(siteurl, &siteurl_ns) != NSERROR_OK) {
+ NSLOG(netsurf, ERROR, "Unable to reset ssl loading parameters");
+ } else {
+ /* In order that we may proceed, replace the loading parameters */
+ nsurl_unref(bw->loading_parameters.url);
+ bw->loading_parameters.url = siteurl_ns;
+ }
+
return browser_window__handle_ssl_query_response(is_proceed, bw);
}
@@ -4693,7 +4704,7 @@ browser_window_page_info_state browser_window_get_page_info_state(
lwc_string_unref(scheme);
/* Did we have to override this SSL setting? */
- if (urldb_get_cert_permissions(bw->current_parameters.url)) {
+ if (urldb_get_cert_permissions(hlcache_handle_get_url(bw->current_content))) {
return PAGE_STATE_SECURE_OVERRIDE;
}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=e6a5c090c9cb877e16f...
commit e6a5c090c9cb877e16fbd8e2034215b8fff42a16
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
curl: Implement SSL chain cache in cURL fetcher
Because cURL can do connection caching behind the scenes, we
need to have a cache for the SSL certificate chains which we
send onward on first header back from cURL.
This uses the new hashmap implementation to mean that we cache
chains on a hostname:port basis.
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c
index 83e92d8..39759cf 100644
--- a/content/fetchers/curl.c
+++ b/content/fetchers/curl.c
@@ -43,6 +43,7 @@
#include <nsutils/time.h>
#include "utils/corestrings.h"
+#include "utils/hashmap.h"
#include "utils/nsoption.h"
#include "utils/log.h"
#include "utils/messages.h"
@@ -121,6 +122,94 @@ static void ns_X509_free(X509 *cert)
#endif /* WITH_OPENSSL */
+/* SSL certificate chain cache */
+
+/* We're only interested in the hostname and port */
+static uint32_t
+curl_fetch_ssl_key_hash(void *key)
+{
+ nsurl *url = key;
+ lwc_string *hostname = nsurl_get_component(url, NSURL_HOST);
+ lwc_string *port = nsurl_get_component(url, NSURL_PORT);
+ uint32_t hash;
+
+ if (port == NULL)
+ port = lwc_string_ref(corestring_lwc_443);
+
+ hash = lwc_string_hash_value(hostname) ^ lwc_string_hash_value(port);
+
+ lwc_string_unref(hostname);
+ lwc_string_unref(port);
+
+ return hash;
+}
+
+/* We only compare the hostname and port */
+static bool
+curl_fetch_ssl_key_eq(void *key1, void *key2)
+{
+ nsurl *url1 = key1;
+ nsurl *url2 = key2;
+ lwc_string *hostname1 = nsurl_get_component(url1, NSURL_HOST);
+ lwc_string *hostname2 = nsurl_get_component(url2, NSURL_HOST);
+ lwc_string *port1 = nsurl_get_component(url1, NSURL_PORT);
+ lwc_string *port2 = nsurl_get_component(url2, NSURL_PORT);
+ bool iseq = false;
+
+ if (port1 == NULL)
+ port1 = lwc_string_ref(corestring_lwc_443);
+ if (port2 == NULL)
+ port2 = lwc_string_ref(corestring_lwc_443);
+
+ if (lwc_string_isequal(hostname1, hostname2, &iseq) != lwc_error_ok)
+ goto out;
+ if (!iseq)
+ goto out;
+
+ iseq = false;
+ if (lwc_string_isequal(port1, port2, &iseq) != lwc_error_ok)
+ goto out;
+
+out:
+ lwc_string_unref(hostname1);
+ lwc_string_unref(hostname2);
+ lwc_string_unref(port1);
+ lwc_string_unref(port2);
+
+ return iseq;
+}
+
+static void *
+curl_fetch_ssl_value_alloc(void *key)
+{
+ struct cert_chain *out;
+
+ if (cert_chain_alloc(0, &out) != NSERROR_OK) {
+ return NULL;
+ }
+
+ return out;
+}
+
+static void
+curl_fetch_ssl_value_destroy(void *value)
+{
+ struct cert_chain *chain = value;
+ if (cert_chain_free(chain) != NSERROR_OK) {
+ NSLOG(netsurf, WARNING, "Problem freeing SSL certificate chain");
+ }
+}
+
+static hashmap_parameters_t curl_fetch_ssl_hashmap_parameters = {
+ .key_clone = (hashmap_key_clone_t)nsurl_ref,
+ .key_destroy = (hashmap_key_destroy_t)nsurl_unref,
+ .key_eq = curl_fetch_ssl_key_eq,
+ .key_hash = curl_fetch_ssl_key_hash,
+ .value_alloc = curl_fetch_ssl_value_alloc,
+ .value_destroy = curl_fetch_ssl_value_destroy,
+};
+
+static hashmap_t *curl_fetch_ssl_hashmap = NULL;
/** SSL certificate info */
struct cert_info {
@@ -132,6 +221,7 @@ struct cert_info {
struct curl_fetch_info {
struct fetch *fetch_handle; /**< The fetch handle we're parented by. */
CURL * curl_handle; /**< cURL handle if being fetched, or 0. */
+ bool sent_ssl_chain; /**< Have we tried to send the SSL chain */
bool had_headers; /**< Headers have been processed. */
bool abort; /**< Abort requested. */
bool stopped; /**< Download stopped on purpose. */
@@ -224,6 +314,10 @@ static void fetch_curl_finalise(lwc_string *scheme)
"curl_multi_cleanup failed: ignoring");
curl_global_cleanup();
+
+ NSLOG(netsurf, DEBUG, "Cleaning up SSL cert chain hashmap");
+ hashmap_destroy(curl_fetch_ssl_hashmap);
+ curl_fetch_ssl_hashmap = NULL;
}
/* Free anything remaining in the cached curl handle ring */
@@ -373,6 +467,7 @@ fetch_curl_setup(struct fetch *parent_fetch,
/* construct a new fetch structure */
fetch->curl_handle = NULL;
+ fetch->sent_ssl_chain = false;
fetch->had_headers = false;
fetch->abort = false;
fetch->stopped = false;
@@ -466,16 +561,31 @@ failed:
#ifdef WITH_OPENSSL
/**
+ * Retrieve the ssl cert chain for the fetch, creating a blank one if needed
+ */
+static struct cert_chain *
+fetch_curl_get_cached_chain(struct curl_fetch_info *f)
+{
+ struct cert_chain *chain;
+
+ chain = hashmap_lookup(curl_fetch_ssl_hashmap, f->url);
+ if (chain == NULL) {
+ chain = hashmap_insert(curl_fetch_ssl_hashmap, f->url);
+ }
+
+ return chain;
+}
+
+/**
* Report the certificate information in the fetch to the users
*/
static void
-fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
+fetch_curl_store_certs_in_cache(struct curl_fetch_info *f)
{
size_t depth;
BIO *mem;
BUF_MEM *buf[MAX_CERT_DEPTH];
- struct cert_chain chain;
- fetch_msg msg;
+ struct cert_chain chain, *cached_chain;
struct cert_info *certs;
memset(&chain, 0, sizeof(chain));
@@ -558,10 +668,12 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
chain.certs[depth].der_length = buf[depth]->length;
}
- msg.type = FETCH_CERTS;
- msg.data.chain = &chain;
-
- fetch_send_callback(&msg, f->fetch_handle);
+ /* Now dup that chain into the cache */
+ cached_chain = fetch_curl_get_cached_chain(f);
+ if (cert_chain_dup_into(&chain, cached_chain) != NSERROR_OK) {
+ /* Something went wrong storing the chain, give up */
+ hashmap_remove(curl_fetch_ssl_hashmap, f->url);
+ }
/* release the openssl memory buffer */
for (depth = 0; depth < chain.depth; depth++) {
@@ -571,6 +683,26 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
}
}
+/**
+ * Report the certificate information in the fetch to the users
+ */
+static void
+fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
+{
+ fetch_msg msg;
+ struct cert_chain *chain;
+
+ chain = hashmap_lookup(curl_fetch_ssl_hashmap, f->url);
+
+ if (chain != NULL) {
+ msg.type = FETCH_CERTS;
+ msg.data.chain = chain;
+
+ fetch_send_callback(&msg, f->fetch_handle);
+ }
+
+ f->sent_ssl_chain = true;
+}
/**
* OpenSSL Certificate verification callback
@@ -673,7 +805,7 @@ static int fetch_curl_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *parm)
ok = X509_verify_cert(x509_ctx);
}
- fetch_curl_report_certs_upstream(f);
+ fetch_curl_store_certs_in_cache(f);
return ok;
}
@@ -1430,6 +1562,10 @@ fetch_curl_header(char *data, size_t size, size_t nmemb, void *_f)
return 0;
}
+ if (f->sent_ssl_chain == false) {
+ fetch_curl_report_certs_upstream(f);
+ }
+
msg.type = FETCH_HEADER;
msg.data.header_or_data.buf = (const uint8_t *) data;
msg.data.header_or_data.len = size;
@@ -1653,6 +1789,12 @@ nserror fetch_curl_register(void)
data = curl_version_info(CURLVERSION_NOW);
+ curl_fetch_ssl_hashmap = hashmap_create(&curl_fetch_ssl_hashmap_parameters);
+ if (curl_fetch_ssl_hashmap == NULL) {
+ NSLOG(netsurf, CRITICAL, "Unable to initialise SSL certificate hashmap");
+ return NSERROR_NOMEM;
+ }
+
for (i = 0; data->protocols[i]; i++) {
if (strcmp(data->protocols[i], "http") == 0) {
scheme = lwc_string_ref(corestring_lwc_http);
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=af53312b838b1caca59...
commit af53312b838b1caca59fb0279daa606089c03b02
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
corestrings: add '443'
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/utils/corestringlist.h b/utils/corestringlist.h
index c9d0749..02689fc 100644
--- a/utils/corestringlist.h
+++ b/utils/corestringlist.h
@@ -139,6 +139,7 @@ CORESTRING_LWC_STRING(_blank);
CORESTRING_LWC_STRING(_parent);
CORESTRING_LWC_STRING(_self);
CORESTRING_LWC_STRING(_top);
+CORESTRING_LWC_STRING(443);
/* unusual lwc strings */
CORESTRING_LWC_VALUE(shortcut_icon, "shortcut icon");
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=5a5670410b2cf3c90f5...
commit 5a5670410b2cf3c90f5ef8534c48feb0c97532bb
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
ssl_certs: Add dup_into
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/include/netsurf/ssl_certs.h b/include/netsurf/ssl_certs.h
index 1aaf485..b5e79ab 100644
--- a/include/netsurf/ssl_certs.h
+++ b/include/netsurf/ssl_certs.h
@@ -87,6 +87,18 @@ struct cert_chain {
nserror cert_chain_alloc(size_t depth, struct cert_chain **chain_out);
/**
+ * duplicate a certificate chain into an existing chain
+ *
+ * \param src The certificate chain to copy from
+ * \param dst The chain to overwrite with a copy of src
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ *
+ * NOTE: if this returns NSERROR_NOMEM then the destination chain will have
+ * some amount of content and should be cleaned up with cert_chain_free.
+ */
+nserror cert_chain_dup_into(const struct cert_chain *src, struct cert_chain *dst);
+
+/**
* duplicate a certificate chain
*
* \param src The certificate chain to copy from
diff --git a/utils/ssl_certs.c b/utils/ssl_certs.c
index 7154561..09500a4 100644
--- a/utils/ssl_certs.c
+++ b/utils/ssl_certs.c
@@ -54,6 +54,43 @@ cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
/*
+ * duplicate certificate chain into existing chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_dup_into(const struct cert_chain *src, struct cert_chain *dst)
+{
+ size_t depth;
+ for (depth = 0; depth < dst->depth; depth++) {
+ if (dst->certs[depth].der != NULL) {
+ free(dst->certs[depth].der);
+ dst->certs[depth].der = NULL;
+ }
+ }
+
+ dst->depth = src->depth;
+
+ for (depth = 0; depth < src->depth; depth++) {
+ dst->certs[depth].err = src->certs[depth].err;
+ dst->certs[depth].der_length = src->certs[depth].der_length;
+ if (src->certs[depth].der != NULL) {
+ dst->certs[depth].der = malloc(src->certs[depth].der_length);
+ if (dst->certs[depth].der == NULL) {
+ return NSERROR_NOMEM;
+ }
+ memcpy(dst->certs[depth].der,
+ src->certs[depth].der,
+ src->certs[depth].der_length);
+ }
+
+ }
+
+ return NSERROR_OK;
+}
+
+
+/*
* duplicate certificate chain
*
* exported interface documented in netsurf/ssl_certs.h
-----------------------------------------------------------------------
Summary of changes:
content/fetchers/curl.c | 158 ++++++++++++++++++++++++++++++++++++++++---
desktop/browser_window.c | 15 +++-
include/netsurf/ssl_certs.h | 12 ++++
utils/corestringlist.h | 1 +
utils/ssl_certs.c | 37 ++++++++++
5 files changed, 213 insertions(+), 10 deletions(-)
diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c
index 83e92d8..39759cf 100644
--- a/content/fetchers/curl.c
+++ b/content/fetchers/curl.c
@@ -43,6 +43,7 @@
#include <nsutils/time.h>
#include "utils/corestrings.h"
+#include "utils/hashmap.h"
#include "utils/nsoption.h"
#include "utils/log.h"
#include "utils/messages.h"
@@ -121,6 +122,94 @@ static void ns_X509_free(X509 *cert)
#endif /* WITH_OPENSSL */
+/* SSL certificate chain cache */
+
+/* We're only interested in the hostname and port */
+static uint32_t
+curl_fetch_ssl_key_hash(void *key)
+{
+ nsurl *url = key;
+ lwc_string *hostname = nsurl_get_component(url, NSURL_HOST);
+ lwc_string *port = nsurl_get_component(url, NSURL_PORT);
+ uint32_t hash;
+
+ if (port == NULL)
+ port = lwc_string_ref(corestring_lwc_443);
+
+ hash = lwc_string_hash_value(hostname) ^ lwc_string_hash_value(port);
+
+ lwc_string_unref(hostname);
+ lwc_string_unref(port);
+
+ return hash;
+}
+
+/* We only compare the hostname and port */
+static bool
+curl_fetch_ssl_key_eq(void *key1, void *key2)
+{
+ nsurl *url1 = key1;
+ nsurl *url2 = key2;
+ lwc_string *hostname1 = nsurl_get_component(url1, NSURL_HOST);
+ lwc_string *hostname2 = nsurl_get_component(url2, NSURL_HOST);
+ lwc_string *port1 = nsurl_get_component(url1, NSURL_PORT);
+ lwc_string *port2 = nsurl_get_component(url2, NSURL_PORT);
+ bool iseq = false;
+
+ if (port1 == NULL)
+ port1 = lwc_string_ref(corestring_lwc_443);
+ if (port2 == NULL)
+ port2 = lwc_string_ref(corestring_lwc_443);
+
+ if (lwc_string_isequal(hostname1, hostname2, &iseq) != lwc_error_ok)
+ goto out;
+ if (!iseq)
+ goto out;
+
+ iseq = false;
+ if (lwc_string_isequal(port1, port2, &iseq) != lwc_error_ok)
+ goto out;
+
+out:
+ lwc_string_unref(hostname1);
+ lwc_string_unref(hostname2);
+ lwc_string_unref(port1);
+ lwc_string_unref(port2);
+
+ return iseq;
+}
+
+static void *
+curl_fetch_ssl_value_alloc(void *key)
+{
+ struct cert_chain *out;
+
+ if (cert_chain_alloc(0, &out) != NSERROR_OK) {
+ return NULL;
+ }
+
+ return out;
+}
+
+static void
+curl_fetch_ssl_value_destroy(void *value)
+{
+ struct cert_chain *chain = value;
+ if (cert_chain_free(chain) != NSERROR_OK) {
+ NSLOG(netsurf, WARNING, "Problem freeing SSL certificate chain");
+ }
+}
+
+static hashmap_parameters_t curl_fetch_ssl_hashmap_parameters = {
+ .key_clone = (hashmap_key_clone_t)nsurl_ref,
+ .key_destroy = (hashmap_key_destroy_t)nsurl_unref,
+ .key_eq = curl_fetch_ssl_key_eq,
+ .key_hash = curl_fetch_ssl_key_hash,
+ .value_alloc = curl_fetch_ssl_value_alloc,
+ .value_destroy = curl_fetch_ssl_value_destroy,
+};
+
+static hashmap_t *curl_fetch_ssl_hashmap = NULL;
/** SSL certificate info */
struct cert_info {
@@ -132,6 +221,7 @@ struct cert_info {
struct curl_fetch_info {
struct fetch *fetch_handle; /**< The fetch handle we're parented by. */
CURL * curl_handle; /**< cURL handle if being fetched, or 0. */
+ bool sent_ssl_chain; /**< Have we tried to send the SSL chain */
bool had_headers; /**< Headers have been processed. */
bool abort; /**< Abort requested. */
bool stopped; /**< Download stopped on purpose. */
@@ -224,6 +314,10 @@ static void fetch_curl_finalise(lwc_string *scheme)
"curl_multi_cleanup failed: ignoring");
curl_global_cleanup();
+
+ NSLOG(netsurf, DEBUG, "Cleaning up SSL cert chain hashmap");
+ hashmap_destroy(curl_fetch_ssl_hashmap);
+ curl_fetch_ssl_hashmap = NULL;
}
/* Free anything remaining in the cached curl handle ring */
@@ -373,6 +467,7 @@ fetch_curl_setup(struct fetch *parent_fetch,
/* construct a new fetch structure */
fetch->curl_handle = NULL;
+ fetch->sent_ssl_chain = false;
fetch->had_headers = false;
fetch->abort = false;
fetch->stopped = false;
@@ -466,16 +561,31 @@ failed:
#ifdef WITH_OPENSSL
/**
+ * Retrieve the ssl cert chain for the fetch, creating a blank one if needed
+ */
+static struct cert_chain *
+fetch_curl_get_cached_chain(struct curl_fetch_info *f)
+{
+ struct cert_chain *chain;
+
+ chain = hashmap_lookup(curl_fetch_ssl_hashmap, f->url);
+ if (chain == NULL) {
+ chain = hashmap_insert(curl_fetch_ssl_hashmap, f->url);
+ }
+
+ return chain;
+}
+
+/**
* Report the certificate information in the fetch to the users
*/
static void
-fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
+fetch_curl_store_certs_in_cache(struct curl_fetch_info *f)
{
size_t depth;
BIO *mem;
BUF_MEM *buf[MAX_CERT_DEPTH];
- struct cert_chain chain;
- fetch_msg msg;
+ struct cert_chain chain, *cached_chain;
struct cert_info *certs;
memset(&chain, 0, sizeof(chain));
@@ -558,10 +668,12 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
chain.certs[depth].der_length = buf[depth]->length;
}
- msg.type = FETCH_CERTS;
- msg.data.chain = &chain;
-
- fetch_send_callback(&msg, f->fetch_handle);
+ /* Now dup that chain into the cache */
+ cached_chain = fetch_curl_get_cached_chain(f);
+ if (cert_chain_dup_into(&chain, cached_chain) != NSERROR_OK) {
+ /* Something went wrong storing the chain, give up */
+ hashmap_remove(curl_fetch_ssl_hashmap, f->url);
+ }
/* release the openssl memory buffer */
for (depth = 0; depth < chain.depth; depth++) {
@@ -571,6 +683,26 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
}
}
+/**
+ * Report the certificate information in the fetch to the users
+ */
+static void
+fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
+{
+ fetch_msg msg;
+ struct cert_chain *chain;
+
+ chain = hashmap_lookup(curl_fetch_ssl_hashmap, f->url);
+
+ if (chain != NULL) {
+ msg.type = FETCH_CERTS;
+ msg.data.chain = chain;
+
+ fetch_send_callback(&msg, f->fetch_handle);
+ }
+
+ f->sent_ssl_chain = true;
+}
/**
* OpenSSL Certificate verification callback
@@ -673,7 +805,7 @@ static int fetch_curl_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *parm)
ok = X509_verify_cert(x509_ctx);
}
- fetch_curl_report_certs_upstream(f);
+ fetch_curl_store_certs_in_cache(f);
return ok;
}
@@ -1430,6 +1562,10 @@ fetch_curl_header(char *data, size_t size, size_t nmemb, void *_f)
return 0;
}
+ if (f->sent_ssl_chain == false) {
+ fetch_curl_report_certs_upstream(f);
+ }
+
msg.type = FETCH_HEADER;
msg.data.header_or_data.buf = (const uint8_t *) data;
msg.data.header_or_data.len = size;
@@ -1653,6 +1789,12 @@ nserror fetch_curl_register(void)
data = curl_version_info(CURLVERSION_NOW);
+ curl_fetch_ssl_hashmap = hashmap_create(&curl_fetch_ssl_hashmap_parameters);
+ if (curl_fetch_ssl_hashmap == NULL) {
+ NSLOG(netsurf, CRITICAL, "Unable to initialise SSL certificate hashmap");
+ return NSERROR_NOMEM;
+ }
+
for (i = 0; data->protocols[i]; i++) {
if (strcmp(data->protocols[i], "http") == 0) {
scheme = lwc_string_ref(corestring_lwc_http);
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 59ab73d..bdf48b3 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -3590,17 +3590,28 @@ navigate_internal_query_ssl(struct browser_window *bw,
struct browser_fetch_parameters *params)
{
bool is_proceed = false, is_back = false;
+ const char *siteurl = NULL;
+ nsurl *siteurl_ns;
assert(params->post_multipart != NULL);
is_proceed = fetch_multipart_data_find(params->post_multipart, "proceed") != NULL;
is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL;
+ siteurl = fetch_multipart_data_find(params->post_multipart, "siteurl");
- if (!(is_proceed || is_back)) {
+ if (!(is_proceed || is_back) || siteurl == NULL) {
/* This is a request, so pass it on */
return navigate_internal_real(bw, params);
}
+ if (nsurl_create(siteurl, &siteurl_ns) != NSERROR_OK) {
+ NSLOG(netsurf, ERROR, "Unable to reset ssl loading parameters");
+ } else {
+ /* In order that we may proceed, replace the loading parameters */
+ nsurl_unref(bw->loading_parameters.url);
+ bw->loading_parameters.url = siteurl_ns;
+ }
+
return browser_window__handle_ssl_query_response(is_proceed, bw);
}
@@ -4693,7 +4704,7 @@ browser_window_page_info_state browser_window_get_page_info_state(
lwc_string_unref(scheme);
/* Did we have to override this SSL setting? */
- if (urldb_get_cert_permissions(bw->current_parameters.url)) {
+ if (urldb_get_cert_permissions(hlcache_handle_get_url(bw->current_content))) {
return PAGE_STATE_SECURE_OVERRIDE;
}
diff --git a/include/netsurf/ssl_certs.h b/include/netsurf/ssl_certs.h
index 1aaf485..b5e79ab 100644
--- a/include/netsurf/ssl_certs.h
+++ b/include/netsurf/ssl_certs.h
@@ -87,6 +87,18 @@ struct cert_chain {
nserror cert_chain_alloc(size_t depth, struct cert_chain **chain_out);
/**
+ * duplicate a certificate chain into an existing chain
+ *
+ * \param src The certificate chain to copy from
+ * \param dst The chain to overwrite with a copy of src
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ *
+ * NOTE: if this returns NSERROR_NOMEM then the destination chain will have
+ * some amount of content and should be cleaned up with cert_chain_free.
+ */
+nserror cert_chain_dup_into(const struct cert_chain *src, struct cert_chain *dst);
+
+/**
* duplicate a certificate chain
*
* \param src The certificate chain to copy from
diff --git a/utils/corestringlist.h b/utils/corestringlist.h
index c9d0749..02689fc 100644
--- a/utils/corestringlist.h
+++ b/utils/corestringlist.h
@@ -139,6 +139,7 @@ CORESTRING_LWC_STRING(_blank);
CORESTRING_LWC_STRING(_parent);
CORESTRING_LWC_STRING(_self);
CORESTRING_LWC_STRING(_top);
+CORESTRING_LWC_STRING(443);
/* unusual lwc strings */
CORESTRING_LWC_VALUE(shortcut_icon, "shortcut icon");
diff --git a/utils/ssl_certs.c b/utils/ssl_certs.c
index 7154561..09500a4 100644
--- a/utils/ssl_certs.c
+++ b/utils/ssl_certs.c
@@ -54,6 +54,43 @@ cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
/*
+ * duplicate certificate chain into existing chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_dup_into(const struct cert_chain *src, struct cert_chain *dst)
+{
+ size_t depth;
+ for (depth = 0; depth < dst->depth; depth++) {
+ if (dst->certs[depth].der != NULL) {
+ free(dst->certs[depth].der);
+ dst->certs[depth].der = NULL;
+ }
+ }
+
+ dst->depth = src->depth;
+
+ for (depth = 0; depth < src->depth; depth++) {
+ dst->certs[depth].err = src->certs[depth].err;
+ dst->certs[depth].der_length = src->certs[depth].der_length;
+ if (src->certs[depth].der != NULL) {
+ dst->certs[depth].der = malloc(src->certs[depth].der_length);
+ if (dst->certs[depth].der == NULL) {
+ return NSERROR_NOMEM;
+ }
+ memcpy(dst->certs[depth].der,
+ src->certs[depth].der,
+ src->certs[depth].der_length);
+ }
+
+ }
+
+ return NSERROR_OK;
+}
+
+
+/*
* duplicate certificate chain
*
* exported interface documented in netsurf/ssl_certs.h
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-485-g494db4c
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/494db4cd51a5896778fcd...
...commit http://git.netsurf-browser.org/netsurf.git/commit/494db4cd51a5896778fcda1...
...tree http://git.netsurf-browser.org/netsurf.git/tree/494db4cd51a5896778fcda150...
The branch, master has been updated
via 494db4cd51a5896778fcda150ed9c29b92ef504a (commit)
from 859972df714b0c91bf7fd181796d34451f07e9f2 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=494db4cd51a5896778f...
commit 494db4cd51a5896778fcda150ed9c29b92ef504a
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Page info: Fix typo.
diff --git a/desktop/page-info.c b/desktop/page-info.c
index fba0d63..89c63b7 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -412,7 +412,7 @@ static nserror page_info__set_text(
{
int printed;
static const char *header[PAGE_STATE__COUNT] = {
- [PAGE_STATE_UNKNOWN] = "Provenience unknown",
+ [PAGE_STATE_UNKNOWN] = "Provenance unknown",
[PAGE_STATE_INTERNAL] = "NetSurf data",
[PAGE_STATE_LOCAL] = "Local data",
[PAGE_STATE_INSECURE] = "Connection not secure",
-----------------------------------------------------------------------
Summary of changes:
desktop/page-info.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/desktop/page-info.c b/desktop/page-info.c
index fba0d63..89c63b7 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -412,7 +412,7 @@ static nserror page_info__set_text(
{
int printed;
static const char *header[PAGE_STATE__COUNT] = {
- [PAGE_STATE_UNKNOWN] = "Provenience unknown",
+ [PAGE_STATE_UNKNOWN] = "Provenance unknown",
[PAGE_STATE_INTERNAL] = "NetSurf data",
[PAGE_STATE_LOCAL] = "Local data",
[PAGE_STATE_INSECURE] = "Connection not secure",
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-484-g859972d
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/859972df714b0c91bf7fd...
...commit http://git.netsurf-browser.org/netsurf.git/commit/859972df714b0c91bf7fd18...
...tree http://git.netsurf-browser.org/netsurf.git/tree/859972df714b0c91bf7fd1817...
The branch, master has been updated
via 859972df714b0c91bf7fd181796d34451f07e9f2 (commit)
via ac75a9161e23b7073edcf0ec07d584b87d2e0e08 (commit)
via 088917641f0865e11be5e81bf90de3dbc8cba19e (commit)
via 3e02961ec8f21fd656c8b306c5075c0c799df97f (commit)
via 54b1960d18042cf6dfd86cfe01d58455357586d2 (commit)
via fd80341513813684ba3155b87e4e6cc8c87631f1 (commit)
via 61187d31ab7ba292510e1e56886325a1c6a1c822 (commit)
from be659af7e563527270a8725ae42ecf7c25aecc7e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=859972df714b0c91bf7...
commit 859972df714b0c91bf7fd181796d34451f07e9f2
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
llcache: Rework fs_backing_store to use hashmap
As a result, we no longer waste a bunch of RAM on the entries
tables. This ought to be no slower, and more memory efficient.
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/content/fs_backing_store.c b/content/fs_backing_store.c
index 19eb1ca..71d1c83 100644
--- a/content/fs_backing_store.c
+++ b/content/fs_backing_store.c
@@ -49,6 +49,7 @@
#include "utils/nsurl.h"
#include "utils/log.h"
#include "utils/messages.h"
+#include "utils/hashmap.h"
#include "desktop/gui_internal.h"
#include "netsurf/misc.h"
@@ -61,20 +62,11 @@
#define DEFAULT_ENTRY_SIZE 16
/** Backing store file format version */
-#define CONTROL_VERSION 130
+#define CONTROL_VERSION 200
/** Number of milliseconds after a update before control data maintenance is performed */
#define CONTROL_MAINT_TIME 10000
-/** Get address from ident */
-#define BS_ADDRESS(ident, state) ((ident) & ((1 << state->ident_bits) - 1))
-
-/** Lookup store entry index from ident */
-#define BS_ENTRY_INDEX(ident, state) state->addrmap[(ident) & ((1 << state->ident_bits) - 1)]
-
-/** Get store entry from ident. */
-#define BS_ENTRY(ident, state) state->entries[state->addrmap[(ident) & ((1 << state->ident_bits) - 1)]]
-
/** Filename of serialised entries */
#define ENTRIES_FNAME "entries"
@@ -184,8 +176,8 @@ struct store_entry_element {
* @note Order is important to avoid excessive structure packing overhead.
*/
struct store_entry {
+ nsurl *url; /**< The URL for this entry */
int64_t last_used; /**< UNIX time the entry was last used */
- entry_ident_t ident; /**< entry identifier */
uint16_t use_count; /**< number of times this entry has been accessed */
uint8_t flags; /**< entry flags */
/** Entry element (data or meta) specific information */
@@ -219,29 +211,16 @@ struct store_state {
size_t limit; /**< The backing store upper bound target size */
size_t hysteresis; /**< The hysteresis around the target size */
- unsigned int ident_bits; /**< log2 number of bits to use for address. */
-
-
- /* cache entry management */
- struct store_entry *entries; /**< store entries. */
- unsigned int entry_bits; /**< log2 number of bits in entry index. */
- unsigned int last_entry; /**< index of last usable entry. */
+ /**
+ * The cache object hash
+ */
+ hashmap_t *entries;
/** flag indicating if the entries have been made persistent
* since they were last changed.
*/
bool entries_dirty;
- /**
- * URL identifier to entry index mapping.
- *
- * This is an open coded index on the entries URL field and
- * provides a computationally inexpensive way to go from the
- * URL to an entry.
- */
- entry_index_t *addrmap;
-
-
/** small block indexes */
struct block_file blocks[ENTRY_ELEM_COUNT][BLOCK_FILE_COUNT];
@@ -273,60 +252,44 @@ struct store_state {
*/
struct store_state *storestate;
-
-/**
- * Remove a backing store entry from the entry table.
- *
- * This finds the store entry associated with the given key and
- * removes it from the table. The removed entry is returned but is
- * only valid until the next set_store_entry call.
+/* Entries hashmap parameters
*
- * @param[in] state The store state to use.
- * @param[in, out] bse Pointer to the entry to be removed.
- * @return NSERROR_OK and \a bse updated on success or NSERROR_NOT_FOUND
- * if no entry corresponds to the URL.
+ * Our hashmap has nsurl keys and store_entry values
*/
-static nserror
-remove_store_entry(struct store_state *state, struct store_entry **bse)
-{
- entry_index_t sei; /* store entry index */
-
- /* sei is index to entry to be removed, we swap it to the end
- * of the table so there are no gaps and the returned entry is
- * held in storage with reasonable lifetime.
- */
-
- sei = BS_ENTRY_INDEX((*bse)->ident, state);
-
- /* remove entry from map */
- BS_ENTRY_INDEX((*bse)->ident, state) = 0;
- /* global allocation accounting */
- state->total_alloc -= state->entries[sei].elem[ENTRY_ELEM_DATA].size;
- state->total_alloc -= state->entries[sei].elem[ENTRY_ELEM_META].size;
-
- state->last_entry--;
-
- if (sei == state->last_entry) {
- /* the removed entry was the last one, how convenient */
- *bse = &state->entries[sei];
- } else {
- /* need to swap entries */
- struct store_entry tent;
-
- tent = state->entries[sei];
- state->entries[sei] = state->entries[state->last_entry];
- state->entries[state->last_entry] = tent;
-
- /* update map for moved entry */
- BS_ENTRY_INDEX(state->entries[sei].ident, state) = sei;
+static bool
+entries_hashmap_key_eq(void *key1, void *key2)
+{
+ return nsurl_compare((nsurl *)key1, (nsurl *)key2, NSURL_COMPLETE);
+}
- *bse = &state->entries[state->last_entry];
+static void *
+entries_hashmap_value_alloc(void *key)
+{
+ struct store_entry *ent = calloc(1, sizeof(struct store_entry));
+ if (ent != NULL) {
+ ent->url = nsurl_ref(key);
}
+ return ent;
+}
- return NSERROR_OK;
+static void
+entries_hashmap_value_destroy(void *value)
+{
+ struct store_entry *ent = value;
+ /** \todo Do we need to do any disk cleanup here? if so, meep! */
+ nsurl_unref(ent->url);
+ free(ent);
}
+static hashmap_parameters_t entries_hashmap_parameters = {
+ .key_clone = (hashmap_key_clone_t)nsurl_ref,
+ .key_destroy = (hashmap_key_destroy_t)nsurl_unref,
+ .key_hash = (hashmap_key_hash_t)nsurl_hash,
+ .key_eq = entries_hashmap_key_eq,
+ .value_alloc = entries_hashmap_value_alloc,
+ .value_destroy = entries_hashmap_value_destroy,
+};
/**
* Generate a filename for an object.
@@ -484,7 +447,7 @@ invalidate_element(struct store_state *state,
char *fname;
/* unlink the file from disc */
- fname = store_fname(state, bse->ident, elem_idx);
+ fname = store_fname(state, nsurl_hash(bse->url), elem_idx);
if (fname == NULL) {
return NSERROR_NOMEM;
}
@@ -492,6 +455,8 @@ invalidate_element(struct store_state *state,
free(fname);
}
+ state->total_alloc -= bse->elem[elem_idx].size;
+
return NSERROR_OK;
}
@@ -519,29 +484,27 @@ invalidate_entry(struct store_state *state, struct store_entry *bse)
* This entry cannot be immediately removed as it has
* associated allocation so wait for allocation release.
*/
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEBUG,
"invalidating entry with referenced allocation");
return NSERROR_OK;
}
- NSLOG(netsurf, INFO, "Removing entry for %p", bse);
-
- /* remove the entry from the index */
- ret = remove_store_entry(state, &bse);
- if (ret != NSERROR_OK) {
- return ret;
- }
+ NSLOG(netsurf, VERBOSE, "Removing entry for %s", nsurl_access(bse->url));
ret = invalidate_element(state, bse, ENTRY_ELEM_META);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "Error invalidating metadata element");
+ NSLOG(netsurf, ERROR, "Error invalidating metadata element");
}
ret = invalidate_element(state, bse, ENTRY_ELEM_DATA);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "Error invalidating data element");
+ NSLOG(netsurf, ERROR, "Error invalidating data element");
}
+ /* As our final act we remove bse from the cache */
+ hashmap_remove(state->entries, bse->url);
+ /* From now, bse is invalid memory */
+
return NSERROR_OK;
}
@@ -551,8 +514,8 @@ invalidate_entry(struct store_state *state, struct store_entry *bse)
*/
static int compar(const void *va, const void *vb)
{
- const struct store_entry *a = &BS_ENTRY(*(entry_ident_t *)va, storestate);
- const struct store_entry *b = &BS_ENTRY(*(entry_ident_t *)vb, storestate);
+ const struct store_entry *a = *(const struct store_entry **)va;
+ const struct store_entry *b = *(const struct store_entry **)vb;
/* consider the allocation flags - if an entry has an
* allocation it is considered more valuable as it cannot be
@@ -591,6 +554,22 @@ static int compar(const void *va, const void *vb)
return 0;
}
+typedef struct {
+ struct store_entry **elist;
+ size_t ent_count;
+} eviction_state_t;
+
+/**
+ * Iterator for gathering entries to compute eviction order
+ */
+static bool
+entry_eviction_iterator_cb(void *key, void *value, void *ctx)
+{
+ eviction_state_t *estate = ctx;
+ struct store_entry *ent = value;
+ estate->elist[estate->ent_count++] = ent;
+ return false;
+}
/**
* Evict entries from backing store as per configuration.
@@ -608,15 +587,14 @@ static int compar(const void *va, const void *vb)
*/
static nserror store_evict(struct store_state *state)
{
- entry_ident_t *elist; /* sorted list of entry identifiers */
- unsigned int ent;
- unsigned int ent_count;
- size_t removed; /* size of removed entries */
+ size_t ent = 0;
+ size_t removed = 0; /* size of removed entries */
nserror ret = NSERROR_OK;
+ size_t old_count;
+ eviction_state_t estate;
/* check if the cache has exceeded configured limit */
- if ((state->total_alloc < state->limit) &&
- (state->last_entry < (1U << state->entry_bits))) {
+ if (state->total_alloc < state->limit) {
/* cache within limits */
return NSERROR_OK;
}
@@ -627,24 +605,31 @@ static nserror store_evict(struct store_state *state)
state->hysteresis);
/* allocate storage for the list */
- elist = malloc(sizeof(entry_ident_t) * state->last_entry);
- if (elist == NULL) {
+ old_count = hashmap_count(state->entries);
+ estate.ent_count = 0;
+ estate.elist = malloc(sizeof(struct state_entry*) * old_count);
+ if (estate.elist == NULL) {
return NSERROR_NOMEM;
}
- /* sort the list avoiding entry 0 which is the empty sentinel */
- for (ent = 1; ent < state->last_entry; ent++) {
- elist[ent - 1] = state->entries[ent].ident;
+ if (hashmap_iterate(state->entries, entry_eviction_iterator_cb, &estate)) {
+ NSLOG(netsurf, WARNING, "Unexpected termination of eviction iterator");
+ free(estate.elist);
+ return NSERROR_UNKNOWN;
}
- ent_count = ent - 1; /* important to keep this as the entry count will change when entries are removed */
- qsort(elist, ent_count, sizeof(entry_ident_t), compar);
+
+ if (old_count != estate.ent_count) {
+ NSLOG(netsurf, WARNING, "Incorrect entry count after eviction iterator");
+ free(estate.elist);
+ return NSERROR_UNKNOWN;
+ }
+
+ qsort(estate.elist, estate.ent_count, sizeof(struct state_entry*), compar);
/* evict entries in listed order */
removed = 0;
- for (ent = 0; ent < ent_count; ent++) {
- struct store_entry *bse;
-
- bse = &BS_ENTRY(elist[ent], state);
+ for (ent = 0; ent < estate.ent_count; ent++) {
+ struct store_entry *bse = estate.elist[ent];
removed += bse->elem[ENTRY_ELEM_DATA].size;
removed += bse->elem[ENTRY_ELEM_META].size;
@@ -659,14 +644,55 @@ static nserror store_evict(struct store_state *state)
}
}
- free(elist);
+ free(estate.elist);
- NSLOG(netsurf, INFO, "removed %"PRIsizet" in %d entries", removed,
- ent);
+ NSLOG(netsurf, INFO,
+ "removed %"PRIsizet" in %"PRIsizet" entries, %"PRIsizet" remaining in %"PRIsizet" entries",
+ removed, ent, state->total_alloc, old_count - ent);
return ret;
}
+/**
+ * Write a single store entry to disk
+ *
+ * To serialise a single store entry for now we write out a 32bit int
+ * which is the length of the url, then that many bytes of the url.
+ * Then we write out the full store entry struct as-is, which includes
+ * a useless nsurl pointer.
+ */
+static nserror
+write_entry(struct store_entry *ent, int fd)
+{
+ uint32_t len = strlen(nsurl_access(ent->url));
+ if (write(fd, &len, sizeof(len)) != sizeof(len))
+ return NSERROR_SAVE_FAILED;
+ if (write(fd, nsurl_access(ent->url), len) != len)
+ return NSERROR_SAVE_FAILED;
+ if (write(fd, ent, sizeof(*ent)) != sizeof(*ent))
+ return NSERROR_SAVE_FAILED;
+
+ return NSERROR_OK;
+}
+
+typedef struct {
+ int fd;
+ size_t written;
+} write_entry_iteration_state;
+
+/**
+ * Callback for iterating the entries hashmap
+ */
+static bool
+write_entry_iterator(void *key, void *value, void *ctx)
+{
+ /* We ignore the key */
+ struct store_entry *ent = value;
+ write_entry_iteration_state *state = ctx;
+ state->written++;
+ /* We stop early if we fail to write this entry */
+ return write_entry(ent, state->fd) != NSERROR_OK;
+}
/**
* Write filesystem entries to file.
@@ -678,13 +704,13 @@ static nserror store_evict(struct store_state *state)
*/
static nserror write_entries(struct store_state *state)
{
- int fd;
char *tname = NULL; /* temporary file name for atomic replace */
char *fname = NULL; /* target filename */
- size_t entries_size;
- size_t written;
+ write_entry_iteration_state weistate;
nserror ret;
+ memset(&weistate, 0, sizeof(weistate));
+
if (state->entries_dirty == false) {
/* entries have not been updated since last write */
return NSERROR_OK;
@@ -695,25 +721,22 @@ static nserror write_entries(struct store_state *state)
return ret;
}
- fd = open(tname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
- if (fd == -1) {
+ weistate.fd = open(tname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (weistate.fd == -1) {
free(tname);
return NSERROR_SAVE_FAILED;
}
- entries_size = state->last_entry * sizeof(struct store_entry);
-
- written = (size_t)write(fd, state->entries, entries_size);
-
- close(fd);
-
- /* check all data was written */
- if (written != entries_size) {
+ if (hashmap_iterate(state->entries, write_entry_iterator, &weistate)) {
+ /* The iteration ended early, so we failed */
+ close(weistate.fd);
unlink(tname);
free(tname);
return NSERROR_SAVE_FAILED;
}
+ close(weistate.fd);
+
ret = netsurf_mkpath(&fname, NULL, 2, state->path, ENTRIES_FNAME);
if (ret != NSERROR_OK) {
unlink(tname);
@@ -730,6 +753,8 @@ static nserror write_entries(struct store_state *state)
return NSERROR_SAVE_FAILED;
}
+ NSLOG(netsurf, INFO, "Wrote out %"PRIsizet" entries", weistate.written);
+
return NSERROR_OK;
}
@@ -777,7 +802,7 @@ static nserror write_blocks(struct store_state *state)
&state->blocks[elem_idx][bfidx].use_map[0],
BLOCK_USE_MAP_SIZE);
if (wr != BLOCK_USE_MAP_SIZE) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEBUG,
"writing block file %d use index on file number %d failed",
elem_idx,
bfidx);
@@ -836,21 +861,21 @@ static nserror set_block_extents(struct store_state *state)
return NSERROR_OK;
}
- NSLOG(netsurf, INFO, "Starting");
+ NSLOG(netsurf, DEBUG, "Starting");
for (elem_idx = 0; elem_idx < ENTRY_ELEM_COUNT; elem_idx++) {
for (bfidx = 0; bfidx < BLOCK_FILE_COUNT; bfidx++) {
if (state->blocks[elem_idx][bfidx].fd != -1) {
/* ensure block file is correct extent */
ftr = ftruncate(state->blocks[elem_idx][bfidx].fd, 1U << (log2_block_size[elem_idx] + BLOCK_ENTRY_COUNT));
if (ftr == -1) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Truncate failed errno:%d",
errno);
}
}
}
}
- NSLOG(netsurf, INFO, "Complete");
+ NSLOG(netsurf, DEBUG, "Complete");
state->blocks_opened = false;
@@ -866,7 +891,7 @@ static nserror set_block_extents(struct store_state *state)
*
* \param s store state to maintain.
*/
-static void control_maintinance(void *s)
+static void control_maintenance(void *s)
{
struct store_state *state = s;
@@ -892,36 +917,22 @@ static void control_maintinance(void *s)
static nserror
get_store_entry(struct store_state *state, nsurl *url, struct store_entry **bse)
{
- entry_ident_t ident;
- unsigned int sei; /* store entry index */
-
- NSLOG(netsurf, INFO, "url:%s", nsurl_access(url));
-
- /* use the url hash as the entry identifier */
- ident = nsurl_hash(url);
+ struct store_entry *ent;
- sei = BS_ENTRY_INDEX(ident, state);
+ ent = hashmap_lookup(state->entries, url);
- if (sei == 0) {
- NSLOG(netsurf, INFO, "Failed to find ident 0x%x in index",
- ident);
+ if (ent == NULL) {
return NSERROR_NOT_FOUND;
}
- if (state->entries[sei].ident != ident) {
- /* entry ident did not match */
- NSLOG(netsurf, INFO, "ident did not match entry");
- return NSERROR_NOT_FOUND;
- }
-
- *bse = &state->entries[sei];
+ *bse = ent;
- state->entries[sei].last_used = time(NULL);
- state->entries[sei].use_count++;
+ ent->last_used = time(NULL);
+ ent->use_count++;
state->entries_dirty = true;
- guit->misc->schedule(CONTROL_MAINT_TIME, control_maintinance, state);
+ guit->misc->schedule(CONTROL_MAINT_TIME, control_maintenance, state);
return NSERROR_OK;
}
@@ -979,13 +990,11 @@ set_store_entry(struct store_state *state,
const size_t datalen,
struct store_entry **bse)
{
- entry_ident_t ident;
- entry_index_t sei; /* store entry index */
struct store_entry *se;
nserror ret;
struct store_entry_element *elem;
- NSLOG(netsurf, INFO, "url:%s", nsurl_access(url));
+ NSLOG(netsurf, DEBUG, "url:%s", nsurl_access(url));
/* evict entries as required and ensure there is at least one
* new entry available.
@@ -995,40 +1004,12 @@ set_store_entry(struct store_state *state,
return ret;
}
- /* use the url hash as the entry identifier */
- ident = nsurl_hash(url);
-
- /* get the entry index from the ident */
- sei = BS_ENTRY_INDEX(ident, state);
- if (sei == 0) {
- /* allocating the next available entry */
- sei = state->last_entry;
- state->last_entry++;
- BS_ENTRY_INDEX(ident, state) = sei;
-
- /* the entry */
- se = &state->entries[sei];
-
- /* clear the new entry */
- memset(se, 0, sizeof(struct store_entry));
- } else {
- /* index found existing entry */
-
- /* the entry */
- se = &state->entries[sei];
-
- if (se->ident != ident) {
- /** @todo Is there a better heuristic than
- * first come, first served? Should we check
- * to see if the old entry is in use and if
- * not prefer the newly stored entry instead?
- */
- NSLOG(netsurf, INFO,
- "Entry index collision trying to replace %x with %x",
- se->ident,
- ident);
- return NSERROR_PERMISSION;
- }
+ se = hashmap_lookup(state->entries, url);
+ if (se == NULL) {
+ se = hashmap_insert(state->entries, url);
+ }
+ if (se == NULL) {
+ return NSERROR_NOMEM;
}
/* the entry element */
@@ -1039,13 +1020,12 @@ set_store_entry(struct store_state *state,
/* this entry cannot be removed as it has associated
* allocation.
*/
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"attempt to overwrite entry with in use data");
return NSERROR_PERMISSION;
}
/* set the common entry data */
- se->ident = ident;
se->use_count = 1;
se->last_used = time(NULL);
@@ -1066,7 +1046,7 @@ set_store_entry(struct store_state *state,
/* ensure control maintenance scheduled. */
state->entries_dirty = true;
- guit->misc->schedule(CONTROL_MAINT_TIME, control_maintinance, state);
+ guit->misc->schedule(CONTROL_MAINT_TIME, control_maintenance, state);
*bse = se;
@@ -1099,7 +1079,7 @@ store_open(struct store_state *state,
fname = store_fname(state, ident, elem_idx);
if (fname == NULL) {
- NSLOG(netsurf, INFO, "filename error");
+ NSLOG(netsurf, ERROR, "filename error");
return -1;
}
@@ -1107,14 +1087,14 @@ store_open(struct store_state *state,
if (openflags & O_CREAT) {
ret = netsurf_mkdir_all(fname);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, WARNING,
"file path \"%s\" could not be created", fname);
free(fname);
return -1;
}
}
- NSLOG(netsurf, INFO, "opening %s", fname);
+ NSLOG(netsurf, DEBUG, "opening %s", fname);
fd = open(fname, openflags, S_IRUSR | S_IWUSR);
free(fname);
@@ -1122,57 +1102,6 @@ store_open(struct store_state *state,
return fd;
}
-/**
- * Construct address ident to filesystem entry map
- *
- * To allow a filesystem entry to be found from it's identifier we
- * construct an mapping index. This is a hash map from the entries URL
- * (its unique key) to filesystem entry.
- *
- * As the entire entry list must be iterated over to construct the map
- * we also compute the total storage in use.
- *
- * @param state The backing store global state.
- * @return NSERROR_OK on success or NSERROR_NOMEM if the map storage
- * could not be allocated.
- */
-static nserror
-build_entrymap(struct store_state *state)
-{
- unsigned int eloop;
-
- NSLOG(netsurf, INFO, "Allocating %"PRIsizet" bytes for max of %d buckets",
- (1 << state->ident_bits) * sizeof(entry_index_t),
- 1 << state->ident_bits);
-
- state->addrmap = calloc(1 << state->ident_bits, sizeof(entry_index_t));
- if (state->addrmap == NULL) {
- return NSERROR_NOMEM;
- }
-
- state->total_alloc = 0;
-
- for (eloop = 1; eloop < state->last_entry; eloop++) {
-
- NSLOG(llcache, DEEPDEBUG,
- "entry:%d ident:0x%08x used:%d",
- eloop,
- BS_ADDRESS(state->entries[eloop].ident, state),
- state->entries[eloop].use_count);
-
- /* update the address map to point at the entry */
- BS_ENTRY_INDEX(state->entries[eloop].ident, state) = eloop;
-
- /* account for the storage space */
- state->total_alloc += state->entries[eloop].elem[ENTRY_ELEM_DATA].size;
- state->total_alloc += state->entries[eloop].elem[ENTRY_ELEM_META].size;
- /* ensure entry does not have any allocation state */
- state->entries[eloop].elem[ENTRY_ELEM_DATA].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
- state->entries[eloop].elem[ENTRY_ELEM_META].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
- }
-
- return NSERROR_OK;
-}
/**
* Unlink entries file
@@ -1206,46 +1135,74 @@ unlink_entries(struct store_state *state)
static nserror
read_entries(struct store_state *state)
{
- int fd;
- ssize_t rd;
- size_t entries_size;
char *fname = NULL;
+ char *url;
+ nsurl *nsurl;
nserror ret;
+ size_t read_entries = 0;
+ struct store_entry *ent;
+ int fd;
ret = netsurf_mkpath(&fname, NULL, 2, state->path, ENTRIES_FNAME);
if (ret != NSERROR_OK) {
return ret;
}
- entries_size = (1 << state->entry_bits) * sizeof(struct store_entry);
-
- NSLOG(netsurf, INFO,
- "Allocating %"PRIsizet" bytes for max of %d entries of %"PRIsizet" length elements %"PRIsizet" length",
- entries_size,
- 1 << state->entry_bits,
- sizeof(struct store_entry),
- sizeof(struct store_entry_element));
-
- state->entries = calloc(1, entries_size);
+ state->entries = hashmap_create(&entries_hashmap_parameters);
if (state->entries == NULL) {
free(fname);
return NSERROR_NOMEM;
}
fd = open(fname, O_RDWR);
- free(fname);
if (fd != -1) {
- rd = read(fd, state->entries, entries_size);
- close(fd);
- if (rd > 0) {
- state->last_entry = rd / sizeof(struct store_entry);
- NSLOG(netsurf, INFO, "Read %d entries",
- state->last_entry);
+ uint32_t urllen;
+ while (read(fd, &urllen, sizeof(urllen)) == sizeof(urllen)) {
+ url = calloc(1, urllen+1);
+ if (read(fd, url, urllen) != urllen) {
+ free(url);
+ close(fd);
+ free(fname);
+ return NSERROR_INIT_FAILED;
+ }
+ ret = nsurl_create(url, &nsurl);
+ if (ret != NSERROR_OK) {
+ free(url);
+ close(fd);
+ free(fname);
+ return ret;
+ }
+ free(url);
+ /* We have to be careful here about nsurl refs */
+ ent = hashmap_insert(state->entries, nsurl);
+ if (ent == NULL) {
+ nsurl_unref(nsurl);
+ close(fd);
+ free(fname);
+ return NSERROR_NOMEM;
+ }
+ /* At this point, ent actually owns a ref of nsurl */
+ if (read(fd, ent, sizeof(*ent)) != sizeof(*ent)) {
+ /* The read failed, so reset the ptr */
+ ent->url = nsurl; /* It already had a ref */
+ nsurl_unref(nsurl);
+ close(fd);
+ free(fname);
+ return NSERROR_INIT_FAILED;
+ }
+ ent->url = nsurl; /* It already owns a ref */
+ nsurl_unref(nsurl);
+ NSLOG(netsurf, DEBUG, "Successfully read entry for %s", nsurl_access(ent->url));
+ read_entries++;
+ state->total_alloc += ent->elem[ENTRY_ELEM_DATA].size;
+ state->total_alloc += ent->elem[ENTRY_ELEM_META].size;
}
- } else {
- /* could rebuild entries from fs */
- state->last_entry = 1;
+ close(fd);
}
+
+ NSLOG(netsurf, INFO, "Read %"PRIsizet" entries from cache", read_entries);
+
+ free(fname);
return NSERROR_OK;
}
@@ -1283,7 +1240,7 @@ read_blocks(struct store_state *state)
&state->blocks[elem_idx][bfidx].use_map[0],
BLOCK_USE_MAP_SIZE);
if (rd <= 0) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"reading block file %d use index on file number %d failed",
elem_idx,
bfidx);
@@ -1382,9 +1339,6 @@ write_control(struct store_state *state)
}
fprintf(fcontrol, "%u%c", CONTROL_VERSION, 0);
- fprintf(fcontrol, "%u%c", state->entry_bits, 0);
- fprintf(fcontrol, "%u%c", state->ident_bits, 0);
- fprintf(fcontrol, "%u%c", state->last_entry, 0);
fclose(fcontrol);
@@ -1404,8 +1358,6 @@ read_control(struct store_state *state)
nserror ret;
FILE *fcontrol;
unsigned int ctrlversion;
- unsigned int addrbits;
- unsigned int entrybits;
char *fname = NULL;
ret = netsurf_mkpath(&fname, NULL, 2, state->path, "control");
@@ -1443,27 +1395,8 @@ read_control(struct store_state *state)
goto control_error;
}
- /* second line is log2 max number of entries */
- if (fscanf(fcontrol, "%u", &entrybits) != 1) {
- goto control_error;
- }
- if (fgetc(fcontrol) != 0) {
- goto control_error;
- }
-
- /* second line is log2 size of address hash */
- if (fscanf(fcontrol, "%u", &addrbits) != 1) {
- goto control_error;
- }
- if (fgetc(fcontrol) != 0) {
- goto control_error;
- }
-
fclose(fcontrol);
- state->entry_bits = entrybits;
- state->ident_bits = addrbits;
-
return NSERROR_OK;
control_error: /* problem with the control file */
@@ -1515,22 +1448,10 @@ initialise(const struct llcache_store_parameters *parameters)
newstate->limit = parameters->limit;
newstate->hysteresis = parameters->hysteresis;
- if (parameters->address_size == 0) {
- newstate->ident_bits = DEFAULT_IDENT_SIZE;
- } else {
- newstate->ident_bits = parameters->address_size;
- }
-
- if (parameters->entry_size == 0) {
- newstate->entry_bits = DEFAULT_ENTRY_SIZE;
- } else {
- newstate->entry_bits = parameters->entry_size;
- }
-
/* read store control and create new if required */
ret = read_control(newstate);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "read control failed %s",
+ NSLOG(netsurf, ERROR, "read control failed %s",
messages_get_errorcode(ret));
ret = write_control(newstate);
if (ret == NSERROR_OK) {
@@ -1545,13 +1466,6 @@ initialise(const struct llcache_store_parameters *parameters)
return ret;
}
- /* ensure the maximum number of entries can be represented in
- * the type available to store it.
- */
- if (newstate->entry_bits > (8 * sizeof(entry_index_t))) {
- newstate->entry_bits = (8 * sizeof(entry_index_t));
- }
-
/* read filesystem entries */
ret = read_entries(newstate);
if (ret != NSERROR_OK) {
@@ -1561,21 +1475,11 @@ initialise(const struct llcache_store_parameters *parameters)
return ret;
}
- /* build entry hash map */
- ret = build_entrymap(newstate);
- if (ret != NSERROR_OK) {
- /* that obviously went well */
- free(newstate->entries);
- free(newstate->path);
- free(newstate);
- return ret;
- }
-
+ /* read blocks */
ret = read_blocks(newstate);
if (ret != NSERROR_OK) {
/* oh dear */
- free(newstate->addrmap);
- free(newstate->entries);
+ hashmap_destroy(newstate->entries);
free(newstate->path);
free(newstate);
return ret;
@@ -1586,12 +1490,10 @@ initialise(const struct llcache_store_parameters *parameters)
NSLOG(netsurf, INFO, "FS backing store init successful");
NSLOG(netsurf, INFO,
- "path:%s limit:%"PRIsizet" hyst:%"PRIsizet" addr:%d entries:%d",
+ "path:%s limit:%"PRIsizet" hyst:%"PRIsizet,
newstate->path,
newstate->limit,
- newstate->hysteresis,
- newstate->ident_bits,
- newstate->entry_bits);
+ newstate->hysteresis);
NSLOG(netsurf, INFO, "Using %"PRIu64"/%"PRIsizet,
newstate->total_alloc, newstate->limit);
@@ -1614,7 +1516,7 @@ finalise(void)
unsigned int op_count;
if (storestate != NULL) {
- guit->misc->schedule(-1, control_maintinance, storestate);
+ guit->misc->schedule(-1, control_maintenance, storestate);
write_entries(storestate);
write_blocks(storestate);
@@ -1643,8 +1545,7 @@ finalise(void)
0);
}
- free(storestate->addrmap);
- free(storestate->entries);
+ hashmap_destroy(storestate->entries);
free(storestate->path);
free(storestate);
storestate = NULL;
@@ -1676,7 +1577,7 @@ static nserror store_write_block(struct store_state *state,
state->blocks[elem_idx][bf].fd = store_open(state, bf,
elem_idx + ENTRY_ELEM_COUNT, O_CREAT | O_RDWR);
if (state->blocks[elem_idx][bf].fd == -1) {
- NSLOG(netsurf, INFO, "Open failed errno %d", errno);
+ NSLOG(netsurf, ERROR, "Open failed errno %d", errno);
return NSERROR_SAVE_FAILED;
}
@@ -1691,7 +1592,7 @@ static nserror store_write_block(struct store_state *state,
bse->elem[elem_idx].size,
offst);
if (wr != (ssize_t)bse->elem[elem_idx].size) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Write failed %"PRIssizet" of %d bytes from %p at 0x%jx block %d errno %d",
wr,
bse->elem[elem_idx].size,
@@ -1726,10 +1627,10 @@ static nserror store_write_file(struct store_state *state,
int fd;
int err;
- fd = store_open(state, bse->ident, elem_idx, O_CREAT | O_WRONLY);
+ fd = store_open(state, nsurl_hash(bse->url), elem_idx, O_CREAT | O_WRONLY);
if (fd < 0) {
perror("");
- NSLOG(netsurf, INFO, "Open failed %d errno %d", fd, errno);
+ NSLOG(netsurf, ERROR, "Open failed %d errno %d", fd, errno);
return NSERROR_SAVE_FAILED;
}
@@ -1738,7 +1639,7 @@ static nserror store_write_file(struct store_state *state,
close(fd);
if (wr != (ssize_t)bse->elem[elem_idx].size) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Write failed %"PRIssizet" of %d bytes from %p errno %d",
wr,
bse->elem[elem_idx].size,
@@ -1749,7 +1650,7 @@ static nserror store_write_file(struct store_state *state,
return NSERROR_SAVE_FAILED;
}
- NSLOG(netsurf, INFO, "Wrote %"PRIssizet" bytes from %p", wr,
+ NSLOG(netsurf, VERBOSE, "Wrote %"PRIssizet" bytes from %p", wr,
bse->elem[elem_idx].data);
return NSERROR_OK;
@@ -1791,7 +1692,7 @@ store(nsurl *url,
/* set the store entry up */
ret = set_store_entry(storestate, url, elem_idx, data, datalen, &bse);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "store entry setting failed");
+ NSLOG(netsurf, ERROR, "store entry setting failed");
return ret;
}
@@ -1814,7 +1715,7 @@ static nserror entry_release_alloc(struct store_entry_element *elem)
if ((elem->flags & ENTRY_ELEM_FLAG_HEAP) != 0) {
elem->ref--;
if (elem->ref == 0) {
- NSLOG(netsurf, INFO, "freeing %p", elem->data);
+ NSLOG(netsurf, DEEPDEBUG, "freeing %p", elem->data);
free(elem->data);
elem->flags &= ~ENTRY_ELEM_FLAG_HEAP;
}
@@ -1846,7 +1747,7 @@ static nserror store_read_block(struct store_state *state,
state->blocks[elem_idx][bf].fd = store_open(state, bf,
elem_idx + ENTRY_ELEM_COUNT, O_CREAT | O_RDWR);
if (state->blocks[elem_idx][bf].fd == -1) {
- NSLOG(netsurf, INFO, "Open failed errno %d", errno);
+ NSLOG(netsurf, ERROR, "Open failed errno %d", errno);
return NSERROR_SAVE_FAILED;
}
@@ -1861,7 +1762,7 @@ static nserror store_read_block(struct store_state *state,
bse->elem[elem_idx].size,
offst);
if (rd != (ssize_t)bse->elem[elem_idx].size) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Failed reading %"PRIssizet" of %d bytes into %p from 0x%jx block %d errno %d",
rd,
bse->elem[elem_idx].size,
@@ -1872,7 +1773,7 @@ static nserror store_read_block(struct store_state *state,
return NSERROR_SAVE_FAILED;
}
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEEPDEBUG,
"Read %"PRIssizet" bytes into %p from 0x%jx block %d", rd,
bse->elem[elem_idx].data, (uintmax_t)offst,
bse->elem[elem_idx].block);
@@ -1898,9 +1799,9 @@ static nserror store_read_file(struct store_state *state,
size_t tot = 0; /* total size */
/* separate file in backing store */
- fd = store_open(storestate, bse->ident, elem_idx, O_RDONLY);
+ fd = store_open(storestate, nsurl_hash(bse->url), elem_idx, O_RDONLY);
if (fd < 0) {
- NSLOG(netsurf, INFO, "Open failed %d errno %d", fd, errno);
+ NSLOG(netsurf, ERROR, "Open failed %d errno %d", fd, errno);
/** @todo should this invalidate the entry? */
return NSERROR_NOT_FOUND;
}
@@ -1910,7 +1811,7 @@ static nserror store_read_file(struct store_state *state,
bse->elem[elem_idx].data + tot,
bse->elem[elem_idx].size - tot);
if (rd <= 0) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"read error returned %"PRIssizet" errno %d",
rd,
errno);
@@ -1922,7 +1823,7 @@ static nserror store_read_file(struct store_state *state,
close(fd);
- NSLOG(netsurf, INFO, "Read %"PRIsizet" bytes into %p", tot,
+ NSLOG(netsurf, DEEPDEBUG, "Read %"PRIsizet" bytes into %p", tot,
bse->elem[elem_idx].data);
return ret;
@@ -1956,13 +1857,13 @@ fetch(nsurl *url,
/* fetch store entry */
ret = get_store_entry(storestate, url, &bse);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "entry not found");
+ NSLOG(netsurf, DEBUG, "Entry for %s not found", nsurl_access(url));
storestate->miss_count++;
return ret;
}
storestate->hit_count++;
- NSLOG(netsurf, INFO, "retrieving cache data for url:%s",
+ NSLOG(netsurf, DEBUG, "retrieving cache data for url:%s",
nsurl_access(url));
/* calculate the entry element index */
@@ -1978,7 +1879,7 @@ fetch(nsurl *url,
/* use the existing allocation and bump the ref count. */
elem->ref++;
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEEPDEBUG,
"Using existing entry (%p) allocation %p refs:%d", bse,
elem->data, elem->ref);
@@ -1986,11 +1887,11 @@ fetch(nsurl *url,
/* allocate from the heap */
elem->data = malloc(elem->size);
if (elem->data == NULL) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Failed to create new heap allocation");
return NSERROR_NOMEM;
}
- NSLOG(netsurf, INFO, "Created new heap allocation %p",
+ NSLOG(netsurf, DEEPDEBUG, "Created new heap allocation %p",
elem->data);
/* mark the entry as having a valid heap allocation */
@@ -2040,7 +1941,7 @@ static nserror release(nsurl *url, enum backing_store_flags bsflags)
ret = get_store_entry(storestate, url, &bse);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "entry not found");
+ NSLOG(netsurf, WARNING, "entry not found");
return ret;
}
diff --git a/content/llcache.h b/content/llcache.h
index 514272f..f9a3016 100644
--- a/content/llcache.h
+++ b/content/llcache.h
@@ -121,43 +121,6 @@ struct llcache_store_parameters {
size_t limit; /**< The backing store upper bound target size */
size_t hysteresis; /**< The hysteresis around the target size */
-
- /** log2 of the default maximum number of entries the cache
- * can track.
- *
- * If unset this defaults to 16 (65536 entries) The cache
- * control file takes precedence so cache data remains
- * portable between builds with differing defaults.
- */
- unsigned int entry_size;
-
- /** log2 of the default number of entries in the mapping between
- * the url and cache entries.
- *
- * @note This is exposing an internal implementation detail of
- * the filesystem based default backing store implementation.
- * However it is likely any backing store implementation will
- * need some way to map url to cache entries so it is a
- * generally useful configuration value.
- *
- * Too small a value will cause unecessary collisions and
- * cache misses and larger values cause proportionaly larger
- * amounts of memory to be used.
- *
- * The "birthday paradox" means that the hash will experience
- * a collision in every 2^(address_size/2) urls the cache
- * stores.
- *
- * A value of 20 means one object stored in every 1024 will
- * cause a collion and a cache miss while using two megabytes
- * of storage.
- *
- * If unset this defaults to 20 (1048576 entries using two
- * megabytes) The cache control file takes precedence so cache
- * data remains portable between builds with differing
- * defaults.
- */
- unsigned int address_size;
};
/**
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=ac75a9161e23b7073ed...
commit ac75a9161e23b7073edcf0ec07d584b87d2e0e08
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
llcache: Persist anything available during llcache_finalise
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/content/llcache.c b/content/llcache.c
index 3a75bf9..9c74fbd 100644
--- a/content/llcache.c
+++ b/content/llcache.c
@@ -3769,6 +3769,11 @@ void llcache_finalise(void)
llcache_object *object, *next;
uint64_t total_bandwidth = 0; /* total bandwidth */
+ /* Attempt to persist anything we have left lying around */
+ llcache_persist(NULL);
+ /* Now clear the persistence callback */
+ guit->misc->schedule(-1, llcache_persist, NULL);
+
/* Clean uncached objects */
for (object = llcache->uncached_objects; object != NULL; object = next) {
llcache_object_user *user, *next_user;
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=088917641f0865e11be...
commit 088917641f0865e11be5e81bf90de3dbc8cba19e
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
utils: Add hashmap_count()
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/test/hashmap.c b/test/hashmap.c
index 4bda493..ed951e9 100644
--- a/test/hashmap.c
+++ b/test/hashmap.c
@@ -197,7 +197,7 @@ basic_fixture_teardown(void)
START_TEST(empty_hashmap_create_destroy)
{
- /* Do nothing in here, all the checks are in the fixture */
+ ck_assert_int_eq(hashmap_count(test_hashmap), 0);
}
END_TEST
@@ -213,6 +213,7 @@ START_TEST(insert_works)
hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank);
ck_assert(value != NULL);
ck_assert(value->key == corestring_nsurl_about_blank);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 1);
}
END_TEST
@@ -229,9 +230,11 @@ START_TEST(insert_then_remove)
ck_assert(value->key == corestring_nsurl_about_blank);
ck_assert_int_eq(keys, 1);
ck_assert_int_eq(values, 1);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 1);
ck_assert(hashmap_remove(test_hashmap, corestring_nsurl_about_blank) == true);
ck_assert_int_eq(keys, 0);
ck_assert_int_eq(values, 0);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 0);
}
END_TEST
@@ -450,6 +453,7 @@ START_TEST(chain_add_all_twice_remove_all_iterate)
iteration_stop = 0;
ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
ck_assert_int_eq(iteration_counter, chain_count);
+ ck_assert_int_eq(hashmap_count(test_hashmap), chain_count);
iteration_counter = 0;
iteration_stop = chain_count;
@@ -469,6 +473,7 @@ START_TEST(chain_add_all_twice_remove_all_iterate)
ck_assert_int_eq(keys, 0);
ck_assert_int_eq(values, 0);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 0);
}
END_TEST
diff --git a/utils/hashmap.c b/utils/hashmap.c
index b7870a3..814dc55 100644
--- a/utils/hashmap.c
+++ b/utils/hashmap.c
@@ -55,6 +55,11 @@ struct hashmap_s {
* The number of buckets in this map
*/
uint32_t bucket_count;
+
+ /**
+ * The number of entries in this map
+ */
+ size_t entry_count;
};
/* Exported function, documented in hashmap.h */
@@ -65,14 +70,16 @@ hashmap_create(hashmap_parameters_t *params)
ret->params = params;
ret->bucket_count = DEFAULT_HASHMAP_BUCKETS;
+ ret->entry_count = 0;
ret->buckets = malloc(ret->bucket_count * sizeof(hashmap_entry_t *));
- memset(ret->buckets, 0, ret->bucket_count * sizeof(hashmap_entry_t *));
if (ret->buckets == NULL) {
free(ret);
return NULL;
}
-
+
+ memset(ret->buckets, 0, ret->bucket_count * sizeof(hashmap_entry_t *));
+
return ret;
}
@@ -176,7 +183,9 @@ hashmap_insert(hashmap_t *hashmap, void *key)
}
hashmap->buckets[bucket] = entry;
-
+
+ hashmap->entry_count++;
+
return entry->value;
err:
@@ -207,6 +216,7 @@ hashmap_remove(hashmap_t *hashmap, void *key)
}
*entry->prevptr = entry->next;
free(entry);
+ hashmap->entry_count--;
return true;
}
}
@@ -233,3 +243,10 @@ hashmap_iterate(hashmap_t *hashmap, hashmap_iteration_cb_t cb, void *ctx)
return false;
}
+
+/* Exported function, documented in hashmap.h */
+size_t
+hashmap_count(hashmap_t *hashmap)
+{
+ return hashmap->entry_count;
+}
diff --git a/utils/hashmap.h b/utils/hashmap.h
index cb8fd50..3968fd3 100644
--- a/utils/hashmap.h
+++ b/utils/hashmap.h
@@ -186,4 +186,12 @@ bool hashmap_remove(hashmap_t *hashmap, void *key);
*/
bool hashmap_iterate(hashmap_t *hashmap, hashmap_iteration_cb_t cb, void *ctx);
+/**
+ * Get the number of entries in this map
+ *
+ * \param hashmap The hashmap to retrieve the entry count from
+ * \return The number of entries in the hashmap
+ */
+size_t hashmap_count(hashmap_t *hashmap);
+
#endif
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=3e02961ec8f21fd656c...
commit 3e02961ec8f21fd656c8b306c5075c0c799df97f
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
utils: Fix destroy of non-empty hashmap
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/utils/hashmap.c b/utils/hashmap.c
index bfbf6ce..b7870a3 100644
--- a/utils/hashmap.c
+++ b/utils/hashmap.c
@@ -85,11 +85,12 @@ hashmap_destroy(hashmap_t *hashmap)
for (bucket = 0; bucket < hashmap->bucket_count; bucket++) {
for (entry = hashmap->buckets[bucket];
- entry != NULL;
- entry = entry->next) {
+ entry != NULL;) {
+ hashmap_entry_t *next = entry->next;
hashmap->params->value_destroy(entry->value);
hashmap->params->key_destroy(entry->key);
free(entry);
+ entry = next;
}
}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=54b1960d18042cf6dfd...
commit 54b1960d18042cf6dfd86cfe01d58455357586d2
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
utils: Add iteration API to hashmap
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/test/hashmap.c b/test/hashmap.c
index 87db6c1..4bda493 100644
--- a/test/hashmap.c
+++ b/test/hashmap.c
@@ -151,6 +151,20 @@ static hashmap_parameters_t test_params = {
.value_destroy = value_destroy,
};
+/* Iteration helpers */
+
+static size_t iteration_counter = 0;
+static size_t iteration_stop = 0;
+static char iteration_ctx = 0;
+
+static bool
+hashmap_test_iterator_cb(void *key, void *value, void *ctx)
+{
+ ck_assert(ctx == &iteration_ctx);
+ iteration_counter++;
+ return iteration_counter == iteration_stop;
+}
+
/* Fixtures for basic tests */
static hashmap_t *test_hashmap = NULL;
@@ -230,6 +244,35 @@ START_TEST(insert_then_lookup)
}
END_TEST
+START_TEST(iterate_empty)
+{
+ iteration_stop = iteration_counter = 0;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, 0);
+}
+END_TEST
+
+START_TEST(iterate_one)
+{
+ iteration_stop = iteration_counter = 0;
+ hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank);
+ ck_assert(value != NULL);
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, 1);
+}
+END_TEST
+
+START_TEST(iterate_one_and_stop)
+{
+ iteration_stop = 1;
+ iteration_counter = 0;
+ hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank);
+ ck_assert(value != NULL);
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == true);
+ ck_assert_int_eq(iteration_counter, 1);
+}
+END_TEST
+
static TCase *basic_api_case_create(void)
{
TCase *tc;
@@ -245,7 +288,11 @@ static TCase *basic_api_case_create(void)
tcase_add_test(tc, remove_not_present);
tcase_add_test(tc, insert_then_remove);
tcase_add_test(tc, insert_then_lookup);
-
+
+ tcase_add_test(tc, iterate_empty);
+ tcase_add_test(tc, iterate_one);
+ tcase_add_test(tc, iterate_one_and_stop);
+
return tc;
}
@@ -374,6 +421,57 @@ START_TEST(chain_add_all_twice_remove_all)
}
END_TEST
+START_TEST(chain_add_all_twice_remove_all_iterate)
+{
+ case_pair *chain_case;
+ size_t chain_count = 0;
+
+ for (chain_case = chain_pairs;
+ chain_case->url != NULL;
+ chain_case++) {
+ ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) == NULL);
+ ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL);
+ chain_count++;
+ }
+
+ iteration_counter = 0;
+ iteration_stop = 0;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, chain_count);
+
+ for (chain_case = chain_pairs;
+ chain_case->url != NULL;
+ chain_case++) {
+ ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) != NULL);
+ ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL);
+ }
+
+ iteration_counter = 0;
+ iteration_stop = 0;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, chain_count);
+
+ iteration_counter = 0;
+ iteration_stop = chain_count;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == true);
+ ck_assert_int_eq(iteration_counter, chain_count);
+
+ for (chain_case = chain_pairs;
+ chain_case->url != NULL;
+ chain_case++) {
+ ck_assert(hashmap_remove(test_hashmap, chain_case->nsurl) == true);
+ }
+
+ iteration_counter = 0;
+ iteration_stop = chain_count;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, 0);
+
+ ck_assert_int_eq(keys, 0);
+ ck_assert_int_eq(values, 0);
+}
+END_TEST
+
#define CHAIN_TEST_MALLOC_COUNT_MAX 60
START_TEST(chain_add_all_remove_all_alloc)
@@ -431,6 +529,7 @@ static TCase *chain_case_create(void)
tcase_add_test(tc, chain_add_remove_all);
tcase_add_test(tc, chain_add_all_remove_all);
tcase_add_test(tc, chain_add_all_twice_remove_all);
+ tcase_add_test(tc, chain_add_all_twice_remove_all_iterate);
tcase_add_loop_test(tc, chain_add_all_remove_all_alloc, 0, CHAIN_TEST_MALLOC_COUNT_MAX + 1);
diff --git a/utils/hashmap.c b/utils/hashmap.c
index 7ed1994..bfbf6ce 100644
--- a/utils/hashmap.c
+++ b/utils/hashmap.c
@@ -213,3 +213,22 @@ hashmap_remove(hashmap_t *hashmap, void *key)
return false;
}
+
+/* Exported function, documented in hashmap.h */
+bool
+hashmap_iterate(hashmap_t *hashmap, hashmap_iteration_cb_t cb, void *ctx)
+{
+ for (uint32_t bucket = 0;
+ bucket < hashmap->bucket_count;
+ bucket++) {
+ for (hashmap_entry_t *entry = hashmap->buckets[bucket];
+ entry != NULL;
+ entry = entry->next) {
+ /* If the callback returns true, we early-exit */
+ if (cb(entry->key, entry->value, ctx))
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/utils/hashmap.h b/utils/hashmap.h
index 462b51e..cb8fd50 100644
--- a/utils/hashmap.h
+++ b/utils/hashmap.h
@@ -62,6 +62,16 @@ typedef void* (*hashmap_value_alloc_t)(void *);
typedef void (*hashmap_value_destroy_t)(void *);
/**
+ * Hashmap iteration callback function type.
+ *
+ * First parameter is the key, second is the value.
+ * The final parameter is the context pointer for the iteration.
+ *
+ * Return true to stop iterating early
+ */
+typedef bool (*hashmap_iteration_cb_t)(void *, void *, void *);
+
+/**
* Parameters for hashmaps
*/
typedef struct {
@@ -163,5 +173,17 @@ void *hashmap_insert(hashmap_t *hashmap, void *key);
*/
bool hashmap_remove(hashmap_t *hashmap, void *key);
+/**
+ * Iterate the hashmap
+ *
+ * For each key/value pair in the hashmap, call the callback passing in
+ * the key and value. During iteration you MUST NOT mutate the hashmap.
+ *
+ * \param hashmap The hashmap to iterate
+ * \param cb The callback for each key,value pair
+ * \param ctx The callback context
+ * \return Whether or not we stopped iteration early
+ */
+bool hashmap_iterate(hashmap_t *hashmap, hashmap_iteration_cb_t cb, void *ctx);
#endif
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=fd80341513813684ba3...
commit fd80341513813684ba3155b87e4e6cc8c87631f1
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
utils: Add hashmap to sources
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/utils/Makefile b/utils/Makefile
index e0fbd8e..fa5f58d 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -6,6 +6,7 @@ S_UTILS := \
file.c \
filename.c \
filepath.c \
+ hashmap.c \
hashtable.c \
idna.c \
libdom.c \
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=61187d31ab7ba292510...
commit 61187d31ab7ba292510e1e56886325a1c6a1c822
Author: Daniel Silverstone <dsilvers(a)digital-scurf.org>
Commit: Daniel Silverstone <dsilvers(a)digital-scurf.org>
utils: Add hashmap parameter function types
Signed-off-by: Daniel Silverstone <dsilvers(a)digital-scurf.org>
diff --git a/utils/hashmap.h b/utils/hashmap.h
index 4e1237a..462b51e 100644
--- a/utils/hashmap.h
+++ b/utils/hashmap.h
@@ -32,6 +32,36 @@
typedef struct hashmap_s hashmap_t;
/**
+ * Key cloning function type
+ */
+typedef void* (*hashmap_key_clone_t)(void *);
+
+/**
+ * Key destructor function type
+ */
+typedef void (*hashmap_key_destroy_t)(void *);
+
+/**
+ * Key hashing function type
+ */
+typedef uint32_t (*hashmap_key_hash_t)(void *);
+
+/**
+ * Key comparison function type
+ */
+typedef bool (*hashmap_key_eq_t)(void *, void*);
+
+/**
+ * Value allocation function type
+ */
+typedef void* (*hashmap_value_alloc_t)(void *);
+
+/**
+ * Value destructor function type
+ */
+typedef void (*hashmap_value_destroy_t)(void *);
+
+/**
* Parameters for hashmaps
*/
typedef struct {
@@ -39,12 +69,12 @@ typedef struct {
* A function which when called will clone a key and give
* ownership of the returned object to the hashmap
*/
- void * (*key_clone)(void *key);
+ hashmap_key_clone_t key_clone;
/**
* A function which when given a key will return its hash.
*/
- uint32_t (*key_hash)(void *key);
+ hashmap_key_hash_t key_hash;
/**
* A function to compare two keys and return if they are equal.
@@ -52,22 +82,22 @@ typedef struct {
* as the function is a full equality model.
* (i.e. key1 == key2 => key2 == key1)
*/
- bool (*key_eq)(void *key1, void *key2);
+ hashmap_key_eq_t key_eq;
/**
* A function which when called will destroy a key object
*/
- void (*key_destroy)(void *key);
+ hashmap_key_destroy_t key_destroy;
/**
* A function which when called will allocate a value object
*/
- void * (*value_alloc)(void *key);
+ hashmap_value_alloc_t value_alloc;
/**
* A function which when called will destroy a value object
*/
- void (*value_destroy)(void *value);
+ hashmap_value_destroy_t value_destroy;
} hashmap_parameters_t;
-----------------------------------------------------------------------
Summary of changes:
content/fs_backing_store.c | 597 ++++++++++++++++++--------------------------
content/llcache.c | 5 +
content/llcache.h | 37 ---
test/hashmap.c | 108 +++++++-
utils/Makefile | 1 +
utils/hashmap.c | 47 +++-
utils/hashmap.h | 72 +++++-
7 files changed, 469 insertions(+), 398 deletions(-)
diff --git a/content/fs_backing_store.c b/content/fs_backing_store.c
index 19eb1ca..71d1c83 100644
--- a/content/fs_backing_store.c
+++ b/content/fs_backing_store.c
@@ -49,6 +49,7 @@
#include "utils/nsurl.h"
#include "utils/log.h"
#include "utils/messages.h"
+#include "utils/hashmap.h"
#include "desktop/gui_internal.h"
#include "netsurf/misc.h"
@@ -61,20 +62,11 @@
#define DEFAULT_ENTRY_SIZE 16
/** Backing store file format version */
-#define CONTROL_VERSION 130
+#define CONTROL_VERSION 200
/** Number of milliseconds after a update before control data maintenance is performed */
#define CONTROL_MAINT_TIME 10000
-/** Get address from ident */
-#define BS_ADDRESS(ident, state) ((ident) & ((1 << state->ident_bits) - 1))
-
-/** Lookup store entry index from ident */
-#define BS_ENTRY_INDEX(ident, state) state->addrmap[(ident) & ((1 << state->ident_bits) - 1)]
-
-/** Get store entry from ident. */
-#define BS_ENTRY(ident, state) state->entries[state->addrmap[(ident) & ((1 << state->ident_bits) - 1)]]
-
/** Filename of serialised entries */
#define ENTRIES_FNAME "entries"
@@ -184,8 +176,8 @@ struct store_entry_element {
* @note Order is important to avoid excessive structure packing overhead.
*/
struct store_entry {
+ nsurl *url; /**< The URL for this entry */
int64_t last_used; /**< UNIX time the entry was last used */
- entry_ident_t ident; /**< entry identifier */
uint16_t use_count; /**< number of times this entry has been accessed */
uint8_t flags; /**< entry flags */
/** Entry element (data or meta) specific information */
@@ -219,29 +211,16 @@ struct store_state {
size_t limit; /**< The backing store upper bound target size */
size_t hysteresis; /**< The hysteresis around the target size */
- unsigned int ident_bits; /**< log2 number of bits to use for address. */
-
-
- /* cache entry management */
- struct store_entry *entries; /**< store entries. */
- unsigned int entry_bits; /**< log2 number of bits in entry index. */
- unsigned int last_entry; /**< index of last usable entry. */
+ /**
+ * The cache object hash
+ */
+ hashmap_t *entries;
/** flag indicating if the entries have been made persistent
* since they were last changed.
*/
bool entries_dirty;
- /**
- * URL identifier to entry index mapping.
- *
- * This is an open coded index on the entries URL field and
- * provides a computationally inexpensive way to go from the
- * URL to an entry.
- */
- entry_index_t *addrmap;
-
-
/** small block indexes */
struct block_file blocks[ENTRY_ELEM_COUNT][BLOCK_FILE_COUNT];
@@ -273,60 +252,44 @@ struct store_state {
*/
struct store_state *storestate;
-
-/**
- * Remove a backing store entry from the entry table.
- *
- * This finds the store entry associated with the given key and
- * removes it from the table. The removed entry is returned but is
- * only valid until the next set_store_entry call.
+/* Entries hashmap parameters
*
- * @param[in] state The store state to use.
- * @param[in, out] bse Pointer to the entry to be removed.
- * @return NSERROR_OK and \a bse updated on success or NSERROR_NOT_FOUND
- * if no entry corresponds to the URL.
+ * Our hashmap has nsurl keys and store_entry values
*/
-static nserror
-remove_store_entry(struct store_state *state, struct store_entry **bse)
-{
- entry_index_t sei; /* store entry index */
-
- /* sei is index to entry to be removed, we swap it to the end
- * of the table so there are no gaps and the returned entry is
- * held in storage with reasonable lifetime.
- */
-
- sei = BS_ENTRY_INDEX((*bse)->ident, state);
-
- /* remove entry from map */
- BS_ENTRY_INDEX((*bse)->ident, state) = 0;
- /* global allocation accounting */
- state->total_alloc -= state->entries[sei].elem[ENTRY_ELEM_DATA].size;
- state->total_alloc -= state->entries[sei].elem[ENTRY_ELEM_META].size;
-
- state->last_entry--;
-
- if (sei == state->last_entry) {
- /* the removed entry was the last one, how convenient */
- *bse = &state->entries[sei];
- } else {
- /* need to swap entries */
- struct store_entry tent;
-
- tent = state->entries[sei];
- state->entries[sei] = state->entries[state->last_entry];
- state->entries[state->last_entry] = tent;
-
- /* update map for moved entry */
- BS_ENTRY_INDEX(state->entries[sei].ident, state) = sei;
+static bool
+entries_hashmap_key_eq(void *key1, void *key2)
+{
+ return nsurl_compare((nsurl *)key1, (nsurl *)key2, NSURL_COMPLETE);
+}
- *bse = &state->entries[state->last_entry];
+static void *
+entries_hashmap_value_alloc(void *key)
+{
+ struct store_entry *ent = calloc(1, sizeof(struct store_entry));
+ if (ent != NULL) {
+ ent->url = nsurl_ref(key);
}
+ return ent;
+}
- return NSERROR_OK;
+static void
+entries_hashmap_value_destroy(void *value)
+{
+ struct store_entry *ent = value;
+ /** \todo Do we need to do any disk cleanup here? if so, meep! */
+ nsurl_unref(ent->url);
+ free(ent);
}
+static hashmap_parameters_t entries_hashmap_parameters = {
+ .key_clone = (hashmap_key_clone_t)nsurl_ref,
+ .key_destroy = (hashmap_key_destroy_t)nsurl_unref,
+ .key_hash = (hashmap_key_hash_t)nsurl_hash,
+ .key_eq = entries_hashmap_key_eq,
+ .value_alloc = entries_hashmap_value_alloc,
+ .value_destroy = entries_hashmap_value_destroy,
+};
/**
* Generate a filename for an object.
@@ -484,7 +447,7 @@ invalidate_element(struct store_state *state,
char *fname;
/* unlink the file from disc */
- fname = store_fname(state, bse->ident, elem_idx);
+ fname = store_fname(state, nsurl_hash(bse->url), elem_idx);
if (fname == NULL) {
return NSERROR_NOMEM;
}
@@ -492,6 +455,8 @@ invalidate_element(struct store_state *state,
free(fname);
}
+ state->total_alloc -= bse->elem[elem_idx].size;
+
return NSERROR_OK;
}
@@ -519,29 +484,27 @@ invalidate_entry(struct store_state *state, struct store_entry *bse)
* This entry cannot be immediately removed as it has
* associated allocation so wait for allocation release.
*/
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEBUG,
"invalidating entry with referenced allocation");
return NSERROR_OK;
}
- NSLOG(netsurf, INFO, "Removing entry for %p", bse);
-
- /* remove the entry from the index */
- ret = remove_store_entry(state, &bse);
- if (ret != NSERROR_OK) {
- return ret;
- }
+ NSLOG(netsurf, VERBOSE, "Removing entry for %s", nsurl_access(bse->url));
ret = invalidate_element(state, bse, ENTRY_ELEM_META);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "Error invalidating metadata element");
+ NSLOG(netsurf, ERROR, "Error invalidating metadata element");
}
ret = invalidate_element(state, bse, ENTRY_ELEM_DATA);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "Error invalidating data element");
+ NSLOG(netsurf, ERROR, "Error invalidating data element");
}
+ /* As our final act we remove bse from the cache */
+ hashmap_remove(state->entries, bse->url);
+ /* From now, bse is invalid memory */
+
return NSERROR_OK;
}
@@ -551,8 +514,8 @@ invalidate_entry(struct store_state *state, struct store_entry *bse)
*/
static int compar(const void *va, const void *vb)
{
- const struct store_entry *a = &BS_ENTRY(*(entry_ident_t *)va, storestate);
- const struct store_entry *b = &BS_ENTRY(*(entry_ident_t *)vb, storestate);
+ const struct store_entry *a = *(const struct store_entry **)va;
+ const struct store_entry *b = *(const struct store_entry **)vb;
/* consider the allocation flags - if an entry has an
* allocation it is considered more valuable as it cannot be
@@ -591,6 +554,22 @@ static int compar(const void *va, const void *vb)
return 0;
}
+typedef struct {
+ struct store_entry **elist;
+ size_t ent_count;
+} eviction_state_t;
+
+/**
+ * Iterator for gathering entries to compute eviction order
+ */
+static bool
+entry_eviction_iterator_cb(void *key, void *value, void *ctx)
+{
+ eviction_state_t *estate = ctx;
+ struct store_entry *ent = value;
+ estate->elist[estate->ent_count++] = ent;
+ return false;
+}
/**
* Evict entries from backing store as per configuration.
@@ -608,15 +587,14 @@ static int compar(const void *va, const void *vb)
*/
static nserror store_evict(struct store_state *state)
{
- entry_ident_t *elist; /* sorted list of entry identifiers */
- unsigned int ent;
- unsigned int ent_count;
- size_t removed; /* size of removed entries */
+ size_t ent = 0;
+ size_t removed = 0; /* size of removed entries */
nserror ret = NSERROR_OK;
+ size_t old_count;
+ eviction_state_t estate;
/* check if the cache has exceeded configured limit */
- if ((state->total_alloc < state->limit) &&
- (state->last_entry < (1U << state->entry_bits))) {
+ if (state->total_alloc < state->limit) {
/* cache within limits */
return NSERROR_OK;
}
@@ -627,24 +605,31 @@ static nserror store_evict(struct store_state *state)
state->hysteresis);
/* allocate storage for the list */
- elist = malloc(sizeof(entry_ident_t) * state->last_entry);
- if (elist == NULL) {
+ old_count = hashmap_count(state->entries);
+ estate.ent_count = 0;
+ estate.elist = malloc(sizeof(struct state_entry*) * old_count);
+ if (estate.elist == NULL) {
return NSERROR_NOMEM;
}
- /* sort the list avoiding entry 0 which is the empty sentinel */
- for (ent = 1; ent < state->last_entry; ent++) {
- elist[ent - 1] = state->entries[ent].ident;
+ if (hashmap_iterate(state->entries, entry_eviction_iterator_cb, &estate)) {
+ NSLOG(netsurf, WARNING, "Unexpected termination of eviction iterator");
+ free(estate.elist);
+ return NSERROR_UNKNOWN;
}
- ent_count = ent - 1; /* important to keep this as the entry count will change when entries are removed */
- qsort(elist, ent_count, sizeof(entry_ident_t), compar);
+
+ if (old_count != estate.ent_count) {
+ NSLOG(netsurf, WARNING, "Incorrect entry count after eviction iterator");
+ free(estate.elist);
+ return NSERROR_UNKNOWN;
+ }
+
+ qsort(estate.elist, estate.ent_count, sizeof(struct state_entry*), compar);
/* evict entries in listed order */
removed = 0;
- for (ent = 0; ent < ent_count; ent++) {
- struct store_entry *bse;
-
- bse = &BS_ENTRY(elist[ent], state);
+ for (ent = 0; ent < estate.ent_count; ent++) {
+ struct store_entry *bse = estate.elist[ent];
removed += bse->elem[ENTRY_ELEM_DATA].size;
removed += bse->elem[ENTRY_ELEM_META].size;
@@ -659,14 +644,55 @@ static nserror store_evict(struct store_state *state)
}
}
- free(elist);
+ free(estate.elist);
- NSLOG(netsurf, INFO, "removed %"PRIsizet" in %d entries", removed,
- ent);
+ NSLOG(netsurf, INFO,
+ "removed %"PRIsizet" in %"PRIsizet" entries, %"PRIsizet" remaining in %"PRIsizet" entries",
+ removed, ent, state->total_alloc, old_count - ent);
return ret;
}
+/**
+ * Write a single store entry to disk
+ *
+ * To serialise a single store entry for now we write out a 32bit int
+ * which is the length of the url, then that many bytes of the url.
+ * Then we write out the full store entry struct as-is, which includes
+ * a useless nsurl pointer.
+ */
+static nserror
+write_entry(struct store_entry *ent, int fd)
+{
+ uint32_t len = strlen(nsurl_access(ent->url));
+ if (write(fd, &len, sizeof(len)) != sizeof(len))
+ return NSERROR_SAVE_FAILED;
+ if (write(fd, nsurl_access(ent->url), len) != len)
+ return NSERROR_SAVE_FAILED;
+ if (write(fd, ent, sizeof(*ent)) != sizeof(*ent))
+ return NSERROR_SAVE_FAILED;
+
+ return NSERROR_OK;
+}
+
+typedef struct {
+ int fd;
+ size_t written;
+} write_entry_iteration_state;
+
+/**
+ * Callback for iterating the entries hashmap
+ */
+static bool
+write_entry_iterator(void *key, void *value, void *ctx)
+{
+ /* We ignore the key */
+ struct store_entry *ent = value;
+ write_entry_iteration_state *state = ctx;
+ state->written++;
+ /* We stop early if we fail to write this entry */
+ return write_entry(ent, state->fd) != NSERROR_OK;
+}
/**
* Write filesystem entries to file.
@@ -678,13 +704,13 @@ static nserror store_evict(struct store_state *state)
*/
static nserror write_entries(struct store_state *state)
{
- int fd;
char *tname = NULL; /* temporary file name for atomic replace */
char *fname = NULL; /* target filename */
- size_t entries_size;
- size_t written;
+ write_entry_iteration_state weistate;
nserror ret;
+ memset(&weistate, 0, sizeof(weistate));
+
if (state->entries_dirty == false) {
/* entries have not been updated since last write */
return NSERROR_OK;
@@ -695,25 +721,22 @@ static nserror write_entries(struct store_state *state)
return ret;
}
- fd = open(tname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
- if (fd == -1) {
+ weistate.fd = open(tname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (weistate.fd == -1) {
free(tname);
return NSERROR_SAVE_FAILED;
}
- entries_size = state->last_entry * sizeof(struct store_entry);
-
- written = (size_t)write(fd, state->entries, entries_size);
-
- close(fd);
-
- /* check all data was written */
- if (written != entries_size) {
+ if (hashmap_iterate(state->entries, write_entry_iterator, &weistate)) {
+ /* The iteration ended early, so we failed */
+ close(weistate.fd);
unlink(tname);
free(tname);
return NSERROR_SAVE_FAILED;
}
+ close(weistate.fd);
+
ret = netsurf_mkpath(&fname, NULL, 2, state->path, ENTRIES_FNAME);
if (ret != NSERROR_OK) {
unlink(tname);
@@ -730,6 +753,8 @@ static nserror write_entries(struct store_state *state)
return NSERROR_SAVE_FAILED;
}
+ NSLOG(netsurf, INFO, "Wrote out %"PRIsizet" entries", weistate.written);
+
return NSERROR_OK;
}
@@ -777,7 +802,7 @@ static nserror write_blocks(struct store_state *state)
&state->blocks[elem_idx][bfidx].use_map[0],
BLOCK_USE_MAP_SIZE);
if (wr != BLOCK_USE_MAP_SIZE) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEBUG,
"writing block file %d use index on file number %d failed",
elem_idx,
bfidx);
@@ -836,21 +861,21 @@ static nserror set_block_extents(struct store_state *state)
return NSERROR_OK;
}
- NSLOG(netsurf, INFO, "Starting");
+ NSLOG(netsurf, DEBUG, "Starting");
for (elem_idx = 0; elem_idx < ENTRY_ELEM_COUNT; elem_idx++) {
for (bfidx = 0; bfidx < BLOCK_FILE_COUNT; bfidx++) {
if (state->blocks[elem_idx][bfidx].fd != -1) {
/* ensure block file is correct extent */
ftr = ftruncate(state->blocks[elem_idx][bfidx].fd, 1U << (log2_block_size[elem_idx] + BLOCK_ENTRY_COUNT));
if (ftr == -1) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Truncate failed errno:%d",
errno);
}
}
}
}
- NSLOG(netsurf, INFO, "Complete");
+ NSLOG(netsurf, DEBUG, "Complete");
state->blocks_opened = false;
@@ -866,7 +891,7 @@ static nserror set_block_extents(struct store_state *state)
*
* \param s store state to maintain.
*/
-static void control_maintinance(void *s)
+static void control_maintenance(void *s)
{
struct store_state *state = s;
@@ -892,36 +917,22 @@ static void control_maintinance(void *s)
static nserror
get_store_entry(struct store_state *state, nsurl *url, struct store_entry **bse)
{
- entry_ident_t ident;
- unsigned int sei; /* store entry index */
-
- NSLOG(netsurf, INFO, "url:%s", nsurl_access(url));
-
- /* use the url hash as the entry identifier */
- ident = nsurl_hash(url);
+ struct store_entry *ent;
- sei = BS_ENTRY_INDEX(ident, state);
+ ent = hashmap_lookup(state->entries, url);
- if (sei == 0) {
- NSLOG(netsurf, INFO, "Failed to find ident 0x%x in index",
- ident);
+ if (ent == NULL) {
return NSERROR_NOT_FOUND;
}
- if (state->entries[sei].ident != ident) {
- /* entry ident did not match */
- NSLOG(netsurf, INFO, "ident did not match entry");
- return NSERROR_NOT_FOUND;
- }
-
- *bse = &state->entries[sei];
+ *bse = ent;
- state->entries[sei].last_used = time(NULL);
- state->entries[sei].use_count++;
+ ent->last_used = time(NULL);
+ ent->use_count++;
state->entries_dirty = true;
- guit->misc->schedule(CONTROL_MAINT_TIME, control_maintinance, state);
+ guit->misc->schedule(CONTROL_MAINT_TIME, control_maintenance, state);
return NSERROR_OK;
}
@@ -979,13 +990,11 @@ set_store_entry(struct store_state *state,
const size_t datalen,
struct store_entry **bse)
{
- entry_ident_t ident;
- entry_index_t sei; /* store entry index */
struct store_entry *se;
nserror ret;
struct store_entry_element *elem;
- NSLOG(netsurf, INFO, "url:%s", nsurl_access(url));
+ NSLOG(netsurf, DEBUG, "url:%s", nsurl_access(url));
/* evict entries as required and ensure there is at least one
* new entry available.
@@ -995,40 +1004,12 @@ set_store_entry(struct store_state *state,
return ret;
}
- /* use the url hash as the entry identifier */
- ident = nsurl_hash(url);
-
- /* get the entry index from the ident */
- sei = BS_ENTRY_INDEX(ident, state);
- if (sei == 0) {
- /* allocating the next available entry */
- sei = state->last_entry;
- state->last_entry++;
- BS_ENTRY_INDEX(ident, state) = sei;
-
- /* the entry */
- se = &state->entries[sei];
-
- /* clear the new entry */
- memset(se, 0, sizeof(struct store_entry));
- } else {
- /* index found existing entry */
-
- /* the entry */
- se = &state->entries[sei];
-
- if (se->ident != ident) {
- /** @todo Is there a better heuristic than
- * first come, first served? Should we check
- * to see if the old entry is in use and if
- * not prefer the newly stored entry instead?
- */
- NSLOG(netsurf, INFO,
- "Entry index collision trying to replace %x with %x",
- se->ident,
- ident);
- return NSERROR_PERMISSION;
- }
+ se = hashmap_lookup(state->entries, url);
+ if (se == NULL) {
+ se = hashmap_insert(state->entries, url);
+ }
+ if (se == NULL) {
+ return NSERROR_NOMEM;
}
/* the entry element */
@@ -1039,13 +1020,12 @@ set_store_entry(struct store_state *state,
/* this entry cannot be removed as it has associated
* allocation.
*/
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"attempt to overwrite entry with in use data");
return NSERROR_PERMISSION;
}
/* set the common entry data */
- se->ident = ident;
se->use_count = 1;
se->last_used = time(NULL);
@@ -1066,7 +1046,7 @@ set_store_entry(struct store_state *state,
/* ensure control maintenance scheduled. */
state->entries_dirty = true;
- guit->misc->schedule(CONTROL_MAINT_TIME, control_maintinance, state);
+ guit->misc->schedule(CONTROL_MAINT_TIME, control_maintenance, state);
*bse = se;
@@ -1099,7 +1079,7 @@ store_open(struct store_state *state,
fname = store_fname(state, ident, elem_idx);
if (fname == NULL) {
- NSLOG(netsurf, INFO, "filename error");
+ NSLOG(netsurf, ERROR, "filename error");
return -1;
}
@@ -1107,14 +1087,14 @@ store_open(struct store_state *state,
if (openflags & O_CREAT) {
ret = netsurf_mkdir_all(fname);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, WARNING,
"file path \"%s\" could not be created", fname);
free(fname);
return -1;
}
}
- NSLOG(netsurf, INFO, "opening %s", fname);
+ NSLOG(netsurf, DEBUG, "opening %s", fname);
fd = open(fname, openflags, S_IRUSR | S_IWUSR);
free(fname);
@@ -1122,57 +1102,6 @@ store_open(struct store_state *state,
return fd;
}
-/**
- * Construct address ident to filesystem entry map
- *
- * To allow a filesystem entry to be found from it's identifier we
- * construct an mapping index. This is a hash map from the entries URL
- * (its unique key) to filesystem entry.
- *
- * As the entire entry list must be iterated over to construct the map
- * we also compute the total storage in use.
- *
- * @param state The backing store global state.
- * @return NSERROR_OK on success or NSERROR_NOMEM if the map storage
- * could not be allocated.
- */
-static nserror
-build_entrymap(struct store_state *state)
-{
- unsigned int eloop;
-
- NSLOG(netsurf, INFO, "Allocating %"PRIsizet" bytes for max of %d buckets",
- (1 << state->ident_bits) * sizeof(entry_index_t),
- 1 << state->ident_bits);
-
- state->addrmap = calloc(1 << state->ident_bits, sizeof(entry_index_t));
- if (state->addrmap == NULL) {
- return NSERROR_NOMEM;
- }
-
- state->total_alloc = 0;
-
- for (eloop = 1; eloop < state->last_entry; eloop++) {
-
- NSLOG(llcache, DEEPDEBUG,
- "entry:%d ident:0x%08x used:%d",
- eloop,
- BS_ADDRESS(state->entries[eloop].ident, state),
- state->entries[eloop].use_count);
-
- /* update the address map to point at the entry */
- BS_ENTRY_INDEX(state->entries[eloop].ident, state) = eloop;
-
- /* account for the storage space */
- state->total_alloc += state->entries[eloop].elem[ENTRY_ELEM_DATA].size;
- state->total_alloc += state->entries[eloop].elem[ENTRY_ELEM_META].size;
- /* ensure entry does not have any allocation state */
- state->entries[eloop].elem[ENTRY_ELEM_DATA].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
- state->entries[eloop].elem[ENTRY_ELEM_META].flags &= ~(ENTRY_ELEM_FLAG_HEAP | ENTRY_ELEM_FLAG_MMAP);
- }
-
- return NSERROR_OK;
-}
/**
* Unlink entries file
@@ -1206,46 +1135,74 @@ unlink_entries(struct store_state *state)
static nserror
read_entries(struct store_state *state)
{
- int fd;
- ssize_t rd;
- size_t entries_size;
char *fname = NULL;
+ char *url;
+ nsurl *nsurl;
nserror ret;
+ size_t read_entries = 0;
+ struct store_entry *ent;
+ int fd;
ret = netsurf_mkpath(&fname, NULL, 2, state->path, ENTRIES_FNAME);
if (ret != NSERROR_OK) {
return ret;
}
- entries_size = (1 << state->entry_bits) * sizeof(struct store_entry);
-
- NSLOG(netsurf, INFO,
- "Allocating %"PRIsizet" bytes for max of %d entries of %"PRIsizet" length elements %"PRIsizet" length",
- entries_size,
- 1 << state->entry_bits,
- sizeof(struct store_entry),
- sizeof(struct store_entry_element));
-
- state->entries = calloc(1, entries_size);
+ state->entries = hashmap_create(&entries_hashmap_parameters);
if (state->entries == NULL) {
free(fname);
return NSERROR_NOMEM;
}
fd = open(fname, O_RDWR);
- free(fname);
if (fd != -1) {
- rd = read(fd, state->entries, entries_size);
- close(fd);
- if (rd > 0) {
- state->last_entry = rd / sizeof(struct store_entry);
- NSLOG(netsurf, INFO, "Read %d entries",
- state->last_entry);
+ uint32_t urllen;
+ while (read(fd, &urllen, sizeof(urllen)) == sizeof(urllen)) {
+ url = calloc(1, urllen+1);
+ if (read(fd, url, urllen) != urllen) {
+ free(url);
+ close(fd);
+ free(fname);
+ return NSERROR_INIT_FAILED;
+ }
+ ret = nsurl_create(url, &nsurl);
+ if (ret != NSERROR_OK) {
+ free(url);
+ close(fd);
+ free(fname);
+ return ret;
+ }
+ free(url);
+ /* We have to be careful here about nsurl refs */
+ ent = hashmap_insert(state->entries, nsurl);
+ if (ent == NULL) {
+ nsurl_unref(nsurl);
+ close(fd);
+ free(fname);
+ return NSERROR_NOMEM;
+ }
+ /* At this point, ent actually owns a ref of nsurl */
+ if (read(fd, ent, sizeof(*ent)) != sizeof(*ent)) {
+ /* The read failed, so reset the ptr */
+ ent->url = nsurl; /* It already had a ref */
+ nsurl_unref(nsurl);
+ close(fd);
+ free(fname);
+ return NSERROR_INIT_FAILED;
+ }
+ ent->url = nsurl; /* It already owns a ref */
+ nsurl_unref(nsurl);
+ NSLOG(netsurf, DEBUG, "Successfully read entry for %s", nsurl_access(ent->url));
+ read_entries++;
+ state->total_alloc += ent->elem[ENTRY_ELEM_DATA].size;
+ state->total_alloc += ent->elem[ENTRY_ELEM_META].size;
}
- } else {
- /* could rebuild entries from fs */
- state->last_entry = 1;
+ close(fd);
}
+
+ NSLOG(netsurf, INFO, "Read %"PRIsizet" entries from cache", read_entries);
+
+ free(fname);
return NSERROR_OK;
}
@@ -1283,7 +1240,7 @@ read_blocks(struct store_state *state)
&state->blocks[elem_idx][bfidx].use_map[0],
BLOCK_USE_MAP_SIZE);
if (rd <= 0) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"reading block file %d use index on file number %d failed",
elem_idx,
bfidx);
@@ -1382,9 +1339,6 @@ write_control(struct store_state *state)
}
fprintf(fcontrol, "%u%c", CONTROL_VERSION, 0);
- fprintf(fcontrol, "%u%c", state->entry_bits, 0);
- fprintf(fcontrol, "%u%c", state->ident_bits, 0);
- fprintf(fcontrol, "%u%c", state->last_entry, 0);
fclose(fcontrol);
@@ -1404,8 +1358,6 @@ read_control(struct store_state *state)
nserror ret;
FILE *fcontrol;
unsigned int ctrlversion;
- unsigned int addrbits;
- unsigned int entrybits;
char *fname = NULL;
ret = netsurf_mkpath(&fname, NULL, 2, state->path, "control");
@@ -1443,27 +1395,8 @@ read_control(struct store_state *state)
goto control_error;
}
- /* second line is log2 max number of entries */
- if (fscanf(fcontrol, "%u", &entrybits) != 1) {
- goto control_error;
- }
- if (fgetc(fcontrol) != 0) {
- goto control_error;
- }
-
- /* second line is log2 size of address hash */
- if (fscanf(fcontrol, "%u", &addrbits) != 1) {
- goto control_error;
- }
- if (fgetc(fcontrol) != 0) {
- goto control_error;
- }
-
fclose(fcontrol);
- state->entry_bits = entrybits;
- state->ident_bits = addrbits;
-
return NSERROR_OK;
control_error: /* problem with the control file */
@@ -1515,22 +1448,10 @@ initialise(const struct llcache_store_parameters *parameters)
newstate->limit = parameters->limit;
newstate->hysteresis = parameters->hysteresis;
- if (parameters->address_size == 0) {
- newstate->ident_bits = DEFAULT_IDENT_SIZE;
- } else {
- newstate->ident_bits = parameters->address_size;
- }
-
- if (parameters->entry_size == 0) {
- newstate->entry_bits = DEFAULT_ENTRY_SIZE;
- } else {
- newstate->entry_bits = parameters->entry_size;
- }
-
/* read store control and create new if required */
ret = read_control(newstate);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "read control failed %s",
+ NSLOG(netsurf, ERROR, "read control failed %s",
messages_get_errorcode(ret));
ret = write_control(newstate);
if (ret == NSERROR_OK) {
@@ -1545,13 +1466,6 @@ initialise(const struct llcache_store_parameters *parameters)
return ret;
}
- /* ensure the maximum number of entries can be represented in
- * the type available to store it.
- */
- if (newstate->entry_bits > (8 * sizeof(entry_index_t))) {
- newstate->entry_bits = (8 * sizeof(entry_index_t));
- }
-
/* read filesystem entries */
ret = read_entries(newstate);
if (ret != NSERROR_OK) {
@@ -1561,21 +1475,11 @@ initialise(const struct llcache_store_parameters *parameters)
return ret;
}
- /* build entry hash map */
- ret = build_entrymap(newstate);
- if (ret != NSERROR_OK) {
- /* that obviously went well */
- free(newstate->entries);
- free(newstate->path);
- free(newstate);
- return ret;
- }
-
+ /* read blocks */
ret = read_blocks(newstate);
if (ret != NSERROR_OK) {
/* oh dear */
- free(newstate->addrmap);
- free(newstate->entries);
+ hashmap_destroy(newstate->entries);
free(newstate->path);
free(newstate);
return ret;
@@ -1586,12 +1490,10 @@ initialise(const struct llcache_store_parameters *parameters)
NSLOG(netsurf, INFO, "FS backing store init successful");
NSLOG(netsurf, INFO,
- "path:%s limit:%"PRIsizet" hyst:%"PRIsizet" addr:%d entries:%d",
+ "path:%s limit:%"PRIsizet" hyst:%"PRIsizet,
newstate->path,
newstate->limit,
- newstate->hysteresis,
- newstate->ident_bits,
- newstate->entry_bits);
+ newstate->hysteresis);
NSLOG(netsurf, INFO, "Using %"PRIu64"/%"PRIsizet,
newstate->total_alloc, newstate->limit);
@@ -1614,7 +1516,7 @@ finalise(void)
unsigned int op_count;
if (storestate != NULL) {
- guit->misc->schedule(-1, control_maintinance, storestate);
+ guit->misc->schedule(-1, control_maintenance, storestate);
write_entries(storestate);
write_blocks(storestate);
@@ -1643,8 +1545,7 @@ finalise(void)
0);
}
- free(storestate->addrmap);
- free(storestate->entries);
+ hashmap_destroy(storestate->entries);
free(storestate->path);
free(storestate);
storestate = NULL;
@@ -1676,7 +1577,7 @@ static nserror store_write_block(struct store_state *state,
state->blocks[elem_idx][bf].fd = store_open(state, bf,
elem_idx + ENTRY_ELEM_COUNT, O_CREAT | O_RDWR);
if (state->blocks[elem_idx][bf].fd == -1) {
- NSLOG(netsurf, INFO, "Open failed errno %d", errno);
+ NSLOG(netsurf, ERROR, "Open failed errno %d", errno);
return NSERROR_SAVE_FAILED;
}
@@ -1691,7 +1592,7 @@ static nserror store_write_block(struct store_state *state,
bse->elem[elem_idx].size,
offst);
if (wr != (ssize_t)bse->elem[elem_idx].size) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Write failed %"PRIssizet" of %d bytes from %p at 0x%jx block %d errno %d",
wr,
bse->elem[elem_idx].size,
@@ -1726,10 +1627,10 @@ static nserror store_write_file(struct store_state *state,
int fd;
int err;
- fd = store_open(state, bse->ident, elem_idx, O_CREAT | O_WRONLY);
+ fd = store_open(state, nsurl_hash(bse->url), elem_idx, O_CREAT | O_WRONLY);
if (fd < 0) {
perror("");
- NSLOG(netsurf, INFO, "Open failed %d errno %d", fd, errno);
+ NSLOG(netsurf, ERROR, "Open failed %d errno %d", fd, errno);
return NSERROR_SAVE_FAILED;
}
@@ -1738,7 +1639,7 @@ static nserror store_write_file(struct store_state *state,
close(fd);
if (wr != (ssize_t)bse->elem[elem_idx].size) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Write failed %"PRIssizet" of %d bytes from %p errno %d",
wr,
bse->elem[elem_idx].size,
@@ -1749,7 +1650,7 @@ static nserror store_write_file(struct store_state *state,
return NSERROR_SAVE_FAILED;
}
- NSLOG(netsurf, INFO, "Wrote %"PRIssizet" bytes from %p", wr,
+ NSLOG(netsurf, VERBOSE, "Wrote %"PRIssizet" bytes from %p", wr,
bse->elem[elem_idx].data);
return NSERROR_OK;
@@ -1791,7 +1692,7 @@ store(nsurl *url,
/* set the store entry up */
ret = set_store_entry(storestate, url, elem_idx, data, datalen, &bse);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "store entry setting failed");
+ NSLOG(netsurf, ERROR, "store entry setting failed");
return ret;
}
@@ -1814,7 +1715,7 @@ static nserror entry_release_alloc(struct store_entry_element *elem)
if ((elem->flags & ENTRY_ELEM_FLAG_HEAP) != 0) {
elem->ref--;
if (elem->ref == 0) {
- NSLOG(netsurf, INFO, "freeing %p", elem->data);
+ NSLOG(netsurf, DEEPDEBUG, "freeing %p", elem->data);
free(elem->data);
elem->flags &= ~ENTRY_ELEM_FLAG_HEAP;
}
@@ -1846,7 +1747,7 @@ static nserror store_read_block(struct store_state *state,
state->blocks[elem_idx][bf].fd = store_open(state, bf,
elem_idx + ENTRY_ELEM_COUNT, O_CREAT | O_RDWR);
if (state->blocks[elem_idx][bf].fd == -1) {
- NSLOG(netsurf, INFO, "Open failed errno %d", errno);
+ NSLOG(netsurf, ERROR, "Open failed errno %d", errno);
return NSERROR_SAVE_FAILED;
}
@@ -1861,7 +1762,7 @@ static nserror store_read_block(struct store_state *state,
bse->elem[elem_idx].size,
offst);
if (rd != (ssize_t)bse->elem[elem_idx].size) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Failed reading %"PRIssizet" of %d bytes into %p from 0x%jx block %d errno %d",
rd,
bse->elem[elem_idx].size,
@@ -1872,7 +1773,7 @@ static nserror store_read_block(struct store_state *state,
return NSERROR_SAVE_FAILED;
}
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEEPDEBUG,
"Read %"PRIssizet" bytes into %p from 0x%jx block %d", rd,
bse->elem[elem_idx].data, (uintmax_t)offst,
bse->elem[elem_idx].block);
@@ -1898,9 +1799,9 @@ static nserror store_read_file(struct store_state *state,
size_t tot = 0; /* total size */
/* separate file in backing store */
- fd = store_open(storestate, bse->ident, elem_idx, O_RDONLY);
+ fd = store_open(storestate, nsurl_hash(bse->url), elem_idx, O_RDONLY);
if (fd < 0) {
- NSLOG(netsurf, INFO, "Open failed %d errno %d", fd, errno);
+ NSLOG(netsurf, ERROR, "Open failed %d errno %d", fd, errno);
/** @todo should this invalidate the entry? */
return NSERROR_NOT_FOUND;
}
@@ -1910,7 +1811,7 @@ static nserror store_read_file(struct store_state *state,
bse->elem[elem_idx].data + tot,
bse->elem[elem_idx].size - tot);
if (rd <= 0) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"read error returned %"PRIssizet" errno %d",
rd,
errno);
@@ -1922,7 +1823,7 @@ static nserror store_read_file(struct store_state *state,
close(fd);
- NSLOG(netsurf, INFO, "Read %"PRIsizet" bytes into %p", tot,
+ NSLOG(netsurf, DEEPDEBUG, "Read %"PRIsizet" bytes into %p", tot,
bse->elem[elem_idx].data);
return ret;
@@ -1956,13 +1857,13 @@ fetch(nsurl *url,
/* fetch store entry */
ret = get_store_entry(storestate, url, &bse);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "entry not found");
+ NSLOG(netsurf, DEBUG, "Entry for %s not found", nsurl_access(url));
storestate->miss_count++;
return ret;
}
storestate->hit_count++;
- NSLOG(netsurf, INFO, "retrieving cache data for url:%s",
+ NSLOG(netsurf, DEBUG, "retrieving cache data for url:%s",
nsurl_access(url));
/* calculate the entry element index */
@@ -1978,7 +1879,7 @@ fetch(nsurl *url,
/* use the existing allocation and bump the ref count. */
elem->ref++;
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, DEEPDEBUG,
"Using existing entry (%p) allocation %p refs:%d", bse,
elem->data, elem->ref);
@@ -1986,11 +1887,11 @@ fetch(nsurl *url,
/* allocate from the heap */
elem->data = malloc(elem->size);
if (elem->data == NULL) {
- NSLOG(netsurf, INFO,
+ NSLOG(netsurf, ERROR,
"Failed to create new heap allocation");
return NSERROR_NOMEM;
}
- NSLOG(netsurf, INFO, "Created new heap allocation %p",
+ NSLOG(netsurf, DEEPDEBUG, "Created new heap allocation %p",
elem->data);
/* mark the entry as having a valid heap allocation */
@@ -2040,7 +1941,7 @@ static nserror release(nsurl *url, enum backing_store_flags bsflags)
ret = get_store_entry(storestate, url, &bse);
if (ret != NSERROR_OK) {
- NSLOG(netsurf, INFO, "entry not found");
+ NSLOG(netsurf, WARNING, "entry not found");
return ret;
}
diff --git a/content/llcache.c b/content/llcache.c
index 3a75bf9..9c74fbd 100644
--- a/content/llcache.c
+++ b/content/llcache.c
@@ -3769,6 +3769,11 @@ void llcache_finalise(void)
llcache_object *object, *next;
uint64_t total_bandwidth = 0; /* total bandwidth */
+ /* Attempt to persist anything we have left lying around */
+ llcache_persist(NULL);
+ /* Now clear the persistence callback */
+ guit->misc->schedule(-1, llcache_persist, NULL);
+
/* Clean uncached objects */
for (object = llcache->uncached_objects; object != NULL; object = next) {
llcache_object_user *user, *next_user;
diff --git a/content/llcache.h b/content/llcache.h
index 514272f..f9a3016 100644
--- a/content/llcache.h
+++ b/content/llcache.h
@@ -121,43 +121,6 @@ struct llcache_store_parameters {
size_t limit; /**< The backing store upper bound target size */
size_t hysteresis; /**< The hysteresis around the target size */
-
- /** log2 of the default maximum number of entries the cache
- * can track.
- *
- * If unset this defaults to 16 (65536 entries) The cache
- * control file takes precedence so cache data remains
- * portable between builds with differing defaults.
- */
- unsigned int entry_size;
-
- /** log2 of the default number of entries in the mapping between
- * the url and cache entries.
- *
- * @note This is exposing an internal implementation detail of
- * the filesystem based default backing store implementation.
- * However it is likely any backing store implementation will
- * need some way to map url to cache entries so it is a
- * generally useful configuration value.
- *
- * Too small a value will cause unecessary collisions and
- * cache misses and larger values cause proportionaly larger
- * amounts of memory to be used.
- *
- * The "birthday paradox" means that the hash will experience
- * a collision in every 2^(address_size/2) urls the cache
- * stores.
- *
- * A value of 20 means one object stored in every 1024 will
- * cause a collion and a cache miss while using two megabytes
- * of storage.
- *
- * If unset this defaults to 20 (1048576 entries using two
- * megabytes) The cache control file takes precedence so cache
- * data remains portable between builds with differing
- * defaults.
- */
- unsigned int address_size;
};
/**
diff --git a/test/hashmap.c b/test/hashmap.c
index 87db6c1..ed951e9 100644
--- a/test/hashmap.c
+++ b/test/hashmap.c
@@ -151,6 +151,20 @@ static hashmap_parameters_t test_params = {
.value_destroy = value_destroy,
};
+/* Iteration helpers */
+
+static size_t iteration_counter = 0;
+static size_t iteration_stop = 0;
+static char iteration_ctx = 0;
+
+static bool
+hashmap_test_iterator_cb(void *key, void *value, void *ctx)
+{
+ ck_assert(ctx == &iteration_ctx);
+ iteration_counter++;
+ return iteration_counter == iteration_stop;
+}
+
/* Fixtures for basic tests */
static hashmap_t *test_hashmap = NULL;
@@ -183,7 +197,7 @@ basic_fixture_teardown(void)
START_TEST(empty_hashmap_create_destroy)
{
- /* Do nothing in here, all the checks are in the fixture */
+ ck_assert_int_eq(hashmap_count(test_hashmap), 0);
}
END_TEST
@@ -199,6 +213,7 @@ START_TEST(insert_works)
hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank);
ck_assert(value != NULL);
ck_assert(value->key == corestring_nsurl_about_blank);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 1);
}
END_TEST
@@ -215,9 +230,11 @@ START_TEST(insert_then_remove)
ck_assert(value->key == corestring_nsurl_about_blank);
ck_assert_int_eq(keys, 1);
ck_assert_int_eq(values, 1);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 1);
ck_assert(hashmap_remove(test_hashmap, corestring_nsurl_about_blank) == true);
ck_assert_int_eq(keys, 0);
ck_assert_int_eq(values, 0);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 0);
}
END_TEST
@@ -230,6 +247,35 @@ START_TEST(insert_then_lookup)
}
END_TEST
+START_TEST(iterate_empty)
+{
+ iteration_stop = iteration_counter = 0;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, 0);
+}
+END_TEST
+
+START_TEST(iterate_one)
+{
+ iteration_stop = iteration_counter = 0;
+ hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank);
+ ck_assert(value != NULL);
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, 1);
+}
+END_TEST
+
+START_TEST(iterate_one_and_stop)
+{
+ iteration_stop = 1;
+ iteration_counter = 0;
+ hashmap_test_value_t *value = hashmap_insert(test_hashmap, corestring_nsurl_about_blank);
+ ck_assert(value != NULL);
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == true);
+ ck_assert_int_eq(iteration_counter, 1);
+}
+END_TEST
+
static TCase *basic_api_case_create(void)
{
TCase *tc;
@@ -245,7 +291,11 @@ static TCase *basic_api_case_create(void)
tcase_add_test(tc, remove_not_present);
tcase_add_test(tc, insert_then_remove);
tcase_add_test(tc, insert_then_lookup);
-
+
+ tcase_add_test(tc, iterate_empty);
+ tcase_add_test(tc, iterate_one);
+ tcase_add_test(tc, iterate_one_and_stop);
+
return tc;
}
@@ -374,6 +424,59 @@ START_TEST(chain_add_all_twice_remove_all)
}
END_TEST
+START_TEST(chain_add_all_twice_remove_all_iterate)
+{
+ case_pair *chain_case;
+ size_t chain_count = 0;
+
+ for (chain_case = chain_pairs;
+ chain_case->url != NULL;
+ chain_case++) {
+ ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) == NULL);
+ ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL);
+ chain_count++;
+ }
+
+ iteration_counter = 0;
+ iteration_stop = 0;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, chain_count);
+
+ for (chain_case = chain_pairs;
+ chain_case->url != NULL;
+ chain_case++) {
+ ck_assert(hashmap_lookup(test_hashmap, chain_case->nsurl) != NULL);
+ ck_assert(hashmap_insert(test_hashmap, chain_case->nsurl) != NULL);
+ }
+
+ iteration_counter = 0;
+ iteration_stop = 0;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, chain_count);
+ ck_assert_int_eq(hashmap_count(test_hashmap), chain_count);
+
+ iteration_counter = 0;
+ iteration_stop = chain_count;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == true);
+ ck_assert_int_eq(iteration_counter, chain_count);
+
+ for (chain_case = chain_pairs;
+ chain_case->url != NULL;
+ chain_case++) {
+ ck_assert(hashmap_remove(test_hashmap, chain_case->nsurl) == true);
+ }
+
+ iteration_counter = 0;
+ iteration_stop = chain_count;
+ ck_assert(hashmap_iterate(test_hashmap, hashmap_test_iterator_cb, &iteration_ctx) == false);
+ ck_assert_int_eq(iteration_counter, 0);
+
+ ck_assert_int_eq(keys, 0);
+ ck_assert_int_eq(values, 0);
+ ck_assert_int_eq(hashmap_count(test_hashmap), 0);
+}
+END_TEST
+
#define CHAIN_TEST_MALLOC_COUNT_MAX 60
START_TEST(chain_add_all_remove_all_alloc)
@@ -431,6 +534,7 @@ static TCase *chain_case_create(void)
tcase_add_test(tc, chain_add_remove_all);
tcase_add_test(tc, chain_add_all_remove_all);
tcase_add_test(tc, chain_add_all_twice_remove_all);
+ tcase_add_test(tc, chain_add_all_twice_remove_all_iterate);
tcase_add_loop_test(tc, chain_add_all_remove_all_alloc, 0, CHAIN_TEST_MALLOC_COUNT_MAX + 1);
diff --git a/utils/Makefile b/utils/Makefile
index e0fbd8e..fa5f58d 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -6,6 +6,7 @@ S_UTILS := \
file.c \
filename.c \
filepath.c \
+ hashmap.c \
hashtable.c \
idna.c \
libdom.c \
diff --git a/utils/hashmap.c b/utils/hashmap.c
index 7ed1994..814dc55 100644
--- a/utils/hashmap.c
+++ b/utils/hashmap.c
@@ -55,6 +55,11 @@ struct hashmap_s {
* The number of buckets in this map
*/
uint32_t bucket_count;
+
+ /**
+ * The number of entries in this map
+ */
+ size_t entry_count;
};
/* Exported function, documented in hashmap.h */
@@ -65,14 +70,16 @@ hashmap_create(hashmap_parameters_t *params)
ret->params = params;
ret->bucket_count = DEFAULT_HASHMAP_BUCKETS;
+ ret->entry_count = 0;
ret->buckets = malloc(ret->bucket_count * sizeof(hashmap_entry_t *));
- memset(ret->buckets, 0, ret->bucket_count * sizeof(hashmap_entry_t *));
if (ret->buckets == NULL) {
free(ret);
return NULL;
}
-
+
+ memset(ret->buckets, 0, ret->bucket_count * sizeof(hashmap_entry_t *));
+
return ret;
}
@@ -85,11 +92,12 @@ hashmap_destroy(hashmap_t *hashmap)
for (bucket = 0; bucket < hashmap->bucket_count; bucket++) {
for (entry = hashmap->buckets[bucket];
- entry != NULL;
- entry = entry->next) {
+ entry != NULL;) {
+ hashmap_entry_t *next = entry->next;
hashmap->params->value_destroy(entry->value);
hashmap->params->key_destroy(entry->key);
free(entry);
+ entry = next;
}
}
@@ -175,7 +183,9 @@ hashmap_insert(hashmap_t *hashmap, void *key)
}
hashmap->buckets[bucket] = entry;
-
+
+ hashmap->entry_count++;
+
return entry->value;
err:
@@ -206,6 +216,7 @@ hashmap_remove(hashmap_t *hashmap, void *key)
}
*entry->prevptr = entry->next;
free(entry);
+ hashmap->entry_count--;
return true;
}
}
@@ -213,3 +224,29 @@ hashmap_remove(hashmap_t *hashmap, void *key)
return false;
}
+
+/* Exported function, documented in hashmap.h */
+bool
+hashmap_iterate(hashmap_t *hashmap, hashmap_iteration_cb_t cb, void *ctx)
+{
+ for (uint32_t bucket = 0;
+ bucket < hashmap->bucket_count;
+ bucket++) {
+ for (hashmap_entry_t *entry = hashmap->buckets[bucket];
+ entry != NULL;
+ entry = entry->next) {
+ /* If the callback returns true, we early-exit */
+ if (cb(entry->key, entry->value, ctx))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Exported function, documented in hashmap.h */
+size_t
+hashmap_count(hashmap_t *hashmap)
+{
+ return hashmap->entry_count;
+}
diff --git a/utils/hashmap.h b/utils/hashmap.h
index 4e1237a..3968fd3 100644
--- a/utils/hashmap.h
+++ b/utils/hashmap.h
@@ -32,6 +32,46 @@
typedef struct hashmap_s hashmap_t;
/**
+ * Key cloning function type
+ */
+typedef void* (*hashmap_key_clone_t)(void *);
+
+/**
+ * Key destructor function type
+ */
+typedef void (*hashmap_key_destroy_t)(void *);
+
+/**
+ * Key hashing function type
+ */
+typedef uint32_t (*hashmap_key_hash_t)(void *);
+
+/**
+ * Key comparison function type
+ */
+typedef bool (*hashmap_key_eq_t)(void *, void*);
+
+/**
+ * Value allocation function type
+ */
+typedef void* (*hashmap_value_alloc_t)(void *);
+
+/**
+ * Value destructor function type
+ */
+typedef void (*hashmap_value_destroy_t)(void *);
+
+/**
+ * Hashmap iteration callback function type.
+ *
+ * First parameter is the key, second is the value.
+ * The final parameter is the context pointer for the iteration.
+ *
+ * Return true to stop iterating early
+ */
+typedef bool (*hashmap_iteration_cb_t)(void *, void *, void *);
+
+/**
* Parameters for hashmaps
*/
typedef struct {
@@ -39,12 +79,12 @@ typedef struct {
* A function which when called will clone a key and give
* ownership of the returned object to the hashmap
*/
- void * (*key_clone)(void *key);
+ hashmap_key_clone_t key_clone;
/**
* A function which when given a key will return its hash.
*/
- uint32_t (*key_hash)(void *key);
+ hashmap_key_hash_t key_hash;
/**
* A function to compare two keys and return if they are equal.
@@ -52,22 +92,22 @@ typedef struct {
* as the function is a full equality model.
* (i.e. key1 == key2 => key2 == key1)
*/
- bool (*key_eq)(void *key1, void *key2);
+ hashmap_key_eq_t key_eq;
/**
* A function which when called will destroy a key object
*/
- void (*key_destroy)(void *key);
+ hashmap_key_destroy_t key_destroy;
/**
* A function which when called will allocate a value object
*/
- void * (*value_alloc)(void *key);
+ hashmap_value_alloc_t value_alloc;
/**
* A function which when called will destroy a value object
*/
- void (*value_destroy)(void *value);
+ hashmap_value_destroy_t value_destroy;
} hashmap_parameters_t;
@@ -133,5 +173,25 @@ void *hashmap_insert(hashmap_t *hashmap, void *key);
*/
bool hashmap_remove(hashmap_t *hashmap, void *key);
+/**
+ * Iterate the hashmap
+ *
+ * For each key/value pair in the hashmap, call the callback passing in
+ * the key and value. During iteration you MUST NOT mutate the hashmap.
+ *
+ * \param hashmap The hashmap to iterate
+ * \param cb The callback for each key,value pair
+ * \param ctx The callback context
+ * \return Whether or not we stopped iteration early
+ */
+bool hashmap_iterate(hashmap_t *hashmap, hashmap_iteration_cb_t cb, void *ctx);
+
+/**
+ * Get the number of entries in this map
+ *
+ * \param hashmap The hashmap to retrieve the entry count from
+ * \return The number of entries in the hashmap
+ */
+size_t hashmap_count(hashmap_t *hashmap);
#endif
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-477-gbe659af
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/be659af7e563527270a87...
...commit http://git.netsurf-browser.org/netsurf.git/commit/be659af7e563527270a8725...
...tree http://git.netsurf-browser.org/netsurf.git/tree/be659af7e563527270a8725ae...
The branch, master has been updated
via be659af7e563527270a8725ae42ecf7c25aecc7e (commit)
from 9062ae3c7073ea354e688755b407bb91ac0e8ade (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=be659af7e563527270a...
commit be659af7e563527270a8725ae42ecf7c25aecc7e
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Page info: Avoid anonymous union for AmigaOS3 and OpenBSD.
diff --git a/desktop/page-info.c b/desktop/page-info.c
index 51784c4..fba0d63 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -183,7 +183,7 @@ struct page_info_entry {
union {
struct page_info_text text;
struct page_info_item item;
- };
+ } u;
};
/**
@@ -195,32 +195,38 @@ struct page_info_entry pi__entries[PI_ENTRY__COUNT] = {
},
[PI_ENTRY_DOMAIN] = {
.type = PAGE_INFO_ENTRY_TYPE_TEXT,
- .text = {
- .style = &pi__domain,
+ .u = {
+ .text = {
+ .style = &pi__domain,
+ },
},
},
[PI_ENTRY_CERT] = {
.type = PAGE_INFO_ENTRY_TYPE_ITEM,
- .item = {
+ .u = {
.item = {
- .style = &pi__item,
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
},
- .detail = {
- .style = &pi__item_detail,
- },
- .hover_bg = &pi__hover,
},
},
[PI_ENTRY_COOKIES] = {
.type = PAGE_INFO_ENTRY_TYPE_ITEM,
- .item = {
+ .u = {
.item = {
- .style = &pi__item,
- },
- .detail = {
- .style = &pi__item_detail,
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
},
- .hover_bg = &pi__hover,
},
},
};
@@ -358,36 +364,37 @@ static nserror page_info__measure_text(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
err = page_info__measure_text_entry(
- &entry->text);
+ &entry->u.text);
if (err != NSERROR_OK) {
return err;
}
if (i == PI_ENTRY_DOMAIN) {
- entry->text.padding_bottom =
- entry->text.height * 3 / 2;
+ entry->u.text.padding_bottom =
+ entry->u.text.height * 3 / 2;
}
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
err = page_info__measure_text_entry(
- &entry->item.item);
+ &entry->u.item.item);
if (err != NSERROR_OK) {
return err;
}
err = page_info__measure_text_entry(
- &entry->item.detail);
+ &entry->u.item.detail);
if (err != NSERROR_OK) {
return err;
}
- padding = entry->item.item.height / 4;
- entry->item.padding_top = padding;
- entry->item.padding_bottom = padding;
+ padding = entry->u.item.item.height / 4;
+ entry->u.item.padding_top = padding;
+ entry->u.item.padding_bottom = padding;
break;
}
}
- pi->window_padding = pi->entries[PI_ENTRY_DOMAIN].item.item.height / 2;
+ pi->window_padding = pi->entries[PI_ENTRY_DOMAIN]
+ .u.item.item.height / 2;
return NSERROR_OK;
}
@@ -426,13 +433,13 @@ static nserror page_info__set_text(
assert(pi != NULL);
assert(pi->state < PAGE_STATE__COUNT);
- pi->entries[PI_ENTRY_HEADER].text.style = &pi__heading[pi->state];
- pi->entries[PI_ENTRY_HEADER].text.text = header[pi->state];
- pi->entries[PI_ENTRY_DOMAIN].text.text = (pi->domain) ?
+ pi->entries[PI_ENTRY_HEADER].u.text.style = &pi__heading[pi->state];
+ pi->entries[PI_ENTRY_HEADER].u.text.text = header[pi->state];
+ pi->entries[PI_ENTRY_DOMAIN].u.text.text = (pi->domain) ?
lwc_string_data(pi->domain) : "<No domain>";
- pi->entries[PI_ENTRY_CERT].item.item.text = "Certificate: ";
- pi->entries[PI_ENTRY_CERT].item.detail.text = certificate[pi->state];
+ pi->entries[PI_ENTRY_CERT].u.item.item.text = "Certificate: ";
+ pi->entries[PI_ENTRY_CERT].u.item.detail.text = certificate[pi->state];
printed = snprintf(pi->cookie_text, sizeof(pi->cookie_text),
"(%u in use)", pi->cookies);
@@ -442,8 +449,8 @@ static nserror page_info__set_text(
} else if ((unsigned) printed >= sizeof(pi->cookie_text)) {
return NSERROR_NOSPACE;
}
- pi->entries[PI_ENTRY_COOKIES].item.item.text = "Cookies: ";
- pi->entries[PI_ENTRY_COOKIES].item.detail.text = pi->cookie_text;
+ pi->entries[PI_ENTRY_COOKIES].u.item.item.text = "Cookies: ";
+ pi->entries[PI_ENTRY_COOKIES].u.item.detail.text = pi->cookie_text;
return page_info__measure_text(pi);
}
@@ -487,23 +494,23 @@ static nserror page_info__layout(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
- cur_y += entry->text.height;
- if (max_x < entry->text.width) {
- max_x = entry->text.width;
+ cur_y += entry->u.text.height;
+ if (max_x < entry->u.text.width) {
+ max_x = entry->u.text.width;
}
- cur_y += entry->text.padding_bottom;
+ cur_y += entry->u.text.padding_bottom;
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
{
- int full_width = entry->item.item.width +
- entry->item.detail.width;
- cur_y += entry->item.padding_top;
- cur_y += entry->item.item.height;
+ int full_width = entry->u.item.item.width +
+ entry->u.item.detail.width;
+ cur_y += entry->u.item.padding_top;
+ cur_y += entry->u.item.item.height;
if (max_x < full_width) {
max_x = full_width;
}
- cur_y += entry->item.padding_bottom;
+ cur_y += entry->u.item.padding_bottom;
}
break;
}
@@ -623,43 +630,43 @@ nserror page_info_redraw(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
err = page_info__redraw_text_entry(
- &entry->text,
+ &entry->u.text,
cur_x, cur_y,
&new_ctx);
if (err != NSERROR_OK) {
goto cleanup;
}
- cur_y += entry->text.height;
- cur_y += entry->text.padding_bottom;
+ cur_y += entry->u.text.height;
+ cur_y += entry->u.text.padding_bottom;
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
- if (entry->item.hover) {
+ if (entry->u.item.hover) {
r.y0 = cur_y;
- r.y1 = cur_y + entry->item.padding_top +
- entry->item.item.height +
- entry->item.padding_bottom;
+ r.y1 = cur_y + entry->u.item.padding_top +
+ entry->u.item.item.height +
+ entry->u.item.padding_bottom;
new_ctx.plot->rectangle(&new_ctx,
&pi__hover, &r);
}
- cur_y += entry->item.padding_top;
+ cur_y += entry->u.item.padding_top;
err = page_info__redraw_text_entry(
- &entry->item.item,
+ &entry->u.item.item,
cur_x, cur_y,
&new_ctx);
if (err != NSERROR_OK) {
goto cleanup;
}
- cur_x += entry->item.item.width;
+ cur_x += entry->u.item.item.width;
err = page_info__redraw_text_entry(
- &entry->item.detail,
+ &entry->u.item.detail,
cur_x, cur_y,
&new_ctx);
if (err != NSERROR_OK) {
goto cleanup;
}
- cur_y += entry->item.item.height;
- cur_y += entry->item.padding_bottom;
+ cur_y += entry->u.item.item.height;
+ cur_y += entry->u.item.padding_bottom;
break;
}
}
@@ -728,14 +735,14 @@ nserror page_info_mouse_action(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
- cur_y += entry->text.height;
- cur_y += entry->text.padding_bottom;
+ cur_y += entry->u.text.height;
+ cur_y += entry->u.text.padding_bottom;
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
- height = entry->item.padding_top +
- entry->item.item.height +
- entry->item.padding_bottom;
+ height = entry->u.item.padding_top +
+ entry->u.item.item.height +
+ entry->u.item.padding_bottom;
if (y >= cur_y && y < cur_y + height) {
hovering = true;
@@ -745,7 +752,7 @@ nserror page_info_mouse_action(
return err;
}
}
- if (entry->item.hover != hovering) {
+ if (entry->u.item.hover != hovering) {
int w, h;
struct rect r = {
.x0 = 0,
@@ -758,7 +765,7 @@ nserror page_info_mouse_action(
pi->cw_t->invalidate(pi->cw_h, &r);
}
- entry->item.hover = hovering;
+ entry->u.item.hover = hovering;
cur_y += height;
break;
}
-----------------------------------------------------------------------
Summary of changes:
desktop/page-info.c | 127 +++++++++++++++++++++++++++------------------------
1 file changed, 67 insertions(+), 60 deletions(-)
diff --git a/desktop/page-info.c b/desktop/page-info.c
index 51784c4..fba0d63 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -183,7 +183,7 @@ struct page_info_entry {
union {
struct page_info_text text;
struct page_info_item item;
- };
+ } u;
};
/**
@@ -195,32 +195,38 @@ struct page_info_entry pi__entries[PI_ENTRY__COUNT] = {
},
[PI_ENTRY_DOMAIN] = {
.type = PAGE_INFO_ENTRY_TYPE_TEXT,
- .text = {
- .style = &pi__domain,
+ .u = {
+ .text = {
+ .style = &pi__domain,
+ },
},
},
[PI_ENTRY_CERT] = {
.type = PAGE_INFO_ENTRY_TYPE_ITEM,
- .item = {
+ .u = {
.item = {
- .style = &pi__item,
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
},
- .detail = {
- .style = &pi__item_detail,
- },
- .hover_bg = &pi__hover,
},
},
[PI_ENTRY_COOKIES] = {
.type = PAGE_INFO_ENTRY_TYPE_ITEM,
- .item = {
+ .u = {
.item = {
- .style = &pi__item,
- },
- .detail = {
- .style = &pi__item_detail,
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
},
- .hover_bg = &pi__hover,
},
},
};
@@ -358,36 +364,37 @@ static nserror page_info__measure_text(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
err = page_info__measure_text_entry(
- &entry->text);
+ &entry->u.text);
if (err != NSERROR_OK) {
return err;
}
if (i == PI_ENTRY_DOMAIN) {
- entry->text.padding_bottom =
- entry->text.height * 3 / 2;
+ entry->u.text.padding_bottom =
+ entry->u.text.height * 3 / 2;
}
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
err = page_info__measure_text_entry(
- &entry->item.item);
+ &entry->u.item.item);
if (err != NSERROR_OK) {
return err;
}
err = page_info__measure_text_entry(
- &entry->item.detail);
+ &entry->u.item.detail);
if (err != NSERROR_OK) {
return err;
}
- padding = entry->item.item.height / 4;
- entry->item.padding_top = padding;
- entry->item.padding_bottom = padding;
+ padding = entry->u.item.item.height / 4;
+ entry->u.item.padding_top = padding;
+ entry->u.item.padding_bottom = padding;
break;
}
}
- pi->window_padding = pi->entries[PI_ENTRY_DOMAIN].item.item.height / 2;
+ pi->window_padding = pi->entries[PI_ENTRY_DOMAIN]
+ .u.item.item.height / 2;
return NSERROR_OK;
}
@@ -426,13 +433,13 @@ static nserror page_info__set_text(
assert(pi != NULL);
assert(pi->state < PAGE_STATE__COUNT);
- pi->entries[PI_ENTRY_HEADER].text.style = &pi__heading[pi->state];
- pi->entries[PI_ENTRY_HEADER].text.text = header[pi->state];
- pi->entries[PI_ENTRY_DOMAIN].text.text = (pi->domain) ?
+ pi->entries[PI_ENTRY_HEADER].u.text.style = &pi__heading[pi->state];
+ pi->entries[PI_ENTRY_HEADER].u.text.text = header[pi->state];
+ pi->entries[PI_ENTRY_DOMAIN].u.text.text = (pi->domain) ?
lwc_string_data(pi->domain) : "<No domain>";
- pi->entries[PI_ENTRY_CERT].item.item.text = "Certificate: ";
- pi->entries[PI_ENTRY_CERT].item.detail.text = certificate[pi->state];
+ pi->entries[PI_ENTRY_CERT].u.item.item.text = "Certificate: ";
+ pi->entries[PI_ENTRY_CERT].u.item.detail.text = certificate[pi->state];
printed = snprintf(pi->cookie_text, sizeof(pi->cookie_text),
"(%u in use)", pi->cookies);
@@ -442,8 +449,8 @@ static nserror page_info__set_text(
} else if ((unsigned) printed >= sizeof(pi->cookie_text)) {
return NSERROR_NOSPACE;
}
- pi->entries[PI_ENTRY_COOKIES].item.item.text = "Cookies: ";
- pi->entries[PI_ENTRY_COOKIES].item.detail.text = pi->cookie_text;
+ pi->entries[PI_ENTRY_COOKIES].u.item.item.text = "Cookies: ";
+ pi->entries[PI_ENTRY_COOKIES].u.item.detail.text = pi->cookie_text;
return page_info__measure_text(pi);
}
@@ -487,23 +494,23 @@ static nserror page_info__layout(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
- cur_y += entry->text.height;
- if (max_x < entry->text.width) {
- max_x = entry->text.width;
+ cur_y += entry->u.text.height;
+ if (max_x < entry->u.text.width) {
+ max_x = entry->u.text.width;
}
- cur_y += entry->text.padding_bottom;
+ cur_y += entry->u.text.padding_bottom;
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
{
- int full_width = entry->item.item.width +
- entry->item.detail.width;
- cur_y += entry->item.padding_top;
- cur_y += entry->item.item.height;
+ int full_width = entry->u.item.item.width +
+ entry->u.item.detail.width;
+ cur_y += entry->u.item.padding_top;
+ cur_y += entry->u.item.item.height;
if (max_x < full_width) {
max_x = full_width;
}
- cur_y += entry->item.padding_bottom;
+ cur_y += entry->u.item.padding_bottom;
}
break;
}
@@ -623,43 +630,43 @@ nserror page_info_redraw(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
err = page_info__redraw_text_entry(
- &entry->text,
+ &entry->u.text,
cur_x, cur_y,
&new_ctx);
if (err != NSERROR_OK) {
goto cleanup;
}
- cur_y += entry->text.height;
- cur_y += entry->text.padding_bottom;
+ cur_y += entry->u.text.height;
+ cur_y += entry->u.text.padding_bottom;
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
- if (entry->item.hover) {
+ if (entry->u.item.hover) {
r.y0 = cur_y;
- r.y1 = cur_y + entry->item.padding_top +
- entry->item.item.height +
- entry->item.padding_bottom;
+ r.y1 = cur_y + entry->u.item.padding_top +
+ entry->u.item.item.height +
+ entry->u.item.padding_bottom;
new_ctx.plot->rectangle(&new_ctx,
&pi__hover, &r);
}
- cur_y += entry->item.padding_top;
+ cur_y += entry->u.item.padding_top;
err = page_info__redraw_text_entry(
- &entry->item.item,
+ &entry->u.item.item,
cur_x, cur_y,
&new_ctx);
if (err != NSERROR_OK) {
goto cleanup;
}
- cur_x += entry->item.item.width;
+ cur_x += entry->u.item.item.width;
err = page_info__redraw_text_entry(
- &entry->item.detail,
+ &entry->u.item.detail,
cur_x, cur_y,
&new_ctx);
if (err != NSERROR_OK) {
goto cleanup;
}
- cur_y += entry->item.item.height;
- cur_y += entry->item.padding_bottom;
+ cur_y += entry->u.item.item.height;
+ cur_y += entry->u.item.padding_bottom;
break;
}
}
@@ -728,14 +735,14 @@ nserror page_info_mouse_action(
switch (entry->type) {
case PAGE_INFO_ENTRY_TYPE_TEXT:
- cur_y += entry->text.height;
- cur_y += entry->text.padding_bottom;
+ cur_y += entry->u.text.height;
+ cur_y += entry->u.text.padding_bottom;
break;
case PAGE_INFO_ENTRY_TYPE_ITEM:
- height = entry->item.padding_top +
- entry->item.item.height +
- entry->item.padding_bottom;
+ height = entry->u.item.padding_top +
+ entry->u.item.item.height +
+ entry->u.item.padding_bottom;
if (y >= cur_y && y < cur_y + height) {
hovering = true;
@@ -745,7 +752,7 @@ nserror page_info_mouse_action(
return err;
}
}
- if (entry->item.hover != hovering) {
+ if (entry->u.item.hover != hovering) {
int w, h;
struct rect r = {
.x0 = 0,
@@ -758,7 +765,7 @@ nserror page_info_mouse_action(
pi->cw_t->invalidate(pi->cw_h, &r);
}
- entry->item.hover = hovering;
+ entry->u.item.hover = hovering;
cur_y += height;
break;
}
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-476-g9062ae3
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/9062ae3c7073ea354e688...
...commit http://git.netsurf-browser.org/netsurf.git/commit/9062ae3c7073ea354e68875...
...tree http://git.netsurf-browser.org/netsurf.git/tree/9062ae3c7073ea354e688755b...
The branch, master has been updated
via 9062ae3c7073ea354e688755b407bb91ac0e8ade (commit)
via 66c069816a33158b013d138d3d20228edcf6850d (commit)
via 0e4f09db0fd92c6fc07c7e26c53d78124184b937 (commit)
via 7a3f86494049fcfddaf00331d8bdbc746b4bbecb (commit)
via 1149a1304d56a0fe758eba5686b156f5006f16f8 (commit)
via 4b2697c989e6bb33f60ca2b53ac1dfefaa08fc04 (commit)
via c32e1bb340d2d40d0a5b61e37e6fd55f84953633 (commit)
via 3940918b6830aa8273d0860d5d1d49706f969d3e (commit)
via 9198c9958afedc8104858979ec949c72933cbf91 (commit)
via 8da6252f5024aa1de3d4d9018d2ff32f96d22cd8 (commit)
via a39b651620272d240c40a9ff056eb50a758350f7 (commit)
via d23a7b4c8ffda7448bdb05591655954655a3cfe0 (commit)
via 475d397b8c3aa5ca672b7ec984a41302449b7727 (commit)
via 6783deba4e209461fd8105b9b46dbcacefed6b6f (commit)
from 8a834918e30cd1924b2a3a4840a94445d55b5965 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=9062ae3c7073ea354e6...
commit 9062ae3c7073ea354e688755b407bb91ac0e8ade
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
GTK: Page info: Change crtvrfy to pi throughout.
diff --git a/frontends/gtk/page_info.c b/frontends/gtk/page_info.c
index 1401fdc..2892788 100644
--- a/frontends/gtk/page_info.c
+++ b/frontends/gtk/page_info.c
@@ -41,7 +41,7 @@
/**
* GTK certificate viewing window context
*/
-struct nsgtk_crtvrfy_window {
+struct nsgtk_pi_window {
/** GTK core window context */
struct nsgtk_corewindow core;
/** GTK builder for window */
@@ -56,46 +56,46 @@ struct nsgtk_crtvrfy_window {
/**
* destroy a previously created certificate view
*/
-static nserror nsgtk_crtvrfy_destroy(struct nsgtk_crtvrfy_window *crtvrfy_win)
+static nserror nsgtk_pi_destroy(struct nsgtk_pi_window *pi_win)
{
nserror res;
- res = sslcert_viewer_fini(crtvrfy_win->ssl_data);
+ res = sslcert_viewer_fini(pi_win->ssl_data);
if (res == NSERROR_OK) {
- res = nsgtk_corewindow_fini(&crtvrfy_win->core);
- gtk_widget_destroy(GTK_WIDGET(crtvrfy_win->dlg));
- g_object_unref(G_OBJECT(crtvrfy_win->builder));
- free(crtvrfy_win);
+ res = nsgtk_corewindow_fini(&pi_win->core);
+ gtk_widget_destroy(GTK_WIDGET(pi_win->dlg));
+ g_object_unref(G_OBJECT(pi_win->builder));
+ free(pi_win);
}
return res;
}
static void
-nsgtk_crtvrfy_accept(GtkButton *w, gpointer data)
+nsgtk_pi_accept(GtkButton *w, gpointer data)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)data;
+ struct nsgtk_pi_window *pi_win;
+ pi_win = (struct nsgtk_pi_window *)data;
- sslcert_viewer_accept(crtvrfy_win->ssl_data);
+ sslcert_viewer_accept(pi_win->ssl_data);
- nsgtk_crtvrfy_destroy(crtvrfy_win);
+ nsgtk_pi_destroy(pi_win);
}
static void
-nsgtk_crtvrfy_reject(GtkWidget *w, gpointer data)
+nsgtk_pi_reject(GtkWidget *w, gpointer data)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)data;
+ struct nsgtk_pi_window *pi_win;
+ pi_win = (struct nsgtk_pi_window *)data;
- sslcert_viewer_reject(crtvrfy_win->ssl_data);
+ sslcert_viewer_reject(pi_win->ssl_data);
- nsgtk_crtvrfy_destroy(crtvrfy_win);
+ nsgtk_pi_destroy(pi_win);
}
static gboolean
-nsgtk_crtvrfy_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
+nsgtk_pi_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
{
- nsgtk_crtvrfy_reject(w, data);
+ nsgtk_pi_reject(w, data);
return FALSE;
}
@@ -109,15 +109,15 @@ nsgtk_crtvrfy_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
* \return NSERROR_OK on success otherwise appropriate error code
*/
static nserror
-nsgtk_crtvrfy_mouse(struct nsgtk_corewindow *nsgtk_cw,
+nsgtk_pi_mouse(struct nsgtk_corewindow *nsgtk_cw,
browser_mouse_state mouse_state,
int x, int y)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
+ struct nsgtk_pi_window *pi_win;
/* technically degenerate container of */
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)nsgtk_cw;
+ pi_win = (struct nsgtk_pi_window *)nsgtk_cw;
- sslcert_viewer_mouse_action(crtvrfy_win->ssl_data, mouse_state, x, y);
+ sslcert_viewer_mouse_action(pi_win->ssl_data, mouse_state, x, y);
return NSERROR_OK;
}
@@ -130,14 +130,14 @@ nsgtk_crtvrfy_mouse(struct nsgtk_corewindow *nsgtk_cw,
* \return NSERROR_OK on success otherwise appropriate error code
*/
static nserror
-nsgtk_crtvrfy_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
+nsgtk_pi_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
+ struct nsgtk_pi_window *pi_win;
/* technically degenerate container of */
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)nsgtk_cw;
+ pi_win = (struct nsgtk_pi_window *)nsgtk_cw;
- if (sslcert_viewer_keypress(crtvrfy_win->ssl_data, nskey)) {
+ if (sslcert_viewer_keypress(pi_win->ssl_data, nskey)) {
return NSERROR_OK;
}
return NSERROR_NOT_IMPLEMENTED;
@@ -151,19 +151,19 @@ nsgtk_crtvrfy_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
* \return NSERROR_OK on success otherwise appropriate error code
*/
static nserror
-nsgtk_crtvrfy_draw(struct nsgtk_corewindow *nsgtk_cw, struct rect *r)
+nsgtk_pi_draw(struct nsgtk_corewindow *nsgtk_cw, struct rect *r)
{
struct redraw_context ctx = {
.interactive = true,
.background_images = true,
.plot = &nsgtk_plotters
};
- struct nsgtk_crtvrfy_window *crtvrfy_win;
+ struct nsgtk_pi_window *pi_win;
/* technically degenerate container of */
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)nsgtk_cw;
+ pi_win = (struct nsgtk_pi_window *)nsgtk_cw;
- sslcert_viewer_redraw(crtvrfy_win->ssl_data, 0, 0, r, &ctx);
+ sslcert_viewer_redraw(pi_win->ssl_data, 0, 0, r, &ctx);
return NSERROR_OK;
}
@@ -176,7 +176,7 @@ static nserror dummy_cb(bool proceed, void *pw)
/* exported interface documented in gtk/page_info.h */
nserror nsgtk_page_info(struct browser_window *bw)
{
- struct nsgtk_crtvrfy_window *ncwin;
+ struct nsgtk_pi_window *ncwin;
nserror res;
struct cert_chain *chain;
@@ -189,7 +189,7 @@ nserror nsgtk_page_info(struct browser_window *bw)
}
url = browser_window_access_url(bw);
- ncwin = malloc(sizeof(struct nsgtk_crtvrfy_window));
+ ncwin = malloc(sizeof(struct nsgtk_pi_window));
if (ncwin == NULL) {
return NSERROR_NOMEM;
}
@@ -219,27 +219,27 @@ nserror nsgtk_page_info(struct browser_window *bw)
/* make the delete event call our destructor */
g_signal_connect(G_OBJECT(ncwin->dlg),
"delete_event",
- G_CALLBACK(nsgtk_crtvrfy_delete_event),
+ G_CALLBACK(nsgtk_pi_delete_event),
ncwin);
/* accept button */
g_signal_connect(G_OBJECT(gtk_builder_get_object(ncwin->builder,
"sslaccept")),
"clicked",
- G_CALLBACK(nsgtk_crtvrfy_accept),
+ G_CALLBACK(nsgtk_pi_accept),
ncwin);
/* reject button */
g_signal_connect(G_OBJECT(gtk_builder_get_object(ncwin->builder,
"sslreject")),
"clicked",
- G_CALLBACK(nsgtk_crtvrfy_reject),
+ G_CALLBACK(nsgtk_pi_reject),
ncwin);
/* initialise GTK core window */
- ncwin->core.draw = nsgtk_crtvrfy_draw;
- ncwin->core.key = nsgtk_crtvrfy_key;
- ncwin->core.mouse = nsgtk_crtvrfy_mouse;
+ ncwin->core.draw = nsgtk_pi_draw;
+ ncwin->core.key = nsgtk_pi_key;
+ ncwin->core.mouse = nsgtk_pi_mouse;
res = nsgtk_corewindow_init(&ncwin->core);
if (res != NSERROR_OK) {
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=66c069816a33158b013...
commit 66c069816a33158b013d138d3d20228edcf6850d
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
NetSurf: Init/fini the page-info module on browser startup/quit.
diff --git a/desktop/netsurf.c b/desktop/netsurf.c
index e3babd8..7577e1f 100644
--- a/desktop/netsurf.c
+++ b/desktop/netsurf.c
@@ -49,6 +49,7 @@
#include "netsurf/browser_window.h"
#include "desktop/system_colour.h"
+#include "desktop/page-info.h"
#include "desktop/searchweb.h"
#include "netsurf/misc.h"
#include "desktop/gui_internal.h"
@@ -206,6 +207,11 @@ nserror netsurf_init(const char *store_path)
js_initialise();
+ ret = page_info_init();
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
return NSERROR_OK;
}
@@ -220,7 +226,10 @@ void netsurf_exit(void)
NSLOG(netsurf, INFO, "Closing GUI");
guit->misc->quit();
-
+
+ NSLOG(netsurf, INFO, "Finalising page-info module");
+ page_info_fini();
+
NSLOG(netsurf, INFO, "Finalising JavaScript");
js_finalise();
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=0e4f09db0fd92c6fc07...
commit 0e4f09db0fd92c6fc07c7e26c53d78124184b937
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Buildsystem: Build the page-info module.
diff --git a/desktop/Makefile b/desktop/Makefile
index 0d88b2b..3e40449 100644
--- a/desktop/Makefile
+++ b/desktop/Makefile
@@ -3,7 +3,7 @@
S_DESKTOP := cookie_manager.c knockout.c hotlist.c mouse.c \
plot_style.c print.c search.c searchweb.c scrollbar.c \
sslcert_viewer.c textarea.c version.c system_colour.c \
- local_history.c global_history.c treeview.c
+ local_history.c global_history.c treeview.c page-info.c
S_DESKTOP := $(addprefix desktop/,$(S_DESKTOP))
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=7a3f86494049fcfddaf...
commit 7a3f86494049fcfddaf00331d8bdbc746b4bbecb
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Page info: Add core window size getter, and stub for keypresses.
diff --git a/desktop/page-info.c b/desktop/page-info.c
index bbbbbb5..51784c4 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -766,3 +766,23 @@ nserror page_info_mouse_action(
return NSERROR_OK;
}
+
+/* Exported interface documented in desktop/page_info.h */
+bool page_info_keypress(
+ struct page_info *pi,
+ int32_t key)
+{
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_get_size(
+ struct page_info *pi,
+ int *width,
+ int *height)
+{
+ *width = pi->width;
+ *height = pi->height;
+
+ return NSERROR_OK;
+}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=1149a1304d56a0fe758...
commit 1149a1304d56a0fe758eba5686b156f5006f16f8
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Page info: Implement mouse action handling.
diff --git a/desktop/page-info.c b/desktop/page-info.c
index c7827f3..bbbbbb5 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -675,3 +675,94 @@ cleanup:
return NSERROR_OK;
}
+
+/**
+ * Handle any clicks on an item.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] mouse The current mouse state.
+ * \param[in] clicked The page info window entry to consider clicks on.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__handle_item_click(
+ struct page_info *pi,
+ enum browser_mouse_state mouse,
+ enum pi_entry clicked)
+{
+ nserror err;
+
+ if (!(mouse & BROWSER_MOUSE_CLICK_1)) {
+ return NSERROR_OK;
+ }
+
+ switch (clicked) {
+ case PI_ENTRY_CERT:
+ err = browser_window_show_certificates(pi->bw);
+ break;
+ case PI_ENTRY_COOKIES:
+ err = browser_window_show_cookies(pi->bw);
+ break;
+ default:
+ err = NSERROR_OK;
+ break;
+ }
+
+ return err;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_mouse_action(
+ struct page_info *pi,
+ enum browser_mouse_state mouse,
+ int x,
+ int y)
+{
+ int cur_y = 0;
+ nserror err;
+
+ cur_y += pi->window_padding;
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ struct page_info_entry *entry = pi->entries + i;
+ bool hovering = false;
+ int height;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ cur_y += entry->text.height;
+ cur_y += entry->text.padding_bottom;
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ height = entry->item.padding_top +
+ entry->item.item.height +
+ entry->item.padding_bottom;
+
+ if (y >= cur_y && y < cur_y + height) {
+ hovering = true;
+ err = page_info__handle_item_click(
+ pi, mouse, i);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ }
+ if (entry->item.hover != hovering) {
+ int w, h;
+ struct rect r = {
+ .x0 = 0,
+ .y0 = cur_y,
+ .y1 = cur_y + height,
+ };
+ pi->cw_t->get_window_dimensions(
+ pi->cw_h, &w, &h);
+ r.x1 = (pi->width > w) ? pi->width : w;
+
+ pi->cw_t->invalidate(pi->cw_h, &r);
+ }
+ entry->item.hover = hovering;
+ cur_y += height;
+ break;
+ }
+ }
+
+ return NSERROR_OK;
+}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=4b2697c989e6bb33f60...
commit 4b2697c989e6bb33f60ca2b53ac1dfefaa08fc04
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Page info: Implement redraw.
diff --git a/desktop/page-info.c b/desktop/page-info.c
index 0666aaf..c7827f3 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -561,3 +561,117 @@ void page_info_destroy(
}
free(pi);
}
+
+/**
+ * Render a text entry.
+ *
+ * \param[in] pit The page info window handle.
+ * \param[in] x X-coordinate to plot at.
+ * \param[in] y Y-coordinate to plot at.
+ * \param[in] ctx Current redraw context.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__redraw_text_entry(
+ const struct page_info_text *pit,
+ int x,
+ int y,
+ const struct redraw_context *ctx)
+{
+ int baseline = (pit->height * 3 + 2) / 4;
+
+ ctx->plot->text(ctx, pit->style, x, y + baseline,
+ pit->text, strlen(pit->text));
+
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_redraw(
+ const struct page_info *pi,
+ int x,
+ int y,
+ const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ struct redraw_context new_ctx = *ctx;
+ struct rect r = {
+ .x0 = clip->x0 + x,
+ .y0 = clip->y0 + y,
+ .x1 = clip->x1 + x,
+ .y1 = clip->y1 + y,
+ };
+ int cur_y = 0;
+ nserror err;
+
+ /* Start knockout rendering if it's available for this plotter. */
+ if (ctx->plot->option_knockout) {
+ bool res = knockout_plot_start(ctx, &new_ctx);
+ if (res == false) {
+ return NSERROR_UNKNOWN;
+ }
+ }
+
+ /* Set up clip rectangle and draw background. */
+ new_ctx.plot->clip(&new_ctx, &r);
+ new_ctx.plot->rectangle(&new_ctx, &pi__bg, &r);
+
+ cur_y += pi->window_padding;
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ const struct page_info_entry *entry = pi->entries + i;
+ int cur_x = pi->window_padding;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ err = page_info__redraw_text_entry(
+ &entry->text,
+ cur_x, cur_y,
+ &new_ctx);
+ if (err != NSERROR_OK) {
+ goto cleanup;
+ }
+ cur_y += entry->text.height;
+ cur_y += entry->text.padding_bottom;
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ if (entry->item.hover) {
+ r.y0 = cur_y;
+ r.y1 = cur_y + entry->item.padding_top +
+ entry->item.item.height +
+ entry->item.padding_bottom;
+ new_ctx.plot->rectangle(&new_ctx,
+ &pi__hover, &r);
+ }
+ cur_y += entry->item.padding_top;
+ err = page_info__redraw_text_entry(
+ &entry->item.item,
+ cur_x, cur_y,
+ &new_ctx);
+ if (err != NSERROR_OK) {
+ goto cleanup;
+ }
+ cur_x += entry->item.item.width;
+ err = page_info__redraw_text_entry(
+ &entry->item.detail,
+ cur_x, cur_y,
+ &new_ctx);
+ if (err != NSERROR_OK) {
+ goto cleanup;
+ }
+ cur_y += entry->item.item.height;
+ cur_y += entry->item.padding_bottom;
+ break;
+ }
+ }
+
+cleanup:
+ /* Rendering complete */
+ if (ctx->plot->option_knockout) {
+ bool res = knockout_plot_end(ctx);
+ if (res == false) {
+ return NSERROR_UNKNOWN;
+ }
+ }
+
+ return NSERROR_OK;
+}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=c32e1bb340d2d40d0a5...
commit c32e1bb340d2d40d0a5b61e37e6fd55f84953633
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Page info: Implement page info window creation and destruction.
diff --git a/desktop/page-info.c b/desktop/page-info.c
index ad0a71a..0666aaf 100644
--- a/desktop/page-info.c
+++ b/desktop/page-info.c
@@ -311,3 +311,253 @@ nserror page_info_fini(void)
{
return NSERROR_OK;
}
+
+/**
+ * Measure the text in the page_info window.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__measure_text_entry(
+ struct page_info_text *pit)
+{
+ nserror err;
+ int height_px;
+
+ err = guit->layout->width(pit->style,
+ pit->text, strlen(pit->text),
+ &pit->width);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ /* \todo: This needs to be a helper in plot style or in nscss. */
+ height_px = ((pit->style->size / PLOT_STYLE_SCALE) *
+ FIXTOINT(nscss_screen_dpi) + 36) / 72;
+
+ pit->height = (height_px * 8 + 3) / 6;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Measure the text in the page_info window.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__measure_text(
+ struct page_info *pi)
+{
+ nserror err;
+
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ struct page_info_entry *entry = pi->entries + i;
+ int padding;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ err = page_info__measure_text_entry(
+ &entry->text);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ if (i == PI_ENTRY_DOMAIN) {
+ entry->text.padding_bottom =
+ entry->text.height * 3 / 2;
+ }
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ err = page_info__measure_text_entry(
+ &entry->item.item);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ err = page_info__measure_text_entry(
+ &entry->item.detail);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ padding = entry->item.item.height / 4;
+ entry->item.padding_top = padding;
+ entry->item.padding_bottom = padding;
+
+ break;
+ }
+ }
+
+ pi->window_padding = pi->entries[PI_ENTRY_DOMAIN].item.item.height / 2;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Set the text for the page_info window.
+ *
+ * \todo Use messages for internationalisation.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__set_text(
+ struct page_info *pi)
+{
+ int printed;
+ static const char *header[PAGE_STATE__COUNT] = {
+ [PAGE_STATE_UNKNOWN] = "Provenience unknown",
+ [PAGE_STATE_INTERNAL] = "NetSurf data",
+ [PAGE_STATE_LOCAL] = "Local data",
+ [PAGE_STATE_INSECURE] = "Connection not secure",
+ [PAGE_STATE_SECURE_OVERRIDE] = "Connection not secure",
+ [PAGE_STATE_SECURE_ISSUES] = "Connection not secure",
+ [PAGE_STATE_SECURE] = "Connection is secure",
+ };
+ static const char *certificate[PAGE_STATE__COUNT] = {
+ [PAGE_STATE_UNKNOWN] = "Missing",
+ [PAGE_STATE_INTERNAL] = "None",
+ [PAGE_STATE_LOCAL] = "None",
+ [PAGE_STATE_INSECURE] = "Not valid",
+ [PAGE_STATE_SECURE_OVERRIDE] = "Not valid",
+ [PAGE_STATE_SECURE_ISSUES] = "Not valid",
+ [PAGE_STATE_SECURE] = "Valid",
+ };
+
+ assert(pi != NULL);
+ assert(pi->state < PAGE_STATE__COUNT);
+
+ pi->entries[PI_ENTRY_HEADER].text.style = &pi__heading[pi->state];
+ pi->entries[PI_ENTRY_HEADER].text.text = header[pi->state];
+ pi->entries[PI_ENTRY_DOMAIN].text.text = (pi->domain) ?
+ lwc_string_data(pi->domain) : "<No domain>";
+
+ pi->entries[PI_ENTRY_CERT].item.item.text = "Certificate: ";
+ pi->entries[PI_ENTRY_CERT].item.detail.text = certificate[pi->state];
+
+ printed = snprintf(pi->cookie_text, sizeof(pi->cookie_text),
+ "(%u in use)", pi->cookies);
+ if (printed < 0) {
+ return NSERROR_UNKNOWN;
+
+ } else if ((unsigned) printed >= sizeof(pi->cookie_text)) {
+ return NSERROR_NOSPACE;
+ }
+ pi->entries[PI_ENTRY_COOKIES].item.item.text = "Cookies: ";
+ pi->entries[PI_ENTRY_COOKIES].item.detail.text = pi->cookie_text;
+
+ return page_info__measure_text(pi);
+}
+
+/**
+ * Create page info from a browser window.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] bw Browser window to show page info for.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__create_from_bw(
+ struct page_info *pi,
+ const struct browser_window *bw)
+{
+ nsurl *url = browser_window_access_url(bw);
+
+ pi->bw = bw;
+ pi->state = browser_window_get_page_info_state(bw);
+ pi->cookies = browser_window_get_cookie_count(bw);
+ pi->domain = nsurl_get_component(url, NSURL_HOST);
+
+ return page_info__set_text(pi);
+}
+
+/**
+ * Lay out the page info window.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__layout(
+ struct page_info *pi)
+{
+ int cur_y = 0;
+ int max_x = 0;
+
+ cur_y += pi->window_padding;
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ struct page_info_entry *entry = pi->entries + i;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ cur_y += entry->text.height;
+ if (max_x < entry->text.width) {
+ max_x = entry->text.width;
+ }
+ cur_y += entry->text.padding_bottom;
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ {
+ int full_width = entry->item.item.width +
+ entry->item.detail.width;
+ cur_y += entry->item.padding_top;
+ cur_y += entry->item.item.height;
+ if (max_x < full_width) {
+ max_x = full_width;
+ }
+ cur_y += entry->item.padding_bottom;
+ }
+ break;
+ }
+ }
+ cur_y += pi->window_padding;
+ max_x += pi->window_padding * 2;
+
+ pi->width = max_x;
+ pi->height = cur_y;
+ return pi->cw_t->update_size(pi->cw_h, max_x, cur_y);
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_create(
+ const struct core_window_callback_table *cw_t,
+ struct core_window *cw_h,
+ const struct browser_window *bw,
+ struct page_info **pi_out)
+{
+ struct page_info *pi;
+ nserror err;
+
+ pi = calloc(1, sizeof(*pi));
+ if (pi == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ pi->cw_t = cw_t;
+ pi->cw_h = cw_h;
+
+ memcpy(pi->entries, pi__entries, sizeof(pi__entries));
+
+ err = page_info__create_from_bw(pi, bw);
+ if (err != NSERROR_OK) {
+ page_info_destroy(pi);
+ return err;
+ }
+
+ err = page_info__layout(pi);
+ if (err != NSERROR_OK) {
+ page_info_destroy(pi);
+ return err;
+ }
+
+ *pi_out = pi;
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+void page_info_destroy(
+ struct page_info *pi)
+{
+ if (pi->domain != NULL) {
+ lwc_string_unref(pi->domain);
+ }
+ free(pi);
+}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=3940918b6830aa8273d...
commit 3940918b6830aa8273d0860d5d1d49706f969d3e
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Page info: Add data structures and module init/fini.
diff --git a/desktop/page-info.c b/desktop/page-info.c
new file mode 100644
index 0000000..ad0a71a
--- /dev/null
+++ b/desktop/page-info.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2020 Michael Drake <tlsa(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Pave info viewer window implementation
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "css/utils.h"
+
+#include "utils/nsurl.h"
+
+#include "netsurf/mouse.h"
+#include "netsurf/layout.h"
+#include "netsurf/keypress.h"
+#include "netsurf/plotters.h"
+#include "netsurf/core_window.h"
+#include "netsurf/browser_window.h"
+
+#include "desktop/knockout.h"
+#include "desktop/page-info.h"
+#include "desktop/gui_internal.h"
+#include "desktop/system_colour.h"
+
+/**
+ * Plot style for heading font.
+ */
+static plot_font_style_t pi__heading[PAGE_STATE__COUNT] = {
+ [PAGE_STATE_UNKNOWN] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_INTERNAL] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_LOCAL] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_INSECURE] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_SECURE_OVERRIDE] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_SECURE_ISSUES] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_SECURE] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+};
+
+/**
+ * Plot style for domain font.
+ */
+static plot_font_style_t pi__domain = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 8 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 700,
+};
+
+/**
+ * Plot style for item font.
+ */
+static plot_font_style_t pi__item = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 11 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+};
+
+/**
+ * Plot style for item detail font.
+ */
+static plot_font_style_t pi__item_detail = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 11 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+};
+
+/**
+ * Plot style for window background.
+ */
+static plot_style_t pi__bg = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+
+/**
+ * Plot style for hover background.
+ */
+static plot_style_t pi__hover = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+
+/**
+ * An "text" type page info entry.
+ */
+struct page_info_text {
+ const char *text;
+ const plot_font_style_t *style;
+ int width;
+ int height;
+ int padding_bottom;
+};
+
+/**
+ * An "item" type page info entry.
+ */
+struct page_info_item {
+ struct page_info_text item;
+ struct page_info_text detail;
+ const plot_style_t *hover_bg;
+ int padding_bottom;
+ int padding_top;
+ bool hover;
+};
+
+/**
+ * List of page info window entries.
+ */
+enum pi_entry {
+ PI_ENTRY_HEADER,
+ PI_ENTRY_DOMAIN,
+ PI_ENTRY_CERT,
+ PI_ENTRY_COOKIES,
+ PI_ENTRY__COUNT,
+};
+
+/**
+ * An entry on a page info window.
+ */
+struct page_info_entry {
+ /**
+ * List of page info entry types.
+ */
+ enum page_info_entry_type {
+ PAGE_INFO_ENTRY_TYPE_TEXT,
+ PAGE_INFO_ENTRY_TYPE_ITEM,
+ } type;
+ /**
+ * Type-specific page info entry data.
+ */
+ union {
+ struct page_info_text text;
+ struct page_info_item item;
+ };
+};
+
+/**
+ * The default page info window data.
+ */
+struct page_info_entry pi__entries[PI_ENTRY__COUNT] = {
+ [PI_ENTRY_HEADER] = {
+ .type = PAGE_INFO_ENTRY_TYPE_TEXT,
+ },
+ [PI_ENTRY_DOMAIN] = {
+ .type = PAGE_INFO_ENTRY_TYPE_TEXT,
+ .text = {
+ .style = &pi__domain,
+ },
+ },
+ [PI_ENTRY_CERT] = {
+ .type = PAGE_INFO_ENTRY_TYPE_ITEM,
+ .item = {
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
+ },
+ },
+ [PI_ENTRY_COOKIES] = {
+ .type = PAGE_INFO_ENTRY_TYPE_ITEM,
+ .item = {
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
+ },
+ },
+};
+
+/**
+ * The page info window structure.
+ */
+struct page_info {
+ const struct core_window_callback_table *cw_t;
+ struct core_window *cw_h;
+
+ const struct browser_window *bw;
+ lwc_string *domain;
+
+ browser_window_page_info_state state;
+ unsigned cookies;
+
+ char cookie_text[64];
+ struct page_info_entry entries[PI_ENTRY__COUNT];
+
+ int width;
+ int height;
+
+ int window_padding;
+};
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_init(void)
+{
+ bool dark_on_light;
+ nserror err;
+ colour good;
+ colour bad;
+ colour bg;
+ colour fg;
+
+ err = ns_system_colour_char("Window", &bg);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ err = ns_system_colour_char("WindowText", &fg);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ dark_on_light = colour_lightness(bg) > colour_lightness(fg);
+
+ pi__bg.fill_colour = bg;
+ pi__hover.fill_colour = dark_on_light?
+ darken_colour(bg) :
+ lighten_colour(bg);
+
+ pi__domain.background = bg;
+ pi__domain.foreground = fg;
+
+ pi__item.background = bg;
+ pi__item.foreground = fg;
+
+ pi__item_detail.background = bg;
+ pi__item_detail.foreground = blend_colour(bg, fg);
+
+ good = colour_engorge_component(fg,
+ dark_on_light, PLOT_COLOUR_COMPONENT_GREEN);
+ bad = colour_engorge_component(fg,
+ dark_on_light, PLOT_COLOUR_COMPONENT_RED);
+
+ pi__heading[PAGE_STATE_UNKNOWN].background = bg;
+ pi__heading[PAGE_STATE_UNKNOWN].foreground = bad;
+ pi__heading[PAGE_STATE_INTERNAL].background = bg;
+ pi__heading[PAGE_STATE_INTERNAL].foreground = fg;
+ pi__heading[PAGE_STATE_LOCAL].background = bg;
+ pi__heading[PAGE_STATE_LOCAL].foreground = fg;
+ pi__heading[PAGE_STATE_INSECURE].background = bg;
+ pi__heading[PAGE_STATE_INSECURE].foreground = bad;
+ pi__heading[PAGE_STATE_SECURE_OVERRIDE].background = bg;
+ pi__heading[PAGE_STATE_SECURE_OVERRIDE].foreground = bad;
+ pi__heading[PAGE_STATE_SECURE_ISSUES].background = bg;
+ pi__heading[PAGE_STATE_SECURE_ISSUES].foreground = bad;
+ pi__heading[PAGE_STATE_SECURE].background = bg;
+ pi__heading[PAGE_STATE_SECURE].foreground = good;
+
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_fini(void)
+{
+ return NSERROR_OK;
+}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=9198c9958afedc81048...
commit 9198c9958afedc8104858979ec949c72933cbf91
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Desktop: Add page-info API.
This will be a core window intended to show information about the
page currently shown in the associated browser window.
diff --git a/desktop/page-info.h b/desktop/page-info.h
new file mode 100644
index 0000000..89b82e0
--- /dev/null
+++ b/desktop/page-info.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2020 Michael Drake <tlsa(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Pave info viewer window interface
+ */
+
+#ifndef NETSURF_DESKTOP_PAGE_INFO_H
+#define NETSURF_DESKTOP_PAGE_INFO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "utils/errors.h"
+#include "netsurf/mouse.h"
+
+struct rect;
+struct nsurl;
+struct page_info;
+struct core_window;
+struct browser_window;
+struct redraw_context;
+struct core_window_callback_table;
+
+/**
+ * Initialise the page_info module.
+ *
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_init(void);
+
+/**
+ * Finalise the page_info module.
+ *
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_fini(void);
+
+/**
+ * Create a page info corewindow.
+ *
+ * The page info window is opened for a particular browser window.
+ * It can be destroyed before the browser window is destroyed by calling
+ * \ref page_info_destroy.
+ *
+ * \param[in] cw_t Callback table for the containing core_window.
+ * \param[in] cw_h Handle for the containing core_window.
+ * \param[in] bw Browser window to show page info for.
+ * \param[out] pi_out The created page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_create(
+ const struct core_window_callback_table *cw_t,
+ struct core_window *cw_h,
+ const struct browser_window *bw,
+ struct page_info **pi_out);
+
+/**
+ * Destroy a page info corewindow.
+ *
+ * \param[in] pi The page info window handle.
+ */
+void page_info_destroy(
+ struct page_info *pi);
+
+/**
+ * Redraw the page info window.
+ *
+ * Causes the page info window to issue plot operations to redraw
+ * the specified area of the viewport.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] x X coordinate to render page_info at.
+ * \param[in] y Y coordinate to render page_info at.
+ * \param[in] clip Current clip rectangle.
+ * \param[in] ctx Current redraw context.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_redraw(
+ const struct page_info *pi,
+ int x,
+ int y,
+ const struct rect *clip,
+ const struct redraw_context *ctx);
+
+/**
+ * Mouse action handling.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] mouse The current mouse state
+ * \param[in] x The current mouse X coordinate
+ * \param[in] y The current mouse Y coordinate
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_mouse_action(
+ struct page_info *pi,
+ enum browser_mouse_state mouse,
+ int x,
+ int y);
+
+/**
+ * Key press handling.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] key The ucs4 character codepoint.
+ * \return true if the keypress is dealt with, false otherwise.
+ */
+bool page_info_keypress(
+ struct page_info *pi,
+ int32_t key);
+
+/**
+ * Get size of page info content area.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[out] width On success, return the page info content width.
+ * \param[out] height On success, return the page info content height.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_get_size(
+ struct page_info *pi,
+ int *width,
+ int *height);
+
+#endif
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=8da6252f5024aa1de3d...
commit 8da6252f5024aa1de3d4d9018d2ff32f96d22cd8
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Browser window API: Add count for page state enum.
diff --git a/include/netsurf/browser_window.h b/include/netsurf/browser_window.h
index cc63f6e..e463dfa 100644
--- a/include/netsurf/browser_window.h
+++ b/include/netsurf/browser_window.h
@@ -70,6 +70,7 @@ typedef enum {
PAGE_STATE_SECURE_OVERRIDE, /**< Secure load, but had to override */
PAGE_STATE_SECURE_ISSUES, /**< Secure load, but has insecure elements */
PAGE_STATE_SECURE, /**< Secure load */
+ PAGE_STATE__COUNT, /**< Count of number of valid page states */
} browser_window_page_info_state;
typedef enum {
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=a39b651620272d240c4...
commit a39b651620272d240c40a9ff056eb50a758350f7
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Browser window: Add stubs for new interfaces for page-info dialogue.
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 2b0e314..59ab73d 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -4721,3 +4721,27 @@ browser_window_get_ssl_chain(struct browser_window *bw,
return NSERROR_OK;
}
+
+/* Exported interface, documented in browser_window.h */
+int browser_window_get_cookie_count(
+ const struct browser_window *bw)
+{
+ /** \todo Implement cookie count */
+ return 0;
+}
+
+/* Exported interface, documented in browser_window.h */
+nserror browser_window_show_cookies(
+ const struct browser_window *bw)
+{
+ /** \todo Implement show cookies */
+ return NSERROR_OK;
+}
+
+/* Exported interface, documented in browser_window.h */
+nserror browser_window_show_certificates(
+ const struct browser_window *bw)
+{
+ /** \todo Implement show certificates */
+ return NSERROR_OK;
+}
diff --git a/include/netsurf/browser_window.h b/include/netsurf/browser_window.h
index eb2bd7c..cc63f6e 100644
--- a/include/netsurf/browser_window.h
+++ b/include/netsurf/browser_window.h
@@ -789,4 +789,33 @@ browser_window_page_info_state browser_window_get_page_info_state(
*/
nserror browser_window_get_ssl_chain(struct browser_window *bw, struct cert_chain **chain);
+/**
+ * Get the number of cookies in use for the current page.
+ *
+ * \param bw A browser window.
+ * \return Number of cookies in use, or 0 on error.
+ */
+int browser_window_get_cookie_count(
+ const struct browser_window *bw);
+
+/**
+ * Open cookie viewer for the current page.
+ *
+ * \param bw A browser window.
+ * \return NSERROR_OK, or appropriate error otherwise.
+ */
+nserror browser_window_show_cookies(
+ const struct browser_window *bw);
+
+/**
+ * Show the certificate page for the current page.
+ *
+ * Does nothing for a page without certificates.
+ *
+ * \param bw A browser window.
+ * \return NSERROR_OK, or appropriate error otherwise.
+ */
+nserror browser_window_show_certificates(
+ const struct browser_window *bw);
+
#endif
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=d23a7b4c8ffda7448bd...
commit d23a7b4c8ffda7448bdb05591655954655a3cfe0
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Browser window: Constify bw through page_info_state getter.
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 6defe01..2b0e314 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -4644,7 +4644,7 @@ browser_window__reload_current_parameters(struct browser_window *bw)
/* Exported interface, documented in browser_window.h */
browser_window_page_info_state browser_window_get_page_info_state(
- struct browser_window *bw)
+ const struct browser_window *bw)
{
lwc_string *scheme;
bool match;
diff --git a/include/netsurf/browser_window.h b/include/netsurf/browser_window.h
index e8faa18..eb2bd7c 100644
--- a/include/netsurf/browser_window.h
+++ b/include/netsurf/browser_window.h
@@ -774,7 +774,7 @@ nserror browser_window_console_log(struct browser_window *bw,
* \return The state of the browser window
*/
browser_window_page_info_state browser_window_get_page_info_state(
- struct browser_window *bw);
+ const struct browser_window *bw);
/**
* Request the current browser window SSL certificate chain.
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=475d397b8c3aa5ca672...
commit 475d397b8c3aa5ca672b7ec984a41302449b7727
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Plot style: Add function to engorge a colour channel.
This can be used to exaggerate the red, green, or blue component.
diff --git a/include/netsurf/plot_style.h b/include/netsurf/plot_style.h
index 692192f..bc86041 100644
--- a/include/netsurf/plot_style.h
+++ b/include/netsurf/plot_style.h
@@ -188,6 +188,39 @@ typedef struct plot_font_style {
#define blue_from_colour(c) \
((c >> 16) & 0xff)
+/** Colour components */
+enum plot_colour_component {
+ PLOT_COLOUR_COMPONENT_RED,
+ PLOT_COLOUR_COMPONENT_GREEN,
+ PLOT_COLOUR_COMPONENT_BLUE,
+ PLOT_COLOUR_COMPONENT_ALPHA,
+};
+
+/**
+ * Engorge a particular colour channel.
+ *
+ * \param[in] col The colour to engorge a component of.
+ * \param[in] dark Whether col is a dark colour.
+ * \param[in] comp Colour component to engorge.
+ */
+static inline colour colour_engorge_component(
+ colour col,
+ bool dark,
+ enum plot_colour_component comp)
+{
+ static const colour mask[PLOT_COLOUR_COMPONENT_ALPHA] = {
+ [PLOT_COLOUR_COMPONENT_RED] = 0x0000ff,
+ [PLOT_COLOUR_COMPONENT_GREEN] = 0x00ff00,
+ [PLOT_COLOUR_COMPONENT_BLUE] = 0xff0000,
+ };
+ colour d = dark ? darken_colour(col) : double_darken_colour(col);
+ colour l = dark ? double_lighten_colour(col) : lighten_colour(col);
+
+ assert(comp < PLOT_COLOUR_COMPONENT_ALPHA);
+
+ return (mask[comp] & l) | (~mask[comp] & d);
+}
+
/* global fill styles */
extern plot_style_t *plot_style_fill_white;
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=6783deba4e209461fd8...
commit 6783deba4e209461fd8105b9b46dbcacefed6b6f
Author: Michael Drake <michael.drake(a)codethink.co.uk>
Commit: Michael Drake <michael.drake(a)codethink.co.uk>
Plot style: Split lightness calc out of colour_to_bw_furthest macro.
diff --git a/include/netsurf/plot_style.h b/include/netsurf/plot_style.h
index f1b6172..692192f 100644
--- a/include/netsurf/plot_style.h
+++ b/include/netsurf/plot_style.h
@@ -153,13 +153,16 @@ typedef struct plot_font_style {
(((((c0 & 0xff00ff) + (c1 & 0xff00ff)) >> 1) & 0xff00ff) | \
((((c0 & 0x00ff00) + (c1 & 0x00ff00)) >> 1) & 0x00ff00))
+/* Get the percieved lightness of the supplied colour, c0. */
+#define colour_lightness(c0) \
+ ((((c0 & 0x0000ff) * 77) >> 8) + \
+ (((c0 & 0x00ff00) * 151) >> 16) + \
+ (((c0 & 0xff0000) * 28) >> 24))
+
/* Choose either black or white, depending on which is furthest from the
* percieved lightness of the supplied colour, c0. */
#define colour_to_bw_furthest(c0) \
- ((((((c0 & 0x0000ff) * 77) >> 8) + \
- (((c0 & 0x00ff00) * 151) >> 16) + \
- (((c0 & 0xff0000) * 28) >> 24)) > \
- (0xff / 2)) ? 0x000000 : 0xffffff)
+ ((colour_lightness(c0) > (0xff / 2)) ? 0x000000 : 0xffffff)
/* Mix two colours according to the proportion given by p, where 0 <= p <= 255
* p = 0 gives result ==> c1, p = 255 gives result ==> c0 */
-----------------------------------------------------------------------
Summary of changes:
desktop/Makefile | 2 +-
desktop/browser_window.c | 26 +-
desktop/netsurf.c | 11 +-
desktop/page-info.c | 788 ++++++++++++++++++++++++++++++++++++++
desktop/page-info.h | 141 +++++++
frontends/gtk/page_info.c | 78 ++--
include/netsurf/browser_window.h | 32 +-
include/netsurf/plot_style.h | 44 ++-
8 files changed, 1075 insertions(+), 47 deletions(-)
create mode 100644 desktop/page-info.c
create mode 100644 desktop/page-info.h
diff --git a/desktop/Makefile b/desktop/Makefile
index 0d88b2b..3e40449 100644
--- a/desktop/Makefile
+++ b/desktop/Makefile
@@ -3,7 +3,7 @@
S_DESKTOP := cookie_manager.c knockout.c hotlist.c mouse.c \
plot_style.c print.c search.c searchweb.c scrollbar.c \
sslcert_viewer.c textarea.c version.c system_colour.c \
- local_history.c global_history.c treeview.c
+ local_history.c global_history.c treeview.c page-info.c
S_DESKTOP := $(addprefix desktop/,$(S_DESKTOP))
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 6defe01..59ab73d 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -4644,7 +4644,7 @@ browser_window__reload_current_parameters(struct browser_window *bw)
/* Exported interface, documented in browser_window.h */
browser_window_page_info_state browser_window_get_page_info_state(
- struct browser_window *bw)
+ const struct browser_window *bw)
{
lwc_string *scheme;
bool match;
@@ -4721,3 +4721,27 @@ browser_window_get_ssl_chain(struct browser_window *bw,
return NSERROR_OK;
}
+
+/* Exported interface, documented in browser_window.h */
+int browser_window_get_cookie_count(
+ const struct browser_window *bw)
+{
+ /** \todo Implement cookie count */
+ return 0;
+}
+
+/* Exported interface, documented in browser_window.h */
+nserror browser_window_show_cookies(
+ const struct browser_window *bw)
+{
+ /** \todo Implement show cookies */
+ return NSERROR_OK;
+}
+
+/* Exported interface, documented in browser_window.h */
+nserror browser_window_show_certificates(
+ const struct browser_window *bw)
+{
+ /** \todo Implement show certificates */
+ return NSERROR_OK;
+}
diff --git a/desktop/netsurf.c b/desktop/netsurf.c
index e3babd8..7577e1f 100644
--- a/desktop/netsurf.c
+++ b/desktop/netsurf.c
@@ -49,6 +49,7 @@
#include "netsurf/browser_window.h"
#include "desktop/system_colour.h"
+#include "desktop/page-info.h"
#include "desktop/searchweb.h"
#include "netsurf/misc.h"
#include "desktop/gui_internal.h"
@@ -206,6 +207,11 @@ nserror netsurf_init(const char *store_path)
js_initialise();
+ ret = page_info_init();
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
return NSERROR_OK;
}
@@ -220,7 +226,10 @@ void netsurf_exit(void)
NSLOG(netsurf, INFO, "Closing GUI");
guit->misc->quit();
-
+
+ NSLOG(netsurf, INFO, "Finalising page-info module");
+ page_info_fini();
+
NSLOG(netsurf, INFO, "Finalising JavaScript");
js_finalise();
diff --git a/desktop/page-info.c b/desktop/page-info.c
new file mode 100644
index 0000000..51784c4
--- /dev/null
+++ b/desktop/page-info.c
@@ -0,0 +1,788 @@
+/*
+ * Copyright 2020 Michael Drake <tlsa(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Pave info viewer window implementation
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "css/utils.h"
+
+#include "utils/nsurl.h"
+
+#include "netsurf/mouse.h"
+#include "netsurf/layout.h"
+#include "netsurf/keypress.h"
+#include "netsurf/plotters.h"
+#include "netsurf/core_window.h"
+#include "netsurf/browser_window.h"
+
+#include "desktop/knockout.h"
+#include "desktop/page-info.h"
+#include "desktop/gui_internal.h"
+#include "desktop/system_colour.h"
+
+/**
+ * Plot style for heading font.
+ */
+static plot_font_style_t pi__heading[PAGE_STATE__COUNT] = {
+ [PAGE_STATE_UNKNOWN] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_INTERNAL] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_LOCAL] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_INSECURE] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_SECURE_OVERRIDE] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_SECURE_ISSUES] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+ [PAGE_STATE_SECURE] = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 14 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+ },
+};
+
+/**
+ * Plot style for domain font.
+ */
+static plot_font_style_t pi__domain = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 8 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 700,
+};
+
+/**
+ * Plot style for item font.
+ */
+static plot_font_style_t pi__item = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 11 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+};
+
+/**
+ * Plot style for item detail font.
+ */
+static plot_font_style_t pi__item_detail = {
+ .family = PLOT_FONT_FAMILY_SANS_SERIF,
+ .size = 11 * PLOT_STYLE_SCALE,
+ .flags = FONTF_NONE,
+ .weight = 400,
+};
+
+/**
+ * Plot style for window background.
+ */
+static plot_style_t pi__bg = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+
+/**
+ * Plot style for hover background.
+ */
+static plot_style_t pi__hover = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+
+/**
+ * An "text" type page info entry.
+ */
+struct page_info_text {
+ const char *text;
+ const plot_font_style_t *style;
+ int width;
+ int height;
+ int padding_bottom;
+};
+
+/**
+ * An "item" type page info entry.
+ */
+struct page_info_item {
+ struct page_info_text item;
+ struct page_info_text detail;
+ const plot_style_t *hover_bg;
+ int padding_bottom;
+ int padding_top;
+ bool hover;
+};
+
+/**
+ * List of page info window entries.
+ */
+enum pi_entry {
+ PI_ENTRY_HEADER,
+ PI_ENTRY_DOMAIN,
+ PI_ENTRY_CERT,
+ PI_ENTRY_COOKIES,
+ PI_ENTRY__COUNT,
+};
+
+/**
+ * An entry on a page info window.
+ */
+struct page_info_entry {
+ /**
+ * List of page info entry types.
+ */
+ enum page_info_entry_type {
+ PAGE_INFO_ENTRY_TYPE_TEXT,
+ PAGE_INFO_ENTRY_TYPE_ITEM,
+ } type;
+ /**
+ * Type-specific page info entry data.
+ */
+ union {
+ struct page_info_text text;
+ struct page_info_item item;
+ };
+};
+
+/**
+ * The default page info window data.
+ */
+struct page_info_entry pi__entries[PI_ENTRY__COUNT] = {
+ [PI_ENTRY_HEADER] = {
+ .type = PAGE_INFO_ENTRY_TYPE_TEXT,
+ },
+ [PI_ENTRY_DOMAIN] = {
+ .type = PAGE_INFO_ENTRY_TYPE_TEXT,
+ .text = {
+ .style = &pi__domain,
+ },
+ },
+ [PI_ENTRY_CERT] = {
+ .type = PAGE_INFO_ENTRY_TYPE_ITEM,
+ .item = {
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
+ },
+ },
+ [PI_ENTRY_COOKIES] = {
+ .type = PAGE_INFO_ENTRY_TYPE_ITEM,
+ .item = {
+ .item = {
+ .style = &pi__item,
+ },
+ .detail = {
+ .style = &pi__item_detail,
+ },
+ .hover_bg = &pi__hover,
+ },
+ },
+};
+
+/**
+ * The page info window structure.
+ */
+struct page_info {
+ const struct core_window_callback_table *cw_t;
+ struct core_window *cw_h;
+
+ const struct browser_window *bw;
+ lwc_string *domain;
+
+ browser_window_page_info_state state;
+ unsigned cookies;
+
+ char cookie_text[64];
+ struct page_info_entry entries[PI_ENTRY__COUNT];
+
+ int width;
+ int height;
+
+ int window_padding;
+};
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_init(void)
+{
+ bool dark_on_light;
+ nserror err;
+ colour good;
+ colour bad;
+ colour bg;
+ colour fg;
+
+ err = ns_system_colour_char("Window", &bg);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ err = ns_system_colour_char("WindowText", &fg);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ dark_on_light = colour_lightness(bg) > colour_lightness(fg);
+
+ pi__bg.fill_colour = bg;
+ pi__hover.fill_colour = dark_on_light?
+ darken_colour(bg) :
+ lighten_colour(bg);
+
+ pi__domain.background = bg;
+ pi__domain.foreground = fg;
+
+ pi__item.background = bg;
+ pi__item.foreground = fg;
+
+ pi__item_detail.background = bg;
+ pi__item_detail.foreground = blend_colour(bg, fg);
+
+ good = colour_engorge_component(fg,
+ dark_on_light, PLOT_COLOUR_COMPONENT_GREEN);
+ bad = colour_engorge_component(fg,
+ dark_on_light, PLOT_COLOUR_COMPONENT_RED);
+
+ pi__heading[PAGE_STATE_UNKNOWN].background = bg;
+ pi__heading[PAGE_STATE_UNKNOWN].foreground = bad;
+ pi__heading[PAGE_STATE_INTERNAL].background = bg;
+ pi__heading[PAGE_STATE_INTERNAL].foreground = fg;
+ pi__heading[PAGE_STATE_LOCAL].background = bg;
+ pi__heading[PAGE_STATE_LOCAL].foreground = fg;
+ pi__heading[PAGE_STATE_INSECURE].background = bg;
+ pi__heading[PAGE_STATE_INSECURE].foreground = bad;
+ pi__heading[PAGE_STATE_SECURE_OVERRIDE].background = bg;
+ pi__heading[PAGE_STATE_SECURE_OVERRIDE].foreground = bad;
+ pi__heading[PAGE_STATE_SECURE_ISSUES].background = bg;
+ pi__heading[PAGE_STATE_SECURE_ISSUES].foreground = bad;
+ pi__heading[PAGE_STATE_SECURE].background = bg;
+ pi__heading[PAGE_STATE_SECURE].foreground = good;
+
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_fini(void)
+{
+ return NSERROR_OK;
+}
+
+/**
+ * Measure the text in the page_info window.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__measure_text_entry(
+ struct page_info_text *pit)
+{
+ nserror err;
+ int height_px;
+
+ err = guit->layout->width(pit->style,
+ pit->text, strlen(pit->text),
+ &pit->width);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ /* \todo: This needs to be a helper in plot style or in nscss. */
+ height_px = ((pit->style->size / PLOT_STYLE_SCALE) *
+ FIXTOINT(nscss_screen_dpi) + 36) / 72;
+
+ pit->height = (height_px * 8 + 3) / 6;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Measure the text in the page_info window.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__measure_text(
+ struct page_info *pi)
+{
+ nserror err;
+
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ struct page_info_entry *entry = pi->entries + i;
+ int padding;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ err = page_info__measure_text_entry(
+ &entry->text);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ if (i == PI_ENTRY_DOMAIN) {
+ entry->text.padding_bottom =
+ entry->text.height * 3 / 2;
+ }
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ err = page_info__measure_text_entry(
+ &entry->item.item);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ err = page_info__measure_text_entry(
+ &entry->item.detail);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ padding = entry->item.item.height / 4;
+ entry->item.padding_top = padding;
+ entry->item.padding_bottom = padding;
+
+ break;
+ }
+ }
+
+ pi->window_padding = pi->entries[PI_ENTRY_DOMAIN].item.item.height / 2;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Set the text for the page_info window.
+ *
+ * \todo Use messages for internationalisation.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__set_text(
+ struct page_info *pi)
+{
+ int printed;
+ static const char *header[PAGE_STATE__COUNT] = {
+ [PAGE_STATE_UNKNOWN] = "Provenience unknown",
+ [PAGE_STATE_INTERNAL] = "NetSurf data",
+ [PAGE_STATE_LOCAL] = "Local data",
+ [PAGE_STATE_INSECURE] = "Connection not secure",
+ [PAGE_STATE_SECURE_OVERRIDE] = "Connection not secure",
+ [PAGE_STATE_SECURE_ISSUES] = "Connection not secure",
+ [PAGE_STATE_SECURE] = "Connection is secure",
+ };
+ static const char *certificate[PAGE_STATE__COUNT] = {
+ [PAGE_STATE_UNKNOWN] = "Missing",
+ [PAGE_STATE_INTERNAL] = "None",
+ [PAGE_STATE_LOCAL] = "None",
+ [PAGE_STATE_INSECURE] = "Not valid",
+ [PAGE_STATE_SECURE_OVERRIDE] = "Not valid",
+ [PAGE_STATE_SECURE_ISSUES] = "Not valid",
+ [PAGE_STATE_SECURE] = "Valid",
+ };
+
+ assert(pi != NULL);
+ assert(pi->state < PAGE_STATE__COUNT);
+
+ pi->entries[PI_ENTRY_HEADER].text.style = &pi__heading[pi->state];
+ pi->entries[PI_ENTRY_HEADER].text.text = header[pi->state];
+ pi->entries[PI_ENTRY_DOMAIN].text.text = (pi->domain) ?
+ lwc_string_data(pi->domain) : "<No domain>";
+
+ pi->entries[PI_ENTRY_CERT].item.item.text = "Certificate: ";
+ pi->entries[PI_ENTRY_CERT].item.detail.text = certificate[pi->state];
+
+ printed = snprintf(pi->cookie_text, sizeof(pi->cookie_text),
+ "(%u in use)", pi->cookies);
+ if (printed < 0) {
+ return NSERROR_UNKNOWN;
+
+ } else if ((unsigned) printed >= sizeof(pi->cookie_text)) {
+ return NSERROR_NOSPACE;
+ }
+ pi->entries[PI_ENTRY_COOKIES].item.item.text = "Cookies: ";
+ pi->entries[PI_ENTRY_COOKIES].item.detail.text = pi->cookie_text;
+
+ return page_info__measure_text(pi);
+}
+
+/**
+ * Create page info from a browser window.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] bw Browser window to show page info for.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__create_from_bw(
+ struct page_info *pi,
+ const struct browser_window *bw)
+{
+ nsurl *url = browser_window_access_url(bw);
+
+ pi->bw = bw;
+ pi->state = browser_window_get_page_info_state(bw);
+ pi->cookies = browser_window_get_cookie_count(bw);
+ pi->domain = nsurl_get_component(url, NSURL_HOST);
+
+ return page_info__set_text(pi);
+}
+
+/**
+ * Lay out the page info window.
+ *
+ * \param[in] pi The page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__layout(
+ struct page_info *pi)
+{
+ int cur_y = 0;
+ int max_x = 0;
+
+ cur_y += pi->window_padding;
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ struct page_info_entry *entry = pi->entries + i;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ cur_y += entry->text.height;
+ if (max_x < entry->text.width) {
+ max_x = entry->text.width;
+ }
+ cur_y += entry->text.padding_bottom;
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ {
+ int full_width = entry->item.item.width +
+ entry->item.detail.width;
+ cur_y += entry->item.padding_top;
+ cur_y += entry->item.item.height;
+ if (max_x < full_width) {
+ max_x = full_width;
+ }
+ cur_y += entry->item.padding_bottom;
+ }
+ break;
+ }
+ }
+ cur_y += pi->window_padding;
+ max_x += pi->window_padding * 2;
+
+ pi->width = max_x;
+ pi->height = cur_y;
+ return pi->cw_t->update_size(pi->cw_h, max_x, cur_y);
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_create(
+ const struct core_window_callback_table *cw_t,
+ struct core_window *cw_h,
+ const struct browser_window *bw,
+ struct page_info **pi_out)
+{
+ struct page_info *pi;
+ nserror err;
+
+ pi = calloc(1, sizeof(*pi));
+ if (pi == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ pi->cw_t = cw_t;
+ pi->cw_h = cw_h;
+
+ memcpy(pi->entries, pi__entries, sizeof(pi__entries));
+
+ err = page_info__create_from_bw(pi, bw);
+ if (err != NSERROR_OK) {
+ page_info_destroy(pi);
+ return err;
+ }
+
+ err = page_info__layout(pi);
+ if (err != NSERROR_OK) {
+ page_info_destroy(pi);
+ return err;
+ }
+
+ *pi_out = pi;
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+void page_info_destroy(
+ struct page_info *pi)
+{
+ if (pi->domain != NULL) {
+ lwc_string_unref(pi->domain);
+ }
+ free(pi);
+}
+
+/**
+ * Render a text entry.
+ *
+ * \param[in] pit The page info window handle.
+ * \param[in] x X-coordinate to plot at.
+ * \param[in] y Y-coordinate to plot at.
+ * \param[in] ctx Current redraw context.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__redraw_text_entry(
+ const struct page_info_text *pit,
+ int x,
+ int y,
+ const struct redraw_context *ctx)
+{
+ int baseline = (pit->height * 3 + 2) / 4;
+
+ ctx->plot->text(ctx, pit->style, x, y + baseline,
+ pit->text, strlen(pit->text));
+
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_redraw(
+ const struct page_info *pi,
+ int x,
+ int y,
+ const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ struct redraw_context new_ctx = *ctx;
+ struct rect r = {
+ .x0 = clip->x0 + x,
+ .y0 = clip->y0 + y,
+ .x1 = clip->x1 + x,
+ .y1 = clip->y1 + y,
+ };
+ int cur_y = 0;
+ nserror err;
+
+ /* Start knockout rendering if it's available for this plotter. */
+ if (ctx->plot->option_knockout) {
+ bool res = knockout_plot_start(ctx, &new_ctx);
+ if (res == false) {
+ return NSERROR_UNKNOWN;
+ }
+ }
+
+ /* Set up clip rectangle and draw background. */
+ new_ctx.plot->clip(&new_ctx, &r);
+ new_ctx.plot->rectangle(&new_ctx, &pi__bg, &r);
+
+ cur_y += pi->window_padding;
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ const struct page_info_entry *entry = pi->entries + i;
+ int cur_x = pi->window_padding;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ err = page_info__redraw_text_entry(
+ &entry->text,
+ cur_x, cur_y,
+ &new_ctx);
+ if (err != NSERROR_OK) {
+ goto cleanup;
+ }
+ cur_y += entry->text.height;
+ cur_y += entry->text.padding_bottom;
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ if (entry->item.hover) {
+ r.y0 = cur_y;
+ r.y1 = cur_y + entry->item.padding_top +
+ entry->item.item.height +
+ entry->item.padding_bottom;
+ new_ctx.plot->rectangle(&new_ctx,
+ &pi__hover, &r);
+ }
+ cur_y += entry->item.padding_top;
+ err = page_info__redraw_text_entry(
+ &entry->item.item,
+ cur_x, cur_y,
+ &new_ctx);
+ if (err != NSERROR_OK) {
+ goto cleanup;
+ }
+ cur_x += entry->item.item.width;
+ err = page_info__redraw_text_entry(
+ &entry->item.detail,
+ cur_x, cur_y,
+ &new_ctx);
+ if (err != NSERROR_OK) {
+ goto cleanup;
+ }
+ cur_y += entry->item.item.height;
+ cur_y += entry->item.padding_bottom;
+ break;
+ }
+ }
+
+cleanup:
+ /* Rendering complete */
+ if (ctx->plot->option_knockout) {
+ bool res = knockout_plot_end(ctx);
+ if (res == false) {
+ return NSERROR_UNKNOWN;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Handle any clicks on an item.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] mouse The current mouse state.
+ * \param[in] clicked The page info window entry to consider clicks on.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+static nserror page_info__handle_item_click(
+ struct page_info *pi,
+ enum browser_mouse_state mouse,
+ enum pi_entry clicked)
+{
+ nserror err;
+
+ if (!(mouse & BROWSER_MOUSE_CLICK_1)) {
+ return NSERROR_OK;
+ }
+
+ switch (clicked) {
+ case PI_ENTRY_CERT:
+ err = browser_window_show_certificates(pi->bw);
+ break;
+ case PI_ENTRY_COOKIES:
+ err = browser_window_show_cookies(pi->bw);
+ break;
+ default:
+ err = NSERROR_OK;
+ break;
+ }
+
+ return err;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_mouse_action(
+ struct page_info *pi,
+ enum browser_mouse_state mouse,
+ int x,
+ int y)
+{
+ int cur_y = 0;
+ nserror err;
+
+ cur_y += pi->window_padding;
+ for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) {
+ struct page_info_entry *entry = pi->entries + i;
+ bool hovering = false;
+ int height;
+
+ switch (entry->type) {
+ case PAGE_INFO_ENTRY_TYPE_TEXT:
+ cur_y += entry->text.height;
+ cur_y += entry->text.padding_bottom;
+ break;
+
+ case PAGE_INFO_ENTRY_TYPE_ITEM:
+ height = entry->item.padding_top +
+ entry->item.item.height +
+ entry->item.padding_bottom;
+
+ if (y >= cur_y && y < cur_y + height) {
+ hovering = true;
+ err = page_info__handle_item_click(
+ pi, mouse, i);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ }
+ if (entry->item.hover != hovering) {
+ int w, h;
+ struct rect r = {
+ .x0 = 0,
+ .y0 = cur_y,
+ .y1 = cur_y + height,
+ };
+ pi->cw_t->get_window_dimensions(
+ pi->cw_h, &w, &h);
+ r.x1 = (pi->width > w) ? pi->width : w;
+
+ pi->cw_t->invalidate(pi->cw_h, &r);
+ }
+ entry->item.hover = hovering;
+ cur_y += height;
+ break;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+bool page_info_keypress(
+ struct page_info *pi,
+ int32_t key)
+{
+ return NSERROR_OK;
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_get_size(
+ struct page_info *pi,
+ int *width,
+ int *height)
+{
+ *width = pi->width;
+ *height = pi->height;
+
+ return NSERROR_OK;
+}
diff --git a/desktop/page-info.h b/desktop/page-info.h
new file mode 100644
index 0000000..89b82e0
--- /dev/null
+++ b/desktop/page-info.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2020 Michael Drake <tlsa(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Pave info viewer window interface
+ */
+
+#ifndef NETSURF_DESKTOP_PAGE_INFO_H
+#define NETSURF_DESKTOP_PAGE_INFO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "utils/errors.h"
+#include "netsurf/mouse.h"
+
+struct rect;
+struct nsurl;
+struct page_info;
+struct core_window;
+struct browser_window;
+struct redraw_context;
+struct core_window_callback_table;
+
+/**
+ * Initialise the page_info module.
+ *
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_init(void);
+
+/**
+ * Finalise the page_info module.
+ *
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_fini(void);
+
+/**
+ * Create a page info corewindow.
+ *
+ * The page info window is opened for a particular browser window.
+ * It can be destroyed before the browser window is destroyed by calling
+ * \ref page_info_destroy.
+ *
+ * \param[in] cw_t Callback table for the containing core_window.
+ * \param[in] cw_h Handle for the containing core_window.
+ * \param[in] bw Browser window to show page info for.
+ * \param[out] pi_out The created page info window handle.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_create(
+ const struct core_window_callback_table *cw_t,
+ struct core_window *cw_h,
+ const struct browser_window *bw,
+ struct page_info **pi_out);
+
+/**
+ * Destroy a page info corewindow.
+ *
+ * \param[in] pi The page info window handle.
+ */
+void page_info_destroy(
+ struct page_info *pi);
+
+/**
+ * Redraw the page info window.
+ *
+ * Causes the page info window to issue plot operations to redraw
+ * the specified area of the viewport.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] x X coordinate to render page_info at.
+ * \param[in] y Y coordinate to render page_info at.
+ * \param[in] clip Current clip rectangle.
+ * \param[in] ctx Current redraw context.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_redraw(
+ const struct page_info *pi,
+ int x,
+ int y,
+ const struct rect *clip,
+ const struct redraw_context *ctx);
+
+/**
+ * Mouse action handling.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] mouse The current mouse state
+ * \param[in] x The current mouse X coordinate
+ * \param[in] y The current mouse Y coordinate
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_mouse_action(
+ struct page_info *pi,
+ enum browser_mouse_state mouse,
+ int x,
+ int y);
+
+/**
+ * Key press handling.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[in] key The ucs4 character codepoint.
+ * \return true if the keypress is dealt with, false otherwise.
+ */
+bool page_info_keypress(
+ struct page_info *pi,
+ int32_t key);
+
+/**
+ * Get size of page info content area.
+ *
+ * \param[in] pi The page info window handle.
+ * \param[out] width On success, return the page info content width.
+ * \param[out] height On success, return the page info content height.
+ * \return NSERROR_OK on success, appropriate error code otherwise.
+ */
+nserror page_info_get_size(
+ struct page_info *pi,
+ int *width,
+ int *height);
+
+#endif
diff --git a/frontends/gtk/page_info.c b/frontends/gtk/page_info.c
index 1401fdc..2892788 100644
--- a/frontends/gtk/page_info.c
+++ b/frontends/gtk/page_info.c
@@ -41,7 +41,7 @@
/**
* GTK certificate viewing window context
*/
-struct nsgtk_crtvrfy_window {
+struct nsgtk_pi_window {
/** GTK core window context */
struct nsgtk_corewindow core;
/** GTK builder for window */
@@ -56,46 +56,46 @@ struct nsgtk_crtvrfy_window {
/**
* destroy a previously created certificate view
*/
-static nserror nsgtk_crtvrfy_destroy(struct nsgtk_crtvrfy_window *crtvrfy_win)
+static nserror nsgtk_pi_destroy(struct nsgtk_pi_window *pi_win)
{
nserror res;
- res = sslcert_viewer_fini(crtvrfy_win->ssl_data);
+ res = sslcert_viewer_fini(pi_win->ssl_data);
if (res == NSERROR_OK) {
- res = nsgtk_corewindow_fini(&crtvrfy_win->core);
- gtk_widget_destroy(GTK_WIDGET(crtvrfy_win->dlg));
- g_object_unref(G_OBJECT(crtvrfy_win->builder));
- free(crtvrfy_win);
+ res = nsgtk_corewindow_fini(&pi_win->core);
+ gtk_widget_destroy(GTK_WIDGET(pi_win->dlg));
+ g_object_unref(G_OBJECT(pi_win->builder));
+ free(pi_win);
}
return res;
}
static void
-nsgtk_crtvrfy_accept(GtkButton *w, gpointer data)
+nsgtk_pi_accept(GtkButton *w, gpointer data)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)data;
+ struct nsgtk_pi_window *pi_win;
+ pi_win = (struct nsgtk_pi_window *)data;
- sslcert_viewer_accept(crtvrfy_win->ssl_data);
+ sslcert_viewer_accept(pi_win->ssl_data);
- nsgtk_crtvrfy_destroy(crtvrfy_win);
+ nsgtk_pi_destroy(pi_win);
}
static void
-nsgtk_crtvrfy_reject(GtkWidget *w, gpointer data)
+nsgtk_pi_reject(GtkWidget *w, gpointer data)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)data;
+ struct nsgtk_pi_window *pi_win;
+ pi_win = (struct nsgtk_pi_window *)data;
- sslcert_viewer_reject(crtvrfy_win->ssl_data);
+ sslcert_viewer_reject(pi_win->ssl_data);
- nsgtk_crtvrfy_destroy(crtvrfy_win);
+ nsgtk_pi_destroy(pi_win);
}
static gboolean
-nsgtk_crtvrfy_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
+nsgtk_pi_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
{
- nsgtk_crtvrfy_reject(w, data);
+ nsgtk_pi_reject(w, data);
return FALSE;
}
@@ -109,15 +109,15 @@ nsgtk_crtvrfy_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
* \return NSERROR_OK on success otherwise appropriate error code
*/
static nserror
-nsgtk_crtvrfy_mouse(struct nsgtk_corewindow *nsgtk_cw,
+nsgtk_pi_mouse(struct nsgtk_corewindow *nsgtk_cw,
browser_mouse_state mouse_state,
int x, int y)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
+ struct nsgtk_pi_window *pi_win;
/* technically degenerate container of */
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)nsgtk_cw;
+ pi_win = (struct nsgtk_pi_window *)nsgtk_cw;
- sslcert_viewer_mouse_action(crtvrfy_win->ssl_data, mouse_state, x, y);
+ sslcert_viewer_mouse_action(pi_win->ssl_data, mouse_state, x, y);
return NSERROR_OK;
}
@@ -130,14 +130,14 @@ nsgtk_crtvrfy_mouse(struct nsgtk_corewindow *nsgtk_cw,
* \return NSERROR_OK on success otherwise appropriate error code
*/
static nserror
-nsgtk_crtvrfy_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
+nsgtk_pi_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
{
- struct nsgtk_crtvrfy_window *crtvrfy_win;
+ struct nsgtk_pi_window *pi_win;
/* technically degenerate container of */
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)nsgtk_cw;
+ pi_win = (struct nsgtk_pi_window *)nsgtk_cw;
- if (sslcert_viewer_keypress(crtvrfy_win->ssl_data, nskey)) {
+ if (sslcert_viewer_keypress(pi_win->ssl_data, nskey)) {
return NSERROR_OK;
}
return NSERROR_NOT_IMPLEMENTED;
@@ -151,19 +151,19 @@ nsgtk_crtvrfy_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
* \return NSERROR_OK on success otherwise appropriate error code
*/
static nserror
-nsgtk_crtvrfy_draw(struct nsgtk_corewindow *nsgtk_cw, struct rect *r)
+nsgtk_pi_draw(struct nsgtk_corewindow *nsgtk_cw, struct rect *r)
{
struct redraw_context ctx = {
.interactive = true,
.background_images = true,
.plot = &nsgtk_plotters
};
- struct nsgtk_crtvrfy_window *crtvrfy_win;
+ struct nsgtk_pi_window *pi_win;
/* technically degenerate container of */
- crtvrfy_win = (struct nsgtk_crtvrfy_window *)nsgtk_cw;
+ pi_win = (struct nsgtk_pi_window *)nsgtk_cw;
- sslcert_viewer_redraw(crtvrfy_win->ssl_data, 0, 0, r, &ctx);
+ sslcert_viewer_redraw(pi_win->ssl_data, 0, 0, r, &ctx);
return NSERROR_OK;
}
@@ -176,7 +176,7 @@ static nserror dummy_cb(bool proceed, void *pw)
/* exported interface documented in gtk/page_info.h */
nserror nsgtk_page_info(struct browser_window *bw)
{
- struct nsgtk_crtvrfy_window *ncwin;
+ struct nsgtk_pi_window *ncwin;
nserror res;
struct cert_chain *chain;
@@ -189,7 +189,7 @@ nserror nsgtk_page_info(struct browser_window *bw)
}
url = browser_window_access_url(bw);
- ncwin = malloc(sizeof(struct nsgtk_crtvrfy_window));
+ ncwin = malloc(sizeof(struct nsgtk_pi_window));
if (ncwin == NULL) {
return NSERROR_NOMEM;
}
@@ -219,27 +219,27 @@ nserror nsgtk_page_info(struct browser_window *bw)
/* make the delete event call our destructor */
g_signal_connect(G_OBJECT(ncwin->dlg),
"delete_event",
- G_CALLBACK(nsgtk_crtvrfy_delete_event),
+ G_CALLBACK(nsgtk_pi_delete_event),
ncwin);
/* accept button */
g_signal_connect(G_OBJECT(gtk_builder_get_object(ncwin->builder,
"sslaccept")),
"clicked",
- G_CALLBACK(nsgtk_crtvrfy_accept),
+ G_CALLBACK(nsgtk_pi_accept),
ncwin);
/* reject button */
g_signal_connect(G_OBJECT(gtk_builder_get_object(ncwin->builder,
"sslreject")),
"clicked",
- G_CALLBACK(nsgtk_crtvrfy_reject),
+ G_CALLBACK(nsgtk_pi_reject),
ncwin);
/* initialise GTK core window */
- ncwin->core.draw = nsgtk_crtvrfy_draw;
- ncwin->core.key = nsgtk_crtvrfy_key;
- ncwin->core.mouse = nsgtk_crtvrfy_mouse;
+ ncwin->core.draw = nsgtk_pi_draw;
+ ncwin->core.key = nsgtk_pi_key;
+ ncwin->core.mouse = nsgtk_pi_mouse;
res = nsgtk_corewindow_init(&ncwin->core);
if (res != NSERROR_OK) {
diff --git a/include/netsurf/browser_window.h b/include/netsurf/browser_window.h
index e8faa18..e463dfa 100644
--- a/include/netsurf/browser_window.h
+++ b/include/netsurf/browser_window.h
@@ -70,6 +70,7 @@ typedef enum {
PAGE_STATE_SECURE_OVERRIDE, /**< Secure load, but had to override */
PAGE_STATE_SECURE_ISSUES, /**< Secure load, but has insecure elements */
PAGE_STATE_SECURE, /**< Secure load */
+ PAGE_STATE__COUNT, /**< Count of number of valid page states */
} browser_window_page_info_state;
typedef enum {
@@ -774,7 +775,7 @@ nserror browser_window_console_log(struct browser_window *bw,
* \return The state of the browser window
*/
browser_window_page_info_state browser_window_get_page_info_state(
- struct browser_window *bw);
+ const struct browser_window *bw);
/**
* Request the current browser window SSL certificate chain.
@@ -789,4 +790,33 @@ browser_window_page_info_state browser_window_get_page_info_state(
*/
nserror browser_window_get_ssl_chain(struct browser_window *bw, struct cert_chain **chain);
+/**
+ * Get the number of cookies in use for the current page.
+ *
+ * \param bw A browser window.
+ * \return Number of cookies in use, or 0 on error.
+ */
+int browser_window_get_cookie_count(
+ const struct browser_window *bw);
+
+/**
+ * Open cookie viewer for the current page.
+ *
+ * \param bw A browser window.
+ * \return NSERROR_OK, or appropriate error otherwise.
+ */
+nserror browser_window_show_cookies(
+ const struct browser_window *bw);
+
+/**
+ * Show the certificate page for the current page.
+ *
+ * Does nothing for a page without certificates.
+ *
+ * \param bw A browser window.
+ * \return NSERROR_OK, or appropriate error otherwise.
+ */
+nserror browser_window_show_certificates(
+ const struct browser_window *bw);
+
#endif
diff --git a/include/netsurf/plot_style.h b/include/netsurf/plot_style.h
index f1b6172..bc86041 100644
--- a/include/netsurf/plot_style.h
+++ b/include/netsurf/plot_style.h
@@ -153,13 +153,16 @@ typedef struct plot_font_style {
(((((c0 & 0xff00ff) + (c1 & 0xff00ff)) >> 1) & 0xff00ff) | \
((((c0 & 0x00ff00) + (c1 & 0x00ff00)) >> 1) & 0x00ff00))
+/* Get the percieved lightness of the supplied colour, c0. */
+#define colour_lightness(c0) \
+ ((((c0 & 0x0000ff) * 77) >> 8) + \
+ (((c0 & 0x00ff00) * 151) >> 16) + \
+ (((c0 & 0xff0000) * 28) >> 24))
+
/* Choose either black or white, depending on which is furthest from the
* percieved lightness of the supplied colour, c0. */
#define colour_to_bw_furthest(c0) \
- ((((((c0 & 0x0000ff) * 77) >> 8) + \
- (((c0 & 0x00ff00) * 151) >> 16) + \
- (((c0 & 0xff0000) * 28) >> 24)) > \
- (0xff / 2)) ? 0x000000 : 0xffffff)
+ ((colour_lightness(c0) > (0xff / 2)) ? 0x000000 : 0xffffff)
/* Mix two colours according to the proportion given by p, where 0 <= p <= 255
* p = 0 gives result ==> c1, p = 255 gives result ==> c0 */
@@ -185,6 +188,39 @@ typedef struct plot_font_style {
#define blue_from_colour(c) \
((c >> 16) & 0xff)
+/** Colour components */
+enum plot_colour_component {
+ PLOT_COLOUR_COMPONENT_RED,
+ PLOT_COLOUR_COMPONENT_GREEN,
+ PLOT_COLOUR_COMPONENT_BLUE,
+ PLOT_COLOUR_COMPONENT_ALPHA,
+};
+
+/**
+ * Engorge a particular colour channel.
+ *
+ * \param[in] col The colour to engorge a component of.
+ * \param[in] dark Whether col is a dark colour.
+ * \param[in] comp Colour component to engorge.
+ */
+static inline colour colour_engorge_component(
+ colour col,
+ bool dark,
+ enum plot_colour_component comp)
+{
+ static const colour mask[PLOT_COLOUR_COMPONENT_ALPHA] = {
+ [PLOT_COLOUR_COMPONENT_RED] = 0x0000ff,
+ [PLOT_COLOUR_COMPONENT_GREEN] = 0x00ff00,
+ [PLOT_COLOUR_COMPONENT_BLUE] = 0xff0000,
+ };
+ colour d = dark ? darken_colour(col) : double_darken_colour(col);
+ colour l = dark ? double_lighten_colour(col) : lighten_colour(col);
+
+ assert(comp < PLOT_COLOUR_COMPONENT_ALPHA);
+
+ return (mask[comp] & l) | (~mask[comp] & d);
+}
+
/* global fill styles */
extern plot_style_t *plot_style_fill_white;
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-462-g8a83491
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/8a834918e30cd1924b2a3...
...commit http://git.netsurf-browser.org/netsurf.git/commit/8a834918e30cd1924b2a3a4...
...tree http://git.netsurf-browser.org/netsurf.git/tree/8a834918e30cd1924b2a3a484...
The branch, master has been updated
via 8a834918e30cd1924b2a3a4840a94445d55b5965 (commit)
from 338dd004f22b8c630ac1ff085b53ef34ab018ed4 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=8a834918e30cd1924b2...
commit 8a834918e30cd1924b2a3a4840a94445d55b5965
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
missed a certificate change on amiga frontend
diff --git a/frontends/amiga/sslcert.c b/frontends/amiga/sslcert.c
index 888729e..2d1c52e 100644
--- a/frontends/amiga/sslcert.c
+++ b/frontends/amiga/sslcert.c
@@ -298,10 +298,9 @@ ami_crtvrfy_create_window(struct ami_crtvrfy_window *crtvrfy_win)
/* exported interface documented in amiga/ssl_cert.h */
nserror ami_cert_verify(struct nsurl *url,
- const struct cert_chain *chain,
- unsigned long num,
- nserror (*cb)(bool proceed, void *pw),
- void *cbpw)
+ const struct cert_chain *chain,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
{
struct ami_crtvrfy_window *ncwin;
nserror res;
-----------------------------------------------------------------------
Summary of changes:
frontends/amiga/sslcert.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/frontends/amiga/sslcert.c b/frontends/amiga/sslcert.c
index 888729e..2d1c52e 100644
--- a/frontends/amiga/sslcert.c
+++ b/frontends/amiga/sslcert.c
@@ -298,10 +298,9 @@ ami_crtvrfy_create_window(struct ami_crtvrfy_window *crtvrfy_win)
/* exported interface documented in amiga/ssl_cert.h */
nserror ami_cert_verify(struct nsurl *url,
- const struct cert_chain *chain,
- unsigned long num,
- nserror (*cb)(bool proceed, void *pw),
- void *cbpw)
+ const struct cert_chain *chain,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
{
struct ami_crtvrfy_window *ncwin;
nserror res;
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-461-g338dd00
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/338dd004f22b8c630ac1f...
...commit http://git.netsurf-browser.org/netsurf.git/commit/338dd004f22b8c630ac1ff0...
...tree http://git.netsurf-browser.org/netsurf.git/tree/338dd004f22b8c630ac1ff085...
The branch, master has been updated
via 338dd004f22b8c630ac1ff085b53ef34ab018ed4 (commit)
from 7039973a1f886fb5651c94495a042eba5940a4d9 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=338dd004f22b8c630ac...
commit 338dd004f22b8c630ac1ff085b53ef34ab018ed4
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
fix windows and amiga frontends to cope with certificate chain changes
diff --git a/frontends/amiga/sslcert.h b/frontends/amiga/sslcert.h
index 392989f..114d557 100644
--- a/frontends/amiga/sslcert.h
+++ b/frontends/amiga/sslcert.h
@@ -19,7 +19,7 @@
#ifndef AMIGA_SSLCERT_H
#define AMIGA_SSLCERT_H
struct nsurl;
-struct ssl_cert_info;
+struct cert_chain;
/**
* Prompt the user to verify a certificate with issues.
@@ -32,7 +32,7 @@ struct ssl_cert_info;
* \return NSERROR_OK or error code if prompt creation failed.
*/
nserror ami_cert_verify(struct nsurl *url,
- const struct ssl_cert_info *certs, unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw), void *cbpw);
#endif
diff --git a/frontends/windows/ssl_cert.c b/frontends/windows/ssl_cert.c
index 4db0616..1e78871 100644
--- a/frontends/windows/ssl_cert.c
+++ b/frontends/windows/ssl_cert.c
@@ -170,8 +170,7 @@ nsw32_sslcert_viewer_close(struct nsw32_corewindow *nsw32_cw)
/* exported interface documented in nsw32/ssl_cert.h */
nserror nsw32_cert_verify(struct nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
@@ -184,8 +183,7 @@ nserror nsw32_cert_verify(struct nsurl *url,
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
- &ncwin->ssl_data);
+ res = sslcert_viewer_create_session_data(url, cb, cbpw, chain, &ncwin->ssl_data);
if (res != NSERROR_OK) {
free(ncwin);
return res;
diff --git a/frontends/windows/ssl_cert.h b/frontends/windows/ssl_cert.h
index 6c1f041..eebfb06 100644
--- a/frontends/windows/ssl_cert.h
+++ b/frontends/windows/ssl_cert.h
@@ -25,7 +25,7 @@
#define NETSURF_WINDOWS_SSL_CERT_H 1
struct nsurl;
-struct ssl_cert_info;
+struct cert_chain;
/**
* Prompt the user to verify a certificate with issuse.
@@ -37,7 +37,7 @@ struct ssl_cert_info;
* \param cbpw Context pointer passed to cb
* \return NSERROR_OK or error code if prompt creation failed.
*/
-nserror nsw32_cert_verify(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+nserror nsw32_cert_verify(struct nsurl *url, const struct cert_chain *certs, nserror (*cb)(bool proceed, void *pw), void *cbpw);
/**
* Create the ssl viewer window class.
-----------------------------------------------------------------------
Summary of changes:
frontends/amiga/sslcert.h | 4 ++--
frontends/windows/ssl_cert.c | 6 ++----
frontends/windows/ssl_cert.h | 4 ++--
3 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/frontends/amiga/sslcert.h b/frontends/amiga/sslcert.h
index 392989f..114d557 100644
--- a/frontends/amiga/sslcert.h
+++ b/frontends/amiga/sslcert.h
@@ -19,7 +19,7 @@
#ifndef AMIGA_SSLCERT_H
#define AMIGA_SSLCERT_H
struct nsurl;
-struct ssl_cert_info;
+struct cert_chain;
/**
* Prompt the user to verify a certificate with issues.
@@ -32,7 +32,7 @@ struct ssl_cert_info;
* \return NSERROR_OK or error code if prompt creation failed.
*/
nserror ami_cert_verify(struct nsurl *url,
- const struct ssl_cert_info *certs, unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw), void *cbpw);
#endif
diff --git a/frontends/windows/ssl_cert.c b/frontends/windows/ssl_cert.c
index 4db0616..1e78871 100644
--- a/frontends/windows/ssl_cert.c
+++ b/frontends/windows/ssl_cert.c
@@ -170,8 +170,7 @@ nsw32_sslcert_viewer_close(struct nsw32_corewindow *nsw32_cw)
/* exported interface documented in nsw32/ssl_cert.h */
nserror nsw32_cert_verify(struct nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
@@ -184,8 +183,7 @@ nserror nsw32_cert_verify(struct nsurl *url,
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
- &ncwin->ssl_data);
+ res = sslcert_viewer_create_session_data(url, cb, cbpw, chain, &ncwin->ssl_data);
if (res != NSERROR_OK) {
free(ncwin);
return res;
diff --git a/frontends/windows/ssl_cert.h b/frontends/windows/ssl_cert.h
index 6c1f041..eebfb06 100644
--- a/frontends/windows/ssl_cert.h
+++ b/frontends/windows/ssl_cert.h
@@ -25,7 +25,7 @@
#define NETSURF_WINDOWS_SSL_CERT_H 1
struct nsurl;
-struct ssl_cert_info;
+struct cert_chain;
/**
* Prompt the user to verify a certificate with issuse.
@@ -37,7 +37,7 @@ struct ssl_cert_info;
* \param cbpw Context pointer passed to cb
* \return NSERROR_OK or error code if prompt creation failed.
*/
-nserror nsw32_cert_verify(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+nserror nsw32_cert_verify(struct nsurl *url, const struct cert_chain *certs, nserror (*cb)(bool proceed, void *pw), void *cbpw);
/**
* Create the ssl viewer window class.
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-460-g7039973
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/7039973a1f886fb5651c9...
...commit http://git.netsurf-browser.org/netsurf.git/commit/7039973a1f886fb5651c944...
...tree http://git.netsurf-browser.org/netsurf.git/tree/7039973a1f886fb5651c94495...
The branch, master has been updated
via 7039973a1f886fb5651c94495a042eba5940a4d9 (commit)
from 0c34d06494afe217ace7460c66df800d457dd2e8 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=7039973a1f886fb5651...
commit 7039973a1f886fb5651c94495a042eba5940a4d9
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
fix frontends to cope with certificate chain update
diff --git a/frontends/amiga/sslcert.c b/frontends/amiga/sslcert.c
index 854d6b0..888729e 100644
--- a/frontends/amiga/sslcert.c
+++ b/frontends/amiga/sslcert.c
@@ -298,7 +298,7 @@ ami_crtvrfy_create_window(struct ami_crtvrfy_window *crtvrfy_win)
/* exported interface documented in amiga/ssl_cert.h */
nserror ami_cert_verify(struct nsurl *url,
- const struct ssl_cert_info *certs,
+ const struct cert_chain *chain,
unsigned long num,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
@@ -346,8 +346,7 @@ nserror ami_cert_verify(struct nsurl *url,
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
- &ncwin->ssl_data);
+ res = sslcert_viewer_create_session_data(url, cb, cbpw, chain, &ncwin->ssl_data);
if (res != NSERROR_OK) {
ami_utf8_free(ncwin->core.wintitle);
ami_utf8_free(ncwin->sslerr);
diff --git a/frontends/atari/gui.c b/frontends/atari/gui.c
index a5df8fc..bc4c0ff 100644
--- a/frontends/atari/gui.c
+++ b/frontends/atari/gui.c
@@ -762,8 +762,8 @@ static void gui_set_clipboard(const char *buffer, size_t length,
}
static nserror
-gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
- unsigned long num, nserror (*cb)(bool proceed, void *pw),
+gui_cert_verify(nsurl *url, const struct cert_chain *chain,
+ nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
struct sslcert_session_data *data;
@@ -781,8 +781,7 @@ gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
cb(false, cbpw);
} else if(b == 3) {
// Inspect
- sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
- &data);
+ sslcert_viewer_create_session_data(url, cb, cbpw, chain, &data);
atari_sslcert_viewer_open(data);
}
return NSERROR_OK;
diff --git a/frontends/monkey/cert.c b/frontends/monkey/cert.c
index ddcd113..3ec2fbe 100644
--- a/frontends/monkey/cert.c
+++ b/frontends/monkey/cert.c
@@ -40,8 +40,8 @@ static uint32_t cert_ctr = 0;
nserror
gui_cert_verify(nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num, nserror (*cb)(bool proceed, void *pw),
+ const struct cert_chain *chain,
+ nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
struct monkey_cert *mcrt_ctx;
diff --git a/frontends/monkey/cert.h b/frontends/monkey/cert.h
index 56feea7..ff499ed 100644
--- a/frontends/monkey/cert.h
+++ b/frontends/monkey/cert.h
@@ -19,11 +19,11 @@
#ifndef NETSURF_MONKEY_CERT_H
#define NETSURF_MONKEY_CERT_H
-struct ssl_cert_info;
+struct cert_chain;
-nserror gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
- unsigned long num, nserror (*cb)(bool proceed, void *pw),
- void *cbpw);
+nserror gui_cert_verify(nsurl *url, const struct cert_chain *certs,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw);
void monkey_sslcert_handle_command(int argc, char **argv);
diff --git a/frontends/riscos/sslcert.c b/frontends/riscos/sslcert.c
index 961e483..815f7e9 100644
--- a/frontends/riscos/sslcert.c
+++ b/frontends/riscos/sslcert.c
@@ -312,8 +312,7 @@ cert_mouse(struct ro_corewindow *ro_cw,
/* exported interface documented in riscos/sslcert.h */
nserror
gui_cert_verify(nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
@@ -327,7 +326,7 @@ gui_cert_verify(nsurl *url,
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
+ res = sslcert_viewer_create_session_data(url, cb, cbpw, chain,
&ncwin->ssl_data);
if (res != NSERROR_OK) {
free(ncwin);
diff --git a/frontends/riscos/sslcert.h b/frontends/riscos/sslcert.h
index 09607f0..613db1a 100644
--- a/frontends/riscos/sslcert.h
+++ b/frontends/riscos/sslcert.h
@@ -41,7 +41,7 @@ void ro_gui_cert_initialise(void);
* \param cb Callback upon user decision.
* \param cbpw Context pointer passed to cb
*/
-nserror gui_cert_verify(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+nserror gui_cert_verify(struct nsurl *url, const struct cert_chain *chain, nserror (*cb)(bool proceed, void *pw), void *cbpw);
#endif
-----------------------------------------------------------------------
Summary of changes:
frontends/amiga/sslcert.c | 5 ++---
frontends/atari/gui.c | 7 +++----
frontends/monkey/cert.c | 4 ++--
frontends/monkey/cert.h | 8 ++++----
frontends/riscos/sslcert.c | 5 ++---
frontends/riscos/sslcert.h | 2 +-
6 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/frontends/amiga/sslcert.c b/frontends/amiga/sslcert.c
index 854d6b0..888729e 100644
--- a/frontends/amiga/sslcert.c
+++ b/frontends/amiga/sslcert.c
@@ -298,7 +298,7 @@ ami_crtvrfy_create_window(struct ami_crtvrfy_window *crtvrfy_win)
/* exported interface documented in amiga/ssl_cert.h */
nserror ami_cert_verify(struct nsurl *url,
- const struct ssl_cert_info *certs,
+ const struct cert_chain *chain,
unsigned long num,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
@@ -346,8 +346,7 @@ nserror ami_cert_verify(struct nsurl *url,
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
- &ncwin->ssl_data);
+ res = sslcert_viewer_create_session_data(url, cb, cbpw, chain, &ncwin->ssl_data);
if (res != NSERROR_OK) {
ami_utf8_free(ncwin->core.wintitle);
ami_utf8_free(ncwin->sslerr);
diff --git a/frontends/atari/gui.c b/frontends/atari/gui.c
index a5df8fc..bc4c0ff 100644
--- a/frontends/atari/gui.c
+++ b/frontends/atari/gui.c
@@ -762,8 +762,8 @@ static void gui_set_clipboard(const char *buffer, size_t length,
}
static nserror
-gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
- unsigned long num, nserror (*cb)(bool proceed, void *pw),
+gui_cert_verify(nsurl *url, const struct cert_chain *chain,
+ nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
struct sslcert_session_data *data;
@@ -781,8 +781,7 @@ gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
cb(false, cbpw);
} else if(b == 3) {
// Inspect
- sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
- &data);
+ sslcert_viewer_create_session_data(url, cb, cbpw, chain, &data);
atari_sslcert_viewer_open(data);
}
return NSERROR_OK;
diff --git a/frontends/monkey/cert.c b/frontends/monkey/cert.c
index ddcd113..3ec2fbe 100644
--- a/frontends/monkey/cert.c
+++ b/frontends/monkey/cert.c
@@ -40,8 +40,8 @@ static uint32_t cert_ctr = 0;
nserror
gui_cert_verify(nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num, nserror (*cb)(bool proceed, void *pw),
+ const struct cert_chain *chain,
+ nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
struct monkey_cert *mcrt_ctx;
diff --git a/frontends/monkey/cert.h b/frontends/monkey/cert.h
index 56feea7..ff499ed 100644
--- a/frontends/monkey/cert.h
+++ b/frontends/monkey/cert.h
@@ -19,11 +19,11 @@
#ifndef NETSURF_MONKEY_CERT_H
#define NETSURF_MONKEY_CERT_H
-struct ssl_cert_info;
+struct cert_chain;
-nserror gui_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
- unsigned long num, nserror (*cb)(bool proceed, void *pw),
- void *cbpw);
+nserror gui_cert_verify(nsurl *url, const struct cert_chain *certs,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw);
void monkey_sslcert_handle_command(int argc, char **argv);
diff --git a/frontends/riscos/sslcert.c b/frontends/riscos/sslcert.c
index 961e483..815f7e9 100644
--- a/frontends/riscos/sslcert.c
+++ b/frontends/riscos/sslcert.c
@@ -312,8 +312,7 @@ cert_mouse(struct ro_corewindow *ro_cw,
/* exported interface documented in riscos/sslcert.h */
nserror
gui_cert_verify(nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
@@ -327,7 +326,7 @@ gui_cert_verify(nsurl *url,
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, cb, cbpw, certs,
+ res = sslcert_viewer_create_session_data(url, cb, cbpw, chain,
&ncwin->ssl_data);
if (res != NSERROR_OK) {
free(ncwin);
diff --git a/frontends/riscos/sslcert.h b/frontends/riscos/sslcert.h
index 09607f0..613db1a 100644
--- a/frontends/riscos/sslcert.h
+++ b/frontends/riscos/sslcert.h
@@ -41,7 +41,7 @@ void ro_gui_cert_initialise(void);
* \param cb Callback upon user decision.
* \param cbpw Context pointer passed to cb
*/
-nserror gui_cert_verify(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+nserror gui_cert_verify(struct nsurl *url, const struct cert_chain *chain, nserror (*cb)(bool proceed, void *pw), void *cbpw);
#endif
--
NetSurf Browser
2 years, 11 months
netsurf: branch master updated. release/3.9-459-g0c34d06
by NetSurf Browser Project
Gitweb links:
...log http://git.netsurf-browser.org/netsurf.git/shortlog/0c34d06494afe217ace74...
...commit http://git.netsurf-browser.org/netsurf.git/commit/0c34d06494afe217ace7460...
...tree http://git.netsurf-browser.org/netsurf.git/tree/0c34d06494afe217ace7460c6...
The branch, master has been updated
via 0c34d06494afe217ace7460c66df800d457dd2e8 (commit)
via 214478fc15a2b9ee67e19e08cf27657c0157037a (commit)
from a653e1e86eb5af1621de97603c33222315d2d2c3 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=0c34d06494afe217ace...
commit 0c34d06494afe217ace7460c66df800d457dd2e8
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
Keep the complete certificate chain from a fetch
Instead of extracting information from the X509 certificate chain in
the fetcher the entire chain is propagated in Distinguished Encoding
Rules (DER) format.
This allows all the information contained in a certificate chain to be
retained which can subsequently be presented to the user
diff --git a/content/content.h b/content/content.h
index 144a698..f8f8d32 100644
--- a/content/content.h
+++ b/content/content.h
@@ -44,7 +44,7 @@ struct object_params;
struct rect;
struct redraw_context;
struct llcache_query_msg;
-struct ssl_cert_info;
+struct cert_chain;
/** Status of a content */
typedef enum {
@@ -118,10 +118,7 @@ union content_msg_data {
* CONTENT_MSG_SSL_CERTS - The certificate chain from the
* underlying fetch
*/
- struct {
- const struct ssl_cert_info *certs; /**< The chain */
- size_t num; /**< The number of certs in the chain */
- } certs;
+ const struct cert_chain *chain;
/**
* CONTENT_MSG_ERROR - Error from content or underlying fetch
diff --git a/content/fetch.h b/content/fetch.h
index 9a81ece..9e80b26 100644
--- a/content/fetch.h
+++ b/content/fetch.h
@@ -75,10 +75,7 @@ typedef struct fetch_msg {
const char *realm;
} auth;
- struct {
- const struct ssl_cert_info *certs;
- size_t num_certs;
- } certs;
+ const struct cert_chain *chain;
} data;
} fetch_msg;
diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c
index 0be33ae..83e92d8 100644
--- a/content/fetchers/curl.c
+++ b/content/fetchers/curl.c
@@ -149,7 +149,7 @@ struct curl_fetch_info {
struct curl_httppost *post_multipart; /**< Multipart post data, or 0. */
uint64_t last_progress_update; /**< Time of last progress update */
int cert_depth; /**< deepest certificate in use */
- struct cert_info cert_data[MAX_SSL_CERTS]; /**< HTTPS certificate data */
+ struct cert_info cert_data[MAX_CERT_DEPTH]; /**< HTTPS certificate data */
};
/** curl handle cache entry */
@@ -471,128 +471,37 @@ failed:
static void
fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
{
- int depth;
+ size_t depth;
BIO *mem;
- BUF_MEM *buf;
- const ASN1_INTEGER *asn1_num;
- BIGNUM *bignum;
- struct ssl_cert_info ssl_certs[MAX_SSL_CERTS];
+ BUF_MEM *buf[MAX_CERT_DEPTH];
+ struct cert_chain chain;
fetch_msg msg;
- struct cert_info *certs = f->cert_data;
- memset(ssl_certs, 0, sizeof(ssl_certs));
+ struct cert_info *certs;
+
+ memset(&chain, 0, sizeof(chain));
+
+ certs = f->cert_data;
+ chain.depth = f->cert_depth + 1; /* 0 indexed certificate depth */
- for (depth = 0; depth <= f->cert_depth; depth++) {
+ for (depth = 0; depth < chain.depth; depth++) {
if (certs[depth].cert == NULL) {
/* This certificate is missing, skip it */
- ssl_certs[depth].err = SSL_CERT_ERR_CERT_MISSING;
+ chain.certs[depth].err = SSL_CERT_ERR_CERT_MISSING;
continue;
}
- /* get certificate version */
- ssl_certs[depth].version = X509_get_version(certs[depth].cert);
-
- /* not before date */
- mem = BIO_new(BIO_s_mem());
- ASN1_TIME_print(mem, X509_get_notBefore(certs[depth].cert));
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].not_before,
- buf->data,
- min(sizeof(ssl_certs[depth].not_before) - 1,
- (unsigned)buf->length));
- ssl_certs[depth].not_before[min(sizeof(ssl_certs[depth].not_before) - 1,
- (unsigned)buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* not after date */
- mem = BIO_new(BIO_s_mem());
- ASN1_TIME_print(mem,
- X509_get_notAfter(certs[depth].cert));
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].not_after,
- buf->data,
- min(sizeof(ssl_certs[depth].not_after) - 1,
- (unsigned)buf->length));
- ssl_certs[depth].not_after[min(sizeof(ssl_certs[depth].not_after) - 1,
- (unsigned)buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* signature type */
- ssl_certs[depth].sig_type =
- X509_get_signature_type(certs[depth].cert);
-
- /* serial number */
- asn1_num = X509_get_serialNumber(certs[depth].cert);
- if (asn1_num != NULL) {
- bignum = ASN1_INTEGER_to_BN(asn1_num, NULL);
- if (bignum != NULL) {
- char *tmp = BN_bn2hex(bignum);
- if (tmp != NULL) {
- strncpy(ssl_certs[depth].serialnum,
- tmp,
- sizeof(ssl_certs[depth].serialnum));
- ssl_certs[depth].serialnum[sizeof(ssl_certs[depth].serialnum)-1] = '\0';
- OPENSSL_free(tmp);
- }
- BN_free(bignum);
- bignum = NULL;
- }
- }
-
- /* issuer name */
- mem = BIO_new(BIO_s_mem());
- X509_NAME_print_ex(mem,
- X509_get_issuer_name(certs[depth].cert),
- 0, XN_FLAG_SEP_CPLUS_SPC |
- XN_FLAG_DN_REV | XN_FLAG_FN_NONE);
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].issuer,
- buf->data,
- min(sizeof(ssl_certs[depth].issuer) - 1,
- (unsigned) buf->length));
- ssl_certs[depth].issuer[min(sizeof(ssl_certs[depth].issuer) - 1,
- (unsigned) buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* subject */
- mem = BIO_new(BIO_s_mem());
- X509_NAME_print_ex(mem,
- X509_get_subject_name(certs[depth].cert),
- 0,
- XN_FLAG_SEP_CPLUS_SPC |
- XN_FLAG_DN_REV |
- XN_FLAG_FN_NONE);
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].subject,
- buf->data,
- min(sizeof(ssl_certs[depth].subject) - 1,
- (unsigned)buf->length));
- ssl_certs[depth].subject[min(sizeof(ssl_certs[depth].subject) - 1,
- (unsigned) buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* type of certificate */
- ssl_certs[depth].cert_type =
- X509_certificate_type(certs[depth].cert,
- X509_get_pubkey(certs[depth].cert));
-
/* error code (if any) */
switch (certs[depth].err) {
case X509_V_OK:
- ssl_certs[depth].err = SSL_CERT_ERR_OK;
+ chain.certs[depth].err = SSL_CERT_ERR_OK;
break;
+
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
/* fallthrough */
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
- ssl_certs[depth].err = SSL_CERT_ERR_BAD_ISSUER;
+ chain.certs[depth].err = SSL_CERT_ERR_BAD_ISSUER;
break;
+
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
/* fallthrough */
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
@@ -600,41 +509,66 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
/* fallthrough */
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
- ssl_certs[depth].err = SSL_CERT_ERR_BAD_SIG;
+ chain.certs[depth].err = SSL_CERT_ERR_BAD_SIG;
break;
+
case X509_V_ERR_CERT_NOT_YET_VALID:
/* fallthrough */
case X509_V_ERR_CRL_NOT_YET_VALID:
- ssl_certs[depth].err = SSL_CERT_ERR_TOO_YOUNG;
+ chain.certs[depth].err = SSL_CERT_ERR_TOO_YOUNG;
break;
+
case X509_V_ERR_CERT_HAS_EXPIRED:
/* fallthrough */
case X509_V_ERR_CRL_HAS_EXPIRED:
- ssl_certs[depth].err = SSL_CERT_ERR_TOO_OLD;
+ chain.certs[depth].err = SSL_CERT_ERR_TOO_OLD;
break;
+
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- ssl_certs[depth].err = SSL_CERT_ERR_SELF_SIGNED;
+ chain.certs[depth].err = SSL_CERT_ERR_SELF_SIGNED;
break;
+
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
- ssl_certs[depth].err = SSL_CERT_ERR_CHAIN_SELF_SIGNED;
+ chain.certs[depth].err = SSL_CERT_ERR_CHAIN_SELF_SIGNED;
break;
+
case X509_V_ERR_CERT_REVOKED:
- ssl_certs[depth].err = SSL_CERT_ERR_REVOKED;
+ chain.certs[depth].err = SSL_CERT_ERR_REVOKED;
break;
+
case X509_V_ERR_HOSTNAME_MISMATCH:
- ssl_certs[depth].err = SSL_CERT_ERR_HOSTNAME_MISMATCH;
+ chain.certs[depth].err = SSL_CERT_ERR_HOSTNAME_MISMATCH;
break;
+
default:
- ssl_certs[depth].err = SSL_CERT_ERR_UNKNOWN;
+ chain.certs[depth].err = SSL_CERT_ERR_UNKNOWN;
break;
}
+
+ /*
+ * get certificate in Distinguished Encoding Rules (DER) format.
+ */
+ mem = BIO_new(BIO_s_mem());
+ i2d_X509_bio(mem, certs[depth].cert);
+ BIO_get_mem_ptr(mem, &buf[depth]);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+
+ chain.certs[depth].der = (uint8_t *)buf[depth]->data;
+ chain.certs[depth].der_length = buf[depth]->length;
}
msg.type = FETCH_CERTS;
- msg.data.certs.certs = ssl_certs;
- msg.data.certs.num_certs = depth;
+ msg.data.chain = &chain;
fetch_send_callback(&msg, f->fetch_handle);
+
+ /* release the openssl memory buffer */
+ for (depth = 0; depth < chain.depth; depth++) {
+ if (buf[depth] != NULL) {
+ BUF_MEM_free(buf[depth]);
+ }
+ }
}
@@ -667,7 +601,7 @@ fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx)
fetch = X509_STORE_CTX_get_app_data(x509_ctx);
/* certificate chain is excessively deep so fail verification */
- if (depth >= MAX_SSL_CERTS) {
+ if (depth >= MAX_CERT_DEPTH) {
X509_STORE_CTX_set_error(x509_ctx,
X509_V_ERR_CERT_CHAIN_TOO_LONG);
return 0;
@@ -1098,8 +1032,11 @@ static void fetch_curl_free(void *vf)
curl_formfree(f->post_multipart);
}
- for (i = 0; i < MAX_SSL_CERTS && f->cert_data[i].cert; i++) {
- ns_X509_free(f->cert_data[i].cert);
+ /* free certificate data */
+ for (i = 0; i < MAX_CERT_DEPTH; i++) {
+ if (f->cert_data[i].cert != NULL) {
+ ns_X509_free(f->cert_data[i].cert);
+ }
}
free(f);
diff --git a/content/hlcache.c b/content/hlcache.c
index ec011ec..23dbc57 100644
--- a/content/hlcache.c
+++ b/content/hlcache.c
@@ -423,8 +423,10 @@ static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
* \param pw Pointer to client-specific data
* \return NSERROR_OK on success, appropriate error otherwise
*/
-static nserror hlcache_llcache_callback(llcache_handle *handle,
- const llcache_event *event, void *pw)
+static nserror
+hlcache_llcache_callback(llcache_handle *handle,
+ const llcache_event *event,
+ void *pw)
{
hlcache_retrieval_ctx *ctx = pw;
lwc_string *effective_type = NULL;
@@ -439,8 +441,7 @@ static nserror hlcache_llcache_callback(llcache_handle *handle,
hlcache_event hlevent;
hlevent.type = CONTENT_MSG_SSL_CERTS;
- hlevent.data.certs.certs = event->data.certs.certs;
- hlevent.data.certs.num = event->data.certs.num;
+ hlevent.data.chain = event->data.chain;
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
}
diff --git a/content/llcache.c b/content/llcache.c
index 8a71f18..3a75bf9 100644
--- a/content/llcache.c
+++ b/content/llcache.c
@@ -36,6 +36,7 @@
#include <string.h>
#include <strings.h>
#include <nsutils/time.h>
+#include <nsutils/base64.h>
#include "netsurf/inttypes.h"
#include "utils/config.h"
@@ -175,8 +176,7 @@ struct llcache_object {
size_t source_len; /**< Byte length of source data */
size_t source_alloc; /**< Allocated size of source buffer */
- size_t ssl_cert_count; /**< The number of SSL certificates stored */
- struct ssl_cert_info *ssl_certs; /**< SSL certificate information if count is non-zero */
+ struct cert_chain *chain; /**< Certificate chain from the fetch */
llcache_store_state store_state; /**< where the data for the object is stored */
@@ -969,11 +969,7 @@ static nserror llcache_object_destroy(llcache_object *object)
NSLOG(llcache, DEBUG, "Destroying object %p, %s", object,
nsurl_access(object->url));
- if (object->ssl_cert_count != 0) {
- free(object->ssl_certs);
- object->ssl_certs = NULL;
- object->ssl_cert_count = 0;
- }
+ cert_chain_free(object->chain);
if (object->source_data != NULL) {
if (object->store_state == LLCACHE_STATE_DISC) {
@@ -1240,6 +1236,13 @@ llcache_serialise_metadata(llcache_object *object,
char *op;
unsigned int hloop;
int use;
+ size_t cert_chain_depth;
+
+ if (object->chain != NULL) {
+ cert_chain_depth = object->chain->depth;
+ } else {
+ cert_chain_depth = 0;
+ }
allocsize = 10 + 1; /* object length */
@@ -1251,22 +1254,19 @@ llcache_serialise_metadata(llcache_object *object,
allocsize += 10 + 1; /* space for number of header entries */
- allocsize += 10 + 1; /* space for number of SSL certificates */
-
- allocsize += nsurl_length(object->url) + 1;
-
for (hloop = 0 ; hloop < object->num_headers ; hloop++) {
allocsize += strlen(object->headers[hloop].name) + 1;
allocsize += strlen(object->headers[hloop].value) + 1;
}
- for (hloop = 0; hloop < object->ssl_cert_count; hloop++) {
- allocsize += (10 + 1) * 4; /* version, sig_type, cert_type, err */
- allocsize += strlen(object->ssl_certs[hloop].not_before) + 1;
- allocsize += strlen(object->ssl_certs[hloop].not_after) + 1;
- allocsize += strlen(object->ssl_certs[hloop].serialnum) + 1;
- allocsize += strlen(object->ssl_certs[hloop].issuer) + 1;
- allocsize += strlen(object->ssl_certs[hloop].subject) + 1;
+ allocsize += nsurl_length(object->url) + 1;
+
+ /* space for number of DER formatted certificates */
+ allocsize += 10 + 1;
+
+ for (hloop = 0; hloop < cert_chain_depth; hloop++) {
+ allocsize += 10 + 1; /* error status */
+ allocsize += 4 * ((object->chain->certs[hloop].der_length + 2) / 3);
}
data = malloc(allocsize);
@@ -1351,8 +1351,8 @@ llcache_serialise_metadata(llcache_object *object,
datasize -= use;
}
- /* number of ssl certificates */
- use = snprintf(op, datasize, "%" PRIsizet, object->ssl_cert_count);
+ /* number of DER formatted ssl certificates */
+ use = snprintf(op, datasize, "%" PRIsizet, cert_chain_depth);
if (use < 0) {
goto operror;
}
@@ -1363,80 +1363,13 @@ llcache_serialise_metadata(llcache_object *object,
datasize -= use;
/* SSL certificates */
- for (hloop = 0; hloop < object->ssl_cert_count; hloop++) {
- struct ssl_cert_info *cert = &(object->ssl_certs[hloop]);
- /* Certificate version */
- use = snprintf(op, datasize, "%ld", cert->version);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* not_before */
- use = snprintf(op, datasize, "%s", cert->not_before);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* not_after */
- use = snprintf(op, datasize, "%s", cert->not_after);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* Signature type */
- use = snprintf(op, datasize, "%d", cert->sig_type);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* serialnum */
- use = snprintf(op, datasize, "%s", cert->serialnum);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* issuer */
- use = snprintf(op, datasize, "%s", cert->issuer);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* subject */
- use = snprintf(op, datasize, "%s", cert->subject);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* Certificate type */
- use = snprintf(op, datasize, "%d", cert->cert_type);
+ for (hloop = 0; hloop < cert_chain_depth; hloop++) {
+ size_t output_length;
+ nsuerror res;
+
+ /* Certificate error code */
+ use = snprintf(op, datasize, "%d",
+ (int)(object->chain->certs[hloop].err));
if (use < 0) {
goto operror;
}
@@ -1445,14 +1378,26 @@ llcache_serialise_metadata(llcache_object *object,
goto overflow;
op += use;
datasize -= use;
- /* Certificate error code */
- use = snprintf(op, datasize, "%d", (int)(cert->err));
- if (use < 0) {
- goto operror;
+
+ /* DER certificate data in base64 encoding */
+ if (object->chain->certs[hloop].der != NULL) {
+ output_length = datasize;
+ res = nsu_base64_encode(
+ object->chain->certs[hloop].der,
+ object->chain->certs[hloop].der_length,
+ (uint8_t *)op,
+ &output_length);
+ if (res != NSUERROR_OK) {
+ goto operror;
+ }
+ use = output_length;
+ } else {
+ use = 0;
}
- use++; /* does not count the null */
+ use++; /* allow for null */
if (use > datasize)
goto overflow;
+ *(op + output_length) = 0;
op += use;
datasize -= use;
}
@@ -1510,7 +1455,7 @@ llcache_process_metadata(llcache_object *object)
size_t num_headers;
size_t hloop;
size_t ssl_cert_count = 0;
- struct ssl_cert_info *ssl_certs = NULL;
+ struct cert_chain *chain = NULL;
NSLOG(llcache, INFO, "Retrieving metadata");
@@ -1642,7 +1587,7 @@ llcache_process_metadata(llcache_object *object)
goto skip_ssl_certificates;
}
- /* Next line is the number of SSL certificates*/
+ /* Next line is the number of DER base64 encoded certificates */
line++;
ln += lnsize + 1;
lnsize = strlen(ln);
@@ -1657,72 +1602,20 @@ llcache_process_metadata(llcache_object *object)
goto skip_ssl_certificates;
}
- ssl_certs = calloc(sizeof(struct ssl_cert_info), ssl_cert_count);
- if (ssl_certs == NULL) {
- res = NSERROR_NOMEM;
+ if (ssl_cert_count > MAX_CERT_DEPTH) {
+ res = NSERROR_INVALID;
+ goto format_error;
+ }
+
+ res = cert_chain_alloc(ssl_cert_count, &chain);
+ if (res != NSERROR_OK) {
goto format_error;
}
for (hloop = 0; hloop < ssl_cert_count; hloop++) {
- struct ssl_cert_info *cert = &ssl_certs[hloop];
int errcode;
- /* Certificate version */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%ld", &cert->version) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
- /* Not before */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->not_before, ln, lnsize);
- /* Not after */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->not_after, ln, lnsize);
- /* Signature type */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%d", &cert->sig_type) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
- /* Serial Number */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->serialnum, ln, lnsize);
- /* issuer */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->issuer, ln, lnsize);
- /* subject */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->subject, ln, lnsize);
- /* Certificate type */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%d", &cert->cert_type) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
+ nsuerror nsures;
+
/* Certificate error code */
line++;
ln += lnsize + 1;
@@ -1735,9 +1628,25 @@ llcache_process_metadata(llcache_object *object)
if (errcode < SSL_CERT_ERR_OK ||
errcode > SSL_CERT_ERR_MAX_KNOWN) {
/* Error with the cert code, assume UNKNOWN */
- cert->err = SSL_CERT_ERR_UNKNOWN;
+ chain->certs[hloop].err = SSL_CERT_ERR_UNKNOWN;
} else {
- cert->err = (ssl_cert_err)errcode;
+ chain->certs[hloop].err = (ssl_cert_err)errcode;
+ }
+
+ /* base64 encoded DER certificate data */
+ line++;
+ ln += lnsize + 1;
+ lnsize = strlen(ln);
+ remaining -= lnsize + 1;
+ if (lnsize > 0) {
+ nsures = nsu_base64_decode_alloc((const uint8_t *)ln,
+ lnsize,
+ &chain->certs[hloop].der,
+ &chain->certs[hloop].der_length);
+ if (nsures != NSUERROR_OK) {
+ res = NSERROR_NOMEM;
+ goto format_error;
+ }
}
}
@@ -1754,8 +1663,7 @@ skip_ssl_certificates:
object->cache.res_time = response_time;
object->cache.fin_time = completion_time;
- object->ssl_cert_count = ssl_cert_count;
- object->ssl_certs = ssl_certs;
+ object->chain = chain;
/* object stored in backing store */
object->store_state = LLCACHE_STATE_DISC;
@@ -1768,9 +1676,7 @@ format_error:
line, res);
guit->llcache->release(object->url, BACKING_STORE_META);
- if (ssl_certs != NULL) {
- free(ssl_certs);
- }
+ cert_chain_free(chain);
return res;
}
@@ -3173,21 +3079,15 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p)
case FETCH_CERTS:
/* Certificate information from the fetch */
- /* Persist the data onto our object */
- object->ssl_certs = calloc(sizeof(struct ssl_cert_info),
- msg->data.certs.num_certs);
- if (object->ssl_certs != NULL) {
- object->ssl_cert_count = msg->data.certs.num_certs;
- memcpy(object->ssl_certs, msg->data.certs.certs,
- sizeof(struct ssl_cert_info) * object->ssl_cert_count);
- }
-
- /* Now pass on the event */
- event.type = LLCACHE_EVENT_GOT_CERTS;
- event.data.certs.certs = msg->data.certs.certs;
- event.data.certs.num = msg->data.certs.num_certs;
+ /* Persist the chain onto our object */
+ error = cert_chain_dup(msg->data.chain, &object->chain);
+ if (error != NSERROR_OK) {
+ /* Now pass on the event */
+ event.type = LLCACHE_EVENT_GOT_CERTS;
+ event.data.chain = msg->data.chain;
- error = llcache_send_event_to_users(object, &event);
+ error = llcache_send_event_to_users(object, &event);
+ }
break;
/* Events requiring action */
@@ -3388,10 +3288,9 @@ static nserror llcache_object_notify_users(llcache_object *object)
handle->state = LLCACHE_FETCH_HEADERS;
/* Emit any certificate data we hold */
- if (object->ssl_cert_count > 0) {
+ if (object->chain != NULL) {
event.type = LLCACHE_EVENT_GOT_CERTS;
- event.data.certs.certs = object->ssl_certs;
- event.data.certs.num = object->ssl_cert_count;
+ event.data.chain = object->chain;
error = handle->cb(handle, &event, handle->pw);
} else {
error = NSERROR_OK;
@@ -3613,16 +3512,12 @@ llcache_object_snapshot(llcache_object *object, llcache_object **snapshot)
}
}
- if (object->ssl_cert_count != 0) {
- newobj->ssl_certs = calloc(sizeof(struct ssl_cert_info),
- object->ssl_cert_count);
- if (newobj->ssl_certs == NULL) {
+ if (object->chain != NULL) {
+ error = cert_chain_dup(object->chain, &newobj->chain);
+ if (error != NSERROR_OK) {
llcache_object_destroy(newobj);
- return NSERROR_NOMEM;
+ return error;
}
- memcpy(newobj->ssl_certs, object->ssl_certs,
- sizeof(struct ssl_cert_info) * object->ssl_cert_count);
- newobj->ssl_cert_count = object->ssl_cert_count;
}
newobj->fetch.state = LLCACHE_FETCH_COMPLETE;
@@ -3662,7 +3557,7 @@ total_object_size(llcache_object *object)
}
}
- tot += object->ssl_cert_count * sizeof(struct ssl_cert_info);
+ tot += cert_chain_size(object->chain);
return tot;
}
diff --git a/content/llcache.h b/content/llcache.h
index 8d2411e..514272f 100644
--- a/content/llcache.h
+++ b/content/llcache.h
@@ -30,7 +30,7 @@
#include "utils/errors.h"
#include "utils/nsurl.h"
-struct ssl_cert_info;
+struct cert_chain;
struct fetch_multipart_data;
/** Handle for low-level cache object */
@@ -83,26 +83,23 @@ typedef enum {
* and must be copied if it is desirable to retain.
*/
typedef struct {
- llcache_event_type type; /**< Type of event */
+ llcache_event_type type; /**< Type of event */
union {
struct {
- const uint8_t *buf; /**< Buffer of data */
- size_t len; /**< Length of buffer, in bytes */
- } data; /**< Received data */
+ const uint8_t *buf; /**< Buffer of data */
+ size_t len; /**< Byte length of buffer */
+ } data; /**< Received data */
struct {
- nserror code; /**< The error code */
- const char *msg; /**< Error message */
+ nserror code; /**< The error code */
+ const char *msg; /**< Error message */
} error;
- const char *progress_msg; /**< Progress message */
+ const char *progress_msg; /**< Progress message */
struct {
- nsurl *from; /**< Redirect origin */
- nsurl *to; /**< Redirect target */
- } redirect; /**< Fetch URL redirect occured */
- struct {
- const struct ssl_cert_info *certs; /**< The chain */
- size_t num; /**< Number of certs in chain */
- } certs;
- } data; /**< Event data */
+ nsurl *from; /**< Redirect origin */
+ nsurl *to; /**< Redirect target */
+ } redirect; /**< Fetch URL redirect occured */
+ const struct cert_chain *chain; /**< Certificate chain */
+ } data; /**< Event data */
} llcache_event;
/**
@@ -171,17 +168,17 @@ struct llcache_parameters {
size_t hysteresis; /**< The hysteresis around the target size */
/** The minimum lifetime to consider sending objects to backing store.*/
- int minimum_lifetime;
+ int minimum_lifetime;
/** The minimum bandwidth to allow the backing store to
* use in bytes/second
*/
- size_t minimum_bandwidth;
+ size_t minimum_bandwidth;
/** The maximum bandwidth to allow the backing store to use in
* bytes/second
*/
- size_t maximum_bandwidth;
+ size_t maximum_bandwidth;
/** The time quantum over which to calculate the bandwidth values
*/
diff --git a/desktop/browser_private.h b/desktop/browser_private.h
index 6e45052..41b8fef 100644
--- a/desktop/browser_private.h
+++ b/desktop/browser_private.h
@@ -89,13 +89,6 @@ struct browser_fetch_parameters {
bool parent_quirks; /**< Optional parent quirks */
};
-/**
- * The SSL context for a fetch, as provided by the fetchers
- */
-struct browser_ssl_info {
- struct ssl_cert_info certs[MAX_SSL_CERTS]; /**< The certificate chain */
- size_t num; /**< The number of certificates in the chain */
-};
/**
* Browser window data.
@@ -113,9 +106,9 @@ struct browser_window {
struct browser_fetch_parameters current_parameters;
/**
- * The SSL information for the current content
+ * The certificate chain for the current content
*/
- struct browser_ssl_info current_ssl_info;
+ struct cert_chain *current_cert_chain;
/**
* Content handle of page in process of being loaded or NULL
@@ -129,9 +122,9 @@ struct browser_window {
struct browser_fetch_parameters loading_parameters;
/**
- * The SSL information for the loading content
+ * The certificate chain for the loading content
*/
- struct browser_ssl_info loading_ssl_info;
+ struct cert_chain *loading_cert_chain;
/**
* Favicon
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 06f41dd..6defe01 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -743,9 +743,10 @@ static nserror browser_window_content_ready(struct browser_window *bw)
browser_window__free_fetch_parameters(&bw->current_parameters);
bw->current_parameters = bw->loading_parameters;
memset(&bw->loading_parameters, 0, sizeof(bw->loading_parameters));
- /* Transfer the SSL info */
- bw->current_ssl_info = bw->loading_ssl_info;
- bw->loading_ssl_info.num = 0;
+ /* Transfer the certificate chain */
+ cert_chain_free(bw->current_cert_chain);
+ bw->current_cert_chain = bw->loading_cert_chain;
+ bw->loading_cert_chain = NULL;
}
/* Format the new content to the correct dimensions */
@@ -1136,7 +1137,7 @@ browser_window__handle_bad_certs(struct browser_window *bw,
nserror err;
/* Initially we don't know WHY the SSL cert was bad */
const char *reason = messages_get_sslcode(SSL_CERT_ERR_UNKNOWN);
- size_t n;
+ size_t depth;
memset(¶ms, 0, sizeof(params));
@@ -1151,12 +1152,14 @@ browser_window__handle_bad_certs(struct browser_window *bw,
goto out;
}
- for (n = 0; n < bw->loading_ssl_info.num; ++n) {
- size_t idx = bw->loading_ssl_info.num - (n + 1);
- ssl_cert_err err = bw->loading_ssl_info.certs[idx].err;
- if (err != SSL_CERT_ERR_OK) {
- reason = messages_get_sslcode(err);
- break;
+ if (bw->loading_cert_chain != NULL) {
+ for (depth = 0; depth < bw->loading_cert_chain->depth; ++depth) {
+ size_t idx = bw->loading_cert_chain->depth - (depth + 1);
+ ssl_cert_err err = bw->loading_cert_chain->certs[idx].err;
+ if (err != SSL_CERT_ERR_OK) {
+ reason = messages_get_sslcode(err);
+ break;
+ }
}
}
@@ -1175,8 +1178,7 @@ browser_window__handle_bad_certs(struct browser_window *bw,
}
err = guit->misc->cert_verify(url,
- bw->loading_ssl_info.certs,
- bw->loading_ssl_info.num,
+ bw->loading_cert_chain,
browser_window__handle_ssl_query_response,
bw);
@@ -1352,11 +1354,8 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw)
switch (event->type) {
case CONTENT_MSG_SSL_CERTS:
/* SSL certificate information has arrived, store it */
- assert(event->data.certs.num < MAX_SSL_CERTS);
- memcpy(&bw->loading_ssl_info.certs[0],
- event->data.certs.certs,
- sizeof(struct ssl_cert_info) * event->data.certs.num);
- bw->loading_ssl_info.num = event->data.certs.num;
+ cert_chain_free(bw->loading_cert_chain);
+ cert_chain_dup(event->data.chain, &bw->loading_cert_chain);
break;
case CONTENT_MSG_LOG:
@@ -3431,7 +3430,8 @@ navigate_internal_real(struct browser_window *bw,
fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL);
/* Clear SSL info for load */
- bw->loading_ssl_info.num = 0;
+ cert_chain_free(bw->loading_cert_chain);
+ bw->loading_cert_chain = NULL;
/* Set up retrieval parameters */
if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) {
@@ -4707,17 +4707,17 @@ browser_window_page_info_state browser_window_get_page_info_state(
}
/* Exported interface, documented in browser_window.h */
-nserror browser_window_get_ssl_chain(struct browser_window *bw, size_t *num,
- struct ssl_cert_info **chain)
+nserror
+browser_window_get_ssl_chain(struct browser_window *bw,
+ struct cert_chain **chain)
{
assert(bw != NULL);
- if (bw->current_ssl_info.num == 0) {
+ if (bw->current_cert_chain == NULL) {
return NSERROR_NOT_FOUND;
}
- *num = bw->current_ssl_info.num;
- *chain = &(bw->current_ssl_info.certs[0]);
+ *chain = bw->current_cert_chain;
return NSERROR_OK;
}
diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c
index 8b52e54..7ef457a 100644
--- a/desktop/gui_factory.c
+++ b/desktop/gui_factory.c
@@ -639,8 +639,7 @@ static nserror gui_default_launch_url(struct nsurl *url)
static nserror gui_default_cert_verify(nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
diff --git a/desktop/sslcert_viewer.c b/desktop/sslcert_viewer.c
index 4d87257..ec0fd34 100644
--- a/desktop/sslcert_viewer.c
+++ b/desktop/sslcert_viewer.c
@@ -52,6 +52,21 @@ enum sslcert_viewer_field {
typedef nserror (*response_cb)(bool proceed, void *pw);
/**
+ * ssl certificate information for certificate error message
+ */
+struct ssl_cert_info {
+ long version; /**< Certificate version */
+ char not_before[32]; /**< Valid from date */
+ char not_after[32]; /**< Valid to date */
+ int sig_type; /**< Signature type */
+ char serialnum[64]; /**< Serial number */
+ char issuer[256]; /**< Issuer details */
+ char subject[256]; /**< Subject details */
+ int cert_type; /**< Certificate type */
+ ssl_cert_err err; /**< Whatever is wrong with this certificate */
+};
+
+/**
* ssl certificate verification context.
*/
struct sslcert_session_data {
@@ -473,38 +488,183 @@ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d)
return err;
}
+#ifdef WITH_OPENSSL
+
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+static nserror
+der_to_certinfo(const uint8_t *der,
+ size_t der_length,
+ struct ssl_cert_info *info)
+{
+ BIO *mem;
+ BUF_MEM *buf;
+ const ASN1_INTEGER *asn1_num;
+ BIGNUM *bignum;
+ X509 *cert; /**< Pointer to certificate */
+
+ if (der == NULL) {
+ return NSERROR_OK;
+ }
+
+ cert = d2i_X509(NULL, &der, der_length);
+ if (cert == NULL) {
+ return NSERROR_INVALID;
+ }
+
+ /* get certificate version */
+ info->version = X509_get_version(cert);
+
+ /* not before date */
+ mem = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(mem, X509_get_notBefore(cert));
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->not_before,
+ buf->data,
+ min(sizeof(info->not_before) - 1, (unsigned)buf->length));
+ info->not_before[min(sizeof(info->not_before) - 1, (unsigned)buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* not after date */
+ mem = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(mem,
+ X509_get_notAfter(cert));
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->not_after,
+ buf->data,
+ min(sizeof(info->not_after) - 1, (unsigned)buf->length));
+ info->not_after[min(sizeof(info->not_after) - 1, (unsigned)buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* signature type */
+ info->sig_type = X509_get_signature_type(cert);
+
+ /* serial number */
+ asn1_num = X509_get_serialNumber(cert);
+ if (asn1_num != NULL) {
+ bignum = ASN1_INTEGER_to_BN(asn1_num, NULL);
+ if (bignum != NULL) {
+ char *tmp = BN_bn2hex(bignum);
+ if (tmp != NULL) {
+ strncpy(info->serialnum,
+ tmp,
+ sizeof(info->serialnum));
+ info->serialnum[sizeof(info->serialnum)-1] = '\0';
+ OPENSSL_free(tmp);
+ }
+ BN_free(bignum);
+ bignum = NULL;
+ }
+ }
+
+ /* issuer name */
+ mem = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(mem,
+ X509_get_issuer_name(cert),
+ 0, XN_FLAG_SEP_CPLUS_SPC |
+ XN_FLAG_DN_REV | XN_FLAG_FN_NONE);
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->issuer,
+ buf->data,
+ min(sizeof(info->issuer) - 1, (unsigned) buf->length));
+ info->issuer[min(sizeof(info->issuer) - 1, (unsigned) buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* subject */
+ mem = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(mem,
+ X509_get_subject_name(cert),
+ 0,
+ XN_FLAG_SEP_CPLUS_SPC |
+ XN_FLAG_DN_REV |
+ XN_FLAG_FN_NONE);
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->subject,
+ buf->data,
+ min(sizeof(info->subject) - 1, (unsigned)buf->length));
+ info->subject[min(sizeof(info->subject) - 1, (unsigned) buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* type of certificate */
+ info->cert_type = X509_certificate_type(cert, X509_get_pubkey(cert));
+
+ X509_free(cert);
+
+ return NSERROR_OK;
+}
+#else
+static nserror
+der_to_certinfo(uint8_t *der, size_t der_length, struct ssl_cert_info *info)
+{
+ return NSERROR_NOT_IMPLEMENTED;
+}
+#endif
+
+/* copy certificate data */
+static nserror
+convert_chain_to_cert_info(const struct cert_chain *chain,
+ struct ssl_cert_info **cert_info_out)
+{
+ struct ssl_cert_info *certs;
+ size_t depth;
+ nserror res;
+
+ certs = calloc(chain->depth, sizeof(struct ssl_cert_info));
+ if (certs == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ for (depth = 0; depth < chain->depth;depth++) {
+ res = der_to_certinfo(chain->certs[depth].der,
+ chain->certs[depth].der_length,
+ certs + depth);
+ if (res != NSERROR_OK) {
+ free(certs);
+ return res;
+ }
+ certs[depth].err = chain->certs[depth].err;
+ }
+
+ *cert_info_out = certs;
+ return NSERROR_OK;
+}
/* Exported interface, documented in sslcert_viewer.h */
nserror
-sslcert_viewer_create_session_data(unsigned long num,
- struct nsurl *url,
+sslcert_viewer_create_session_data(struct nsurl *url,
nserror (*cb)(bool proceed, void *pw),
void *cbpw,
- const struct ssl_cert_info *certs,
+ const struct cert_chain *chain,
struct sslcert_session_data **ssl_d)
{
struct sslcert_session_data *data;
-
+ nserror res;
assert(url != NULL);
- assert(certs != NULL);
+ assert(chain != NULL);
data = malloc(sizeof(struct sslcert_session_data));
if (data == NULL) {
*ssl_d = NULL;
return NSERROR_NOMEM;
}
-
- /* copy certificate data */
- data->certs = malloc(num * sizeof(struct ssl_cert_info));
- if (data->certs == NULL) {
+ res = convert_chain_to_cert_info(chain, &data->certs);
+ if (res != NSERROR_OK) {
free(data);
*ssl_d = NULL;
- return NSERROR_NOMEM;
+ return res;
}
- memcpy(data->certs, certs, num * sizeof(struct ssl_cert_info));
data->url = nsurl_ref(url);
- data->num = num;
+ data->num = chain->depth;
data->cb = cb;
data->cbpw = cbpw;
diff --git a/desktop/sslcert_viewer.h b/desktop/sslcert_viewer.h
index 6955e01..8542840 100644
--- a/desktop/sslcert_viewer.h
+++ b/desktop/sslcert_viewer.h
@@ -32,16 +32,15 @@ struct redraw_context;
struct core_window_callback_table;
struct rect;
struct nsurl;
-struct ssl_cert_info;
+struct cert_chain;
/**
* Create ssl certificate viewer session data.
*
- * \param num The number of certificates in the chain
* \param url Address of the page we're inspecting certificates of
* \param cb Low level cache callback
* \param cbpw Low level cache private data
- * \param certs The SSL certificates
+ * \param chain The SSL certificate chain
* \param ssl_d Updated to SSL certificate session data
* \return NSERROR_OK on success, appropriate error otherwise
*
@@ -49,8 +48,10 @@ struct ssl_cert_info;
* sslcert_viewer_fini destroys the session data.
*/
nserror sslcert_viewer_create_session_data(
- unsigned long num, struct nsurl *url, nserror (*cb)(bool proceed, void *pw),
- void *cbpw, const struct ssl_cert_info *certs,
+ struct nsurl *url,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw,
+ const struct cert_chain *chain,
struct sslcert_session_data **ssl_d);
@@ -65,7 +66,8 @@ nserror sslcert_viewer_create_session_data(
* \return NSERROR_OK on success, appropriate error otherwise
*/
nserror sslcert_viewer_init(struct core_window_callback_table *cw_t,
- void *core_window_handle, struct sslcert_session_data *ssl_d);
+ void *core_window_handle,
+ struct sslcert_session_data *ssl_d);
/**
diff --git a/frontends/gtk/page_info.c b/frontends/gtk/page_info.c
index adc2dfa..1401fdc 100644
--- a/frontends/gtk/page_info.c
+++ b/frontends/gtk/page_info.c
@@ -52,6 +52,7 @@ struct nsgtk_crtvrfy_window {
struct sslcert_session_data *ssl_data;
};
+
/**
* destroy a previously created certificate view
*/
@@ -178,11 +179,14 @@ nserror nsgtk_page_info(struct browser_window *bw)
struct nsgtk_crtvrfy_window *ncwin;
nserror res;
- size_t num;
- struct ssl_cert_info *chain;
+ struct cert_chain *chain;
struct nsurl *url;
- browser_window_get_ssl_chain(bw, &num, &chain);
+ res = browser_window_get_ssl_chain(bw, &chain);
+ if (res != NSERROR_OK) {
+ NSLOG(netsurf, WARNING, "Unable to get certificate chain");
+ return NSERROR_INVALID;
+ }
url = browser_window_access_url(bw);
ncwin = malloc(sizeof(struct nsgtk_crtvrfy_window));
@@ -245,8 +249,8 @@ nserror nsgtk_page_info(struct browser_window *bw)
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, dummy_cb, NULL, chain,
- &ncwin->ssl_data);
+ res = sslcert_viewer_create_session_data(
+ url, dummy_cb, NULL, chain, &ncwin->ssl_data);
if (res != NSERROR_OK) {
g_object_unref(G_OBJECT(ncwin->dlg));
free(ncwin);
diff --git a/include/netsurf/browser_window.h b/include/netsurf/browser_window.h
index 7b2f652..e8faa18 100644
--- a/include/netsurf/browser_window.h
+++ b/include/netsurf/browser_window.h
@@ -42,7 +42,7 @@ struct form_control;
struct nsurl;
struct rect;
struct redraw_context;
-struct ssl_cert_info;
+struct cert_chain;
enum content_debug;
/**
@@ -784,11 +784,9 @@ browser_window_page_info_state browser_window_get_page_info_state(
* If there is no chain available, this will return NSERROR_NOT_FOUND
*
* \param bw The browser window
- * \param num Pointer to be filled out with chain length
- * \param chain Pointer to be filled out with chain base
+ * \param chain Pointer to be filled out with certificate chain
* \return Whether or not the chain is available
*/
-nserror browser_window_get_ssl_chain(struct browser_window *bw, size_t *num,
- struct ssl_cert_info **chain);
+nserror browser_window_get_ssl_chain(struct browser_window *bw, struct cert_chain **chain);
#endif
diff --git a/include/netsurf/misc.h b/include/netsurf/misc.h
index 8a79531..cc0b78d 100644
--- a/include/netsurf/misc.h
+++ b/include/netsurf/misc.h
@@ -27,7 +27,7 @@
struct form_control;
struct gui_window;
-struct ssl_cert_info;
+struct cert_chain;
struct nsurl;
/**
@@ -81,8 +81,7 @@ struct gui_misc_table {
* \return NSERROR_OK on sucess else error and cb never called
*/
nserror (*cert_verify)(struct nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw);
diff --git a/include/netsurf/ssl_certs.h b/include/netsurf/ssl_certs.h
index 0444678..1aaf485 100644
--- a/include/netsurf/ssl_certs.h
+++ b/include/netsurf/ssl_certs.h
@@ -48,22 +48,67 @@ typedef enum {
/** Always the max known ssl certificate error type */
#define SSL_CERT_ERR_MAX_KNOWN SSL_CERT_ERR_HOSTNAME_MISMATCH
+/** maximum number of X509 certificates in chain for TLS connection */
+#define MAX_CERT_DEPTH 10
+
/**
- * ssl certificate information for certificate error message
+ * X509 certificate chain
*/
-struct ssl_cert_info {
- long version; /**< Certificate version */
- char not_before[32]; /**< Valid from date */
- char not_after[32]; /**< Valid to date */
- int sig_type; /**< Signature type */
- char serialnum[64]; /**< Serial number */
- char issuer[256]; /**< Issuer details */
- char subject[256]; /**< Subject details */
- int cert_type; /**< Certificate type */
- ssl_cert_err err; /**< Whatever is wrong with this certificate */
+struct cert_chain {
+ /**
+ * the number of certificates in the chain
+ * */
+ size_t depth;
+ struct {
+ /**
+ * Whatever is wrong with this certificate
+ */
+ ssl_cert_err err;
+
+ /**
+ * data in Distinguished Encoding Rules (DER) format
+ */
+ uint8_t *der;
+
+ /**
+ * DER length
+ */
+ size_t der_length;
+ } certs[MAX_CERT_DEPTH];
};
-/** maximum number of X509 certificates in chain for TLS connection */
-#define MAX_SSL_CERTS 10
+/**
+ * create new certificate chain
+ *
+ * \param dpth the depth to set in the new chain.
+ * \param chain_out A pointer to recive the new chain.
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ */
+nserror cert_chain_alloc(size_t depth, struct cert_chain **chain_out);
+
+/**
+ * duplicate a certificate chain
+ *
+ * \param src The certificate chain to copy from
+ * \param dst_out A pointer to recive the duplicated chain
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ */
+nserror cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out);
+
+/**
+ * free a certificate chain
+ *
+ * \param chain The certificate chain to free
+ * \return NSERROR_OK on success
+ */
+nserror cert_chain_free(struct cert_chain *chain);
+
+/**
+ * total number of data bytes in a chain
+ *
+ * \param chain The chain to size
+ * \return the number of bytes used by the chain
+ */
+size_t cert_chain_size(const struct cert_chain *chain);
#endif /* NETSURF_SSL_CERTS_H_ */
diff --git a/utils/Makefile b/utils/Makefile
index 2f59501..e0fbd8e 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -13,6 +13,7 @@ S_UTILS := \
messages.c \
nsoption.c \
punycode.c \
+ ssl_certs.c \
talloc.c \
time.c \
url.c \
diff --git a/utils/ssl_certs.c b/utils/ssl_certs.c
new file mode 100644
index 0000000..7154561
--- /dev/null
+++ b/utils/ssl_certs.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * helpers for X509 certificate chains
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+
+#include "netsurf/ssl_certs.h"
+
+/*
+ * create new certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
+{
+ struct cert_chain* chain;
+
+ chain = calloc(1, sizeof(struct cert_chain));
+ if (chain == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ chain->depth = depth;
+
+ *chain_out = chain;
+
+ return NSERROR_OK;
+}
+
+
+/*
+ * duplicate certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out)
+{
+ struct cert_chain* dst;
+ size_t depth;
+ nserror res;
+
+ res = cert_chain_alloc(src->depth, &dst);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ for (depth = 0; depth < src->depth; depth++) {
+ dst->certs[depth].err = src->certs[depth].err;
+ dst->certs[depth].der_length = src->certs[depth].der_length;
+ if (src->certs[depth].der != NULL) {
+ dst->certs[depth].der = malloc(src->certs[depth].der_length);
+ if (dst->certs[depth].der == NULL) {
+ cert_chain_free(dst);
+ return NSERROR_NOMEM;
+ }
+ memcpy(dst->certs[depth].der,
+ src->certs[depth].der,
+ src->certs[depth].der_length);
+ }
+
+ }
+
+ *dst_out = dst;
+ return NSERROR_OK;
+}
+
+
+/*
+ * free certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror cert_chain_free(struct cert_chain* chain)
+{
+ size_t depth;
+
+ if (chain != NULL) {
+ for (depth = 0; depth < chain->depth; depth++) {
+ if (chain->certs[depth].der != NULL) {
+ free(chain->certs[depth].der);
+ }
+ }
+
+ free(chain);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/*
+ * calculate storage used of certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+size_t cert_chain_size(const struct cert_chain *chain)
+{
+ size_t size = 0;
+ size_t depth;
+
+ if (chain != NULL) {
+ size += sizeof(struct cert_chain);
+
+ for (depth = 0; depth < chain->depth; depth++) {
+ if (chain->certs[depth].der != NULL) {
+ size += chain->certs[depth].der_length;
+ }
+ }
+ }
+
+ return size;
+}
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=214478fc15a2b9ee67e...
commit 214478fc15a2b9ee67e19e08cf27657c0157037a
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
Improve some content documentation comments
diff --git a/content/content.h b/content/content.h
index 0dbd58d..144a698 100644
--- a/content/content.h
+++ b/content/content.h
@@ -100,67 +100,118 @@ struct content_rfc5988_link {
/** Extra data for some content_msg messages. */
union content_msg_data {
- /** CONTENT_MSG_LOG - Information for logging */
+ /**
+ * CONTENT_MSG_LOG - Information for logging
+ */
struct {
- browser_window_console_source src; /**< The source of the logging */
- const char *msg; /**< The message to log */
- size_t msglen; /**< The length of that message */
- browser_window_console_flags flags; /**< The flags of the logging */
+ /** The source of the logging */
+ browser_window_console_source src;
+ /** The message to log */
+ const char *msg;
+ /** The length of that message */
+ size_t msglen;
+ /** The flags of the logging */
+ browser_window_console_flags flags;
} log;
- /** CONTENT_MSG_SSL_CERTS - The certificate chain from the underlying fetch */
+
+ /**
+ * CONTENT_MSG_SSL_CERTS - The certificate chain from the
+ * underlying fetch
+ */
struct {
const struct ssl_cert_info *certs; /**< The chain */
size_t num; /**< The number of certs in the chain */
} certs;
- /** CONTENT_MSG_ERROR - Error from content or underlying fetch */
+
+ /**
+ * CONTENT_MSG_ERROR - Error from content or underlying fetch
+ */
struct {
- nserror errorcode; /**< The error code to convey meaning */
- const char *errormsg; /**< The message.
- * if NSERROR_UNKNOWN then this is the
- * direct message, otherwise is some
- * kind of metadata (e.g. a message name
- * or somesuch) but always a nul
- * terminated string.
- */
+ /**
+ * The error code to convey meaning
+ */
+ nserror errorcode;
+ /**
+ * The message. if NSERROR_UNKNOWN then this is the direct
+ * message, otherwise is some kind of metadata (e.g. a
+ * message name or somesuch) but always a null terminated
+ * string.
+ */
+ const char *errormsg;
} errordata;
- /** CONTENT_MSG_REDIRECT - Redirect info */
+
+ /**
+ * CONTENT_MSG_REDIRECT - Redirect info
+ */
struct {
struct nsurl *from; /**< Redirect origin */
struct nsurl *to; /**< Redirect target */
} redirect; /**< Fetch URL redirect occured */
- /** CONTENT_MSG_REDRAW - Area of content which needs redrawing */
+
+ /**
+ * CONTENT_MSG_REDRAW - Area of content which needs redrawing
+ */
struct {
int x, y, width, height;
} redraw;
- /** CONTENT_MSG_REFRESH - Minimum delay */
+
+ /**
+ * CONTENT_MSG_REFRESH - Minimum delay
+ */
int delay;
- /** CONTENT_MSG_REFORMAT - Reformat should not cause a redraw */
+
+ /**
+ * CONTENT_MSG_REFORMAT - Reformat should not cause a redraw
+ */
bool background;
- /** CONTENT_MSG_STATUS - Status message update. If NULL, the content's
- * internal status text has been updated, and listener should use
- * content_get_status_message() */
+
+ /**
+ * CONTENT_MSG_STATUS - Status message update. If NULL, the
+ * content's internal status text has been updated, and
+ * listener should use content_get_status_message()
+ */
const char *explicit_status_text;
- /** CONTENT_MSG_DOWNLOAD - Low-level cache handle */
+
+ /**
+ * CONTENT_MSG_DOWNLOAD - Low-level cache handle
+ */
struct llcache_handle *download;
- /** CONTENT_MSG_RFC5988_LINK - rfc5988 link data */
+
+ /**
+ * CONTENT_MSG_RFC5988_LINK - rfc5988 link data
+ */
struct content_rfc5988_link *rfc5988_link;
- /** CONTENT_MSG_GETCTX - Javascript context */
+
+ /**
+ * CONTENT_MSG_GETCTX - Javascript context
+ */
struct jscontext **jscontext;
- /** CONTENT_MSG_GETDIMS - Get the viewport dimensions */
+
+ /**
+ * CONTENT_MSG_GETDIMS - Get the viewport dimensions
+ */
struct {
- /* TODO: Consider getting screen_width, screen_height too. */
+ /** \todo Consider getting screen_width, screen_height too. */
unsigned *viewport_width;
unsigned *viewport_height;
} getdims;
- /** CONTENT_MSG_SCROLL - Part of content to scroll to show */
+
+ /**
+ * CONTENT_MSG_SCROLL - Part of content to scroll to show
+ */
struct {
- /** if true, scroll to show area given by (x0, y0) and (x1,y1).
- * if false, scroll point (x0, y0) to top left of viewport */
+ /*
+ * if true, scroll to show area given by (x0, y0) and (x1,y1).
+ * if false, scroll point (x0, y0) to top left of viewport
+ */
bool area;
int x0, y0;
int x1, y1;
} scroll;
- /** CONTENT_MSG_DRAGSAVE - Drag save a content */
+
+ /**
+ * CONTENT_MSG_DRAGSAVE - Drag save a content
+ */
struct {
enum {
CONTENT_SAVE_ORIG,
@@ -171,19 +222,31 @@ union content_msg_data {
/** if NULL, save the content generating the message */
struct hlcache_handle *content;
} dragsave;
- /** CONTENT_MSG_SAVELINK - Save a URL */
+
+ /**
+ * CONTENT_MSG_SAVELINK - Save a URL
+ */
struct {
struct nsurl *url;
const char *title;
} savelink;
- /** CONTENT_MSG_POINTER - Mouse pointer to set */
+
+ /**
+ * CONTENT_MSG_POINTER - Mouse pointer to set
+ */
browser_pointer_shape pointer;
- /** CONTENT_MSG_SELECTION - Selection made or cleared */
+
+ /**
+ * CONTENT_MSG_SELECTION - Selection made or cleared
+ */
struct {
bool selection; /**< false for selection cleared */
bool read_only;
} selection;
- /** CONTENT_MSG_CARET - set caret position or, hide caret */
+
+ /**
+ * CONTENT_MSG_CARET - set caret position or, hide caret
+ */
struct {
enum {
CONTENT_CARET_SET_POS,
@@ -197,7 +260,10 @@ union content_msg_data {
const struct rect *clip; /**< Carret clip rect */
} pos; /**< With CONTENT_CARET_SET_POS */
} caret;
- /** CONTENT_MSG_DRAG - Drag start or end */
+
+ /**
+ * CONTENT_MSG_DRAG - Drag start or end
+ */
struct {
enum {
CONTENT_DRAG_NONE,
@@ -206,11 +272,17 @@ union content_msg_data {
} type;
const struct rect *rect;
} drag;
- /** CONTENT_MSG_SELECTMENU - Create select menu at pointer */
+
+ /**
+ * CONTENT_MSG_SELECTMENU - Create select menu at pointer
+ */
struct {
struct form_control *gadget;
} select_menu;
- /** CONTENT_MSG_GADGETCLICK - User clicked on a form gadget */
+
+ /**
+ * CONTENT_MSG_GADGETCLICK - User clicked on a form gadget
+ */
struct {
struct form_control *gadget;
} gadget_click;
diff --git a/content/fetch.h b/content/fetch.h
index 66be857..9a81ece 100644
--- a/content/fetch.h
+++ b/content/fetch.h
@@ -34,6 +34,9 @@ struct content;
struct fetch;
struct ssl_cert_info;
+/**
+ * Fetcher message types
+ */
typedef enum {
FETCH_PROGRESS,
FETCH_HEADER,
@@ -49,6 +52,9 @@ typedef enum {
FETCH_SSL_ERR
} fetch_msg_type;
+/**
+ * Fetcher message data
+ */
typedef struct fetch_msg {
fetch_msg_type type;
-----------------------------------------------------------------------
Summary of changes:
content/content.h | 155 +++++++++++++++------
content/fetch.h | 11 +-
content/fetchers/curl.c | 177 ++++++++---------------
content/hlcache.c | 9 +-
content/llcache.c | 285 ++++++++++++--------------------------
content/llcache.h | 35 +++--
desktop/browser_private.h | 15 +-
desktop/browser_window.c | 46 +++---
desktop/gui_factory.c | 3 +-
desktop/sslcert_viewer.c | 184 ++++++++++++++++++++++--
desktop/sslcert_viewer.h | 14 +-
frontends/gtk/page_info.c | 14 +-
include/netsurf/browser_window.h | 8 +-
include/netsurf/misc.h | 5 +-
include/netsurf/ssl_certs.h | 71 ++++++++--
utils/Makefile | 1 +
utils/ssl_certs.c | 138 ++++++++++++++++++
17 files changed, 706 insertions(+), 465 deletions(-)
create mode 100644 utils/ssl_certs.c
diff --git a/content/content.h b/content/content.h
index 0dbd58d..f8f8d32 100644
--- a/content/content.h
+++ b/content/content.h
@@ -44,7 +44,7 @@ struct object_params;
struct rect;
struct redraw_context;
struct llcache_query_msg;
-struct ssl_cert_info;
+struct cert_chain;
/** Status of a content */
typedef enum {
@@ -100,67 +100,115 @@ struct content_rfc5988_link {
/** Extra data for some content_msg messages. */
union content_msg_data {
- /** CONTENT_MSG_LOG - Information for logging */
+ /**
+ * CONTENT_MSG_LOG - Information for logging
+ */
struct {
- browser_window_console_source src; /**< The source of the logging */
- const char *msg; /**< The message to log */
- size_t msglen; /**< The length of that message */
- browser_window_console_flags flags; /**< The flags of the logging */
+ /** The source of the logging */
+ browser_window_console_source src;
+ /** The message to log */
+ const char *msg;
+ /** The length of that message */
+ size_t msglen;
+ /** The flags of the logging */
+ browser_window_console_flags flags;
} log;
- /** CONTENT_MSG_SSL_CERTS - The certificate chain from the underlying fetch */
- struct {
- const struct ssl_cert_info *certs; /**< The chain */
- size_t num; /**< The number of certs in the chain */
- } certs;
- /** CONTENT_MSG_ERROR - Error from content or underlying fetch */
+
+ /**
+ * CONTENT_MSG_SSL_CERTS - The certificate chain from the
+ * underlying fetch
+ */
+ const struct cert_chain *chain;
+
+ /**
+ * CONTENT_MSG_ERROR - Error from content or underlying fetch
+ */
struct {
- nserror errorcode; /**< The error code to convey meaning */
- const char *errormsg; /**< The message.
- * if NSERROR_UNKNOWN then this is the
- * direct message, otherwise is some
- * kind of metadata (e.g. a message name
- * or somesuch) but always a nul
- * terminated string.
- */
+ /**
+ * The error code to convey meaning
+ */
+ nserror errorcode;
+ /**
+ * The message. if NSERROR_UNKNOWN then this is the direct
+ * message, otherwise is some kind of metadata (e.g. a
+ * message name or somesuch) but always a null terminated
+ * string.
+ */
+ const char *errormsg;
} errordata;
- /** CONTENT_MSG_REDIRECT - Redirect info */
+
+ /**
+ * CONTENT_MSG_REDIRECT - Redirect info
+ */
struct {
struct nsurl *from; /**< Redirect origin */
struct nsurl *to; /**< Redirect target */
} redirect; /**< Fetch URL redirect occured */
- /** CONTENT_MSG_REDRAW - Area of content which needs redrawing */
+
+ /**
+ * CONTENT_MSG_REDRAW - Area of content which needs redrawing
+ */
struct {
int x, y, width, height;
} redraw;
- /** CONTENT_MSG_REFRESH - Minimum delay */
+
+ /**
+ * CONTENT_MSG_REFRESH - Minimum delay
+ */
int delay;
- /** CONTENT_MSG_REFORMAT - Reformat should not cause a redraw */
+
+ /**
+ * CONTENT_MSG_REFORMAT - Reformat should not cause a redraw
+ */
bool background;
- /** CONTENT_MSG_STATUS - Status message update. If NULL, the content's
- * internal status text has been updated, and listener should use
- * content_get_status_message() */
+
+ /**
+ * CONTENT_MSG_STATUS - Status message update. If NULL, the
+ * content's internal status text has been updated, and
+ * listener should use content_get_status_message()
+ */
const char *explicit_status_text;
- /** CONTENT_MSG_DOWNLOAD - Low-level cache handle */
+
+ /**
+ * CONTENT_MSG_DOWNLOAD - Low-level cache handle
+ */
struct llcache_handle *download;
- /** CONTENT_MSG_RFC5988_LINK - rfc5988 link data */
+
+ /**
+ * CONTENT_MSG_RFC5988_LINK - rfc5988 link data
+ */
struct content_rfc5988_link *rfc5988_link;
- /** CONTENT_MSG_GETCTX - Javascript context */
+
+ /**
+ * CONTENT_MSG_GETCTX - Javascript context
+ */
struct jscontext **jscontext;
- /** CONTENT_MSG_GETDIMS - Get the viewport dimensions */
+
+ /**
+ * CONTENT_MSG_GETDIMS - Get the viewport dimensions
+ */
struct {
- /* TODO: Consider getting screen_width, screen_height too. */
+ /** \todo Consider getting screen_width, screen_height too. */
unsigned *viewport_width;
unsigned *viewport_height;
} getdims;
- /** CONTENT_MSG_SCROLL - Part of content to scroll to show */
+
+ /**
+ * CONTENT_MSG_SCROLL - Part of content to scroll to show
+ */
struct {
- /** if true, scroll to show area given by (x0, y0) and (x1,y1).
- * if false, scroll point (x0, y0) to top left of viewport */
+ /*
+ * if true, scroll to show area given by (x0, y0) and (x1,y1).
+ * if false, scroll point (x0, y0) to top left of viewport
+ */
bool area;
int x0, y0;
int x1, y1;
} scroll;
- /** CONTENT_MSG_DRAGSAVE - Drag save a content */
+
+ /**
+ * CONTENT_MSG_DRAGSAVE - Drag save a content
+ */
struct {
enum {
CONTENT_SAVE_ORIG,
@@ -171,19 +219,31 @@ union content_msg_data {
/** if NULL, save the content generating the message */
struct hlcache_handle *content;
} dragsave;
- /** CONTENT_MSG_SAVELINK - Save a URL */
+
+ /**
+ * CONTENT_MSG_SAVELINK - Save a URL
+ */
struct {
struct nsurl *url;
const char *title;
} savelink;
- /** CONTENT_MSG_POINTER - Mouse pointer to set */
+
+ /**
+ * CONTENT_MSG_POINTER - Mouse pointer to set
+ */
browser_pointer_shape pointer;
- /** CONTENT_MSG_SELECTION - Selection made or cleared */
+
+ /**
+ * CONTENT_MSG_SELECTION - Selection made or cleared
+ */
struct {
bool selection; /**< false for selection cleared */
bool read_only;
} selection;
- /** CONTENT_MSG_CARET - set caret position or, hide caret */
+
+ /**
+ * CONTENT_MSG_CARET - set caret position or, hide caret
+ */
struct {
enum {
CONTENT_CARET_SET_POS,
@@ -197,7 +257,10 @@ union content_msg_data {
const struct rect *clip; /**< Carret clip rect */
} pos; /**< With CONTENT_CARET_SET_POS */
} caret;
- /** CONTENT_MSG_DRAG - Drag start or end */
+
+ /**
+ * CONTENT_MSG_DRAG - Drag start or end
+ */
struct {
enum {
CONTENT_DRAG_NONE,
@@ -206,11 +269,17 @@ union content_msg_data {
} type;
const struct rect *rect;
} drag;
- /** CONTENT_MSG_SELECTMENU - Create select menu at pointer */
+
+ /**
+ * CONTENT_MSG_SELECTMENU - Create select menu at pointer
+ */
struct {
struct form_control *gadget;
} select_menu;
- /** CONTENT_MSG_GADGETCLICK - User clicked on a form gadget */
+
+ /**
+ * CONTENT_MSG_GADGETCLICK - User clicked on a form gadget
+ */
struct {
struct form_control *gadget;
} gadget_click;
diff --git a/content/fetch.h b/content/fetch.h
index 66be857..9e80b26 100644
--- a/content/fetch.h
+++ b/content/fetch.h
@@ -34,6 +34,9 @@ struct content;
struct fetch;
struct ssl_cert_info;
+/**
+ * Fetcher message types
+ */
typedef enum {
FETCH_PROGRESS,
FETCH_HEADER,
@@ -49,6 +52,9 @@ typedef enum {
FETCH_SSL_ERR
} fetch_msg_type;
+/**
+ * Fetcher message data
+ */
typedef struct fetch_msg {
fetch_msg_type type;
@@ -69,10 +75,7 @@ typedef struct fetch_msg {
const char *realm;
} auth;
- struct {
- const struct ssl_cert_info *certs;
- size_t num_certs;
- } certs;
+ const struct cert_chain *chain;
} data;
} fetch_msg;
diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c
index 0be33ae..83e92d8 100644
--- a/content/fetchers/curl.c
+++ b/content/fetchers/curl.c
@@ -149,7 +149,7 @@ struct curl_fetch_info {
struct curl_httppost *post_multipart; /**< Multipart post data, or 0. */
uint64_t last_progress_update; /**< Time of last progress update */
int cert_depth; /**< deepest certificate in use */
- struct cert_info cert_data[MAX_SSL_CERTS]; /**< HTTPS certificate data */
+ struct cert_info cert_data[MAX_CERT_DEPTH]; /**< HTTPS certificate data */
};
/** curl handle cache entry */
@@ -471,128 +471,37 @@ failed:
static void
fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
{
- int depth;
+ size_t depth;
BIO *mem;
- BUF_MEM *buf;
- const ASN1_INTEGER *asn1_num;
- BIGNUM *bignum;
- struct ssl_cert_info ssl_certs[MAX_SSL_CERTS];
+ BUF_MEM *buf[MAX_CERT_DEPTH];
+ struct cert_chain chain;
fetch_msg msg;
- struct cert_info *certs = f->cert_data;
- memset(ssl_certs, 0, sizeof(ssl_certs));
+ struct cert_info *certs;
+
+ memset(&chain, 0, sizeof(chain));
+
+ certs = f->cert_data;
+ chain.depth = f->cert_depth + 1; /* 0 indexed certificate depth */
- for (depth = 0; depth <= f->cert_depth; depth++) {
+ for (depth = 0; depth < chain.depth; depth++) {
if (certs[depth].cert == NULL) {
/* This certificate is missing, skip it */
- ssl_certs[depth].err = SSL_CERT_ERR_CERT_MISSING;
+ chain.certs[depth].err = SSL_CERT_ERR_CERT_MISSING;
continue;
}
- /* get certificate version */
- ssl_certs[depth].version = X509_get_version(certs[depth].cert);
-
- /* not before date */
- mem = BIO_new(BIO_s_mem());
- ASN1_TIME_print(mem, X509_get_notBefore(certs[depth].cert));
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].not_before,
- buf->data,
- min(sizeof(ssl_certs[depth].not_before) - 1,
- (unsigned)buf->length));
- ssl_certs[depth].not_before[min(sizeof(ssl_certs[depth].not_before) - 1,
- (unsigned)buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* not after date */
- mem = BIO_new(BIO_s_mem());
- ASN1_TIME_print(mem,
- X509_get_notAfter(certs[depth].cert));
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].not_after,
- buf->data,
- min(sizeof(ssl_certs[depth].not_after) - 1,
- (unsigned)buf->length));
- ssl_certs[depth].not_after[min(sizeof(ssl_certs[depth].not_after) - 1,
- (unsigned)buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* signature type */
- ssl_certs[depth].sig_type =
- X509_get_signature_type(certs[depth].cert);
-
- /* serial number */
- asn1_num = X509_get_serialNumber(certs[depth].cert);
- if (asn1_num != NULL) {
- bignum = ASN1_INTEGER_to_BN(asn1_num, NULL);
- if (bignum != NULL) {
- char *tmp = BN_bn2hex(bignum);
- if (tmp != NULL) {
- strncpy(ssl_certs[depth].serialnum,
- tmp,
- sizeof(ssl_certs[depth].serialnum));
- ssl_certs[depth].serialnum[sizeof(ssl_certs[depth].serialnum)-1] = '\0';
- OPENSSL_free(tmp);
- }
- BN_free(bignum);
- bignum = NULL;
- }
- }
-
- /* issuer name */
- mem = BIO_new(BIO_s_mem());
- X509_NAME_print_ex(mem,
- X509_get_issuer_name(certs[depth].cert),
- 0, XN_FLAG_SEP_CPLUS_SPC |
- XN_FLAG_DN_REV | XN_FLAG_FN_NONE);
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].issuer,
- buf->data,
- min(sizeof(ssl_certs[depth].issuer) - 1,
- (unsigned) buf->length));
- ssl_certs[depth].issuer[min(sizeof(ssl_certs[depth].issuer) - 1,
- (unsigned) buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* subject */
- mem = BIO_new(BIO_s_mem());
- X509_NAME_print_ex(mem,
- X509_get_subject_name(certs[depth].cert),
- 0,
- XN_FLAG_SEP_CPLUS_SPC |
- XN_FLAG_DN_REV |
- XN_FLAG_FN_NONE);
- BIO_get_mem_ptr(mem, &buf);
- (void) BIO_set_close(mem, BIO_NOCLOSE);
- BIO_free(mem);
- memcpy(ssl_certs[depth].subject,
- buf->data,
- min(sizeof(ssl_certs[depth].subject) - 1,
- (unsigned)buf->length));
- ssl_certs[depth].subject[min(sizeof(ssl_certs[depth].subject) - 1,
- (unsigned) buf->length)] = 0;
- BUF_MEM_free(buf);
-
- /* type of certificate */
- ssl_certs[depth].cert_type =
- X509_certificate_type(certs[depth].cert,
- X509_get_pubkey(certs[depth].cert));
-
/* error code (if any) */
switch (certs[depth].err) {
case X509_V_OK:
- ssl_certs[depth].err = SSL_CERT_ERR_OK;
+ chain.certs[depth].err = SSL_CERT_ERR_OK;
break;
+
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
/* fallthrough */
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
- ssl_certs[depth].err = SSL_CERT_ERR_BAD_ISSUER;
+ chain.certs[depth].err = SSL_CERT_ERR_BAD_ISSUER;
break;
+
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
/* fallthrough */
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
@@ -600,41 +509,66 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
/* fallthrough */
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
- ssl_certs[depth].err = SSL_CERT_ERR_BAD_SIG;
+ chain.certs[depth].err = SSL_CERT_ERR_BAD_SIG;
break;
+
case X509_V_ERR_CERT_NOT_YET_VALID:
/* fallthrough */
case X509_V_ERR_CRL_NOT_YET_VALID:
- ssl_certs[depth].err = SSL_CERT_ERR_TOO_YOUNG;
+ chain.certs[depth].err = SSL_CERT_ERR_TOO_YOUNG;
break;
+
case X509_V_ERR_CERT_HAS_EXPIRED:
/* fallthrough */
case X509_V_ERR_CRL_HAS_EXPIRED:
- ssl_certs[depth].err = SSL_CERT_ERR_TOO_OLD;
+ chain.certs[depth].err = SSL_CERT_ERR_TOO_OLD;
break;
+
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- ssl_certs[depth].err = SSL_CERT_ERR_SELF_SIGNED;
+ chain.certs[depth].err = SSL_CERT_ERR_SELF_SIGNED;
break;
+
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
- ssl_certs[depth].err = SSL_CERT_ERR_CHAIN_SELF_SIGNED;
+ chain.certs[depth].err = SSL_CERT_ERR_CHAIN_SELF_SIGNED;
break;
+
case X509_V_ERR_CERT_REVOKED:
- ssl_certs[depth].err = SSL_CERT_ERR_REVOKED;
+ chain.certs[depth].err = SSL_CERT_ERR_REVOKED;
break;
+
case X509_V_ERR_HOSTNAME_MISMATCH:
- ssl_certs[depth].err = SSL_CERT_ERR_HOSTNAME_MISMATCH;
+ chain.certs[depth].err = SSL_CERT_ERR_HOSTNAME_MISMATCH;
break;
+
default:
- ssl_certs[depth].err = SSL_CERT_ERR_UNKNOWN;
+ chain.certs[depth].err = SSL_CERT_ERR_UNKNOWN;
break;
}
+
+ /*
+ * get certificate in Distinguished Encoding Rules (DER) format.
+ */
+ mem = BIO_new(BIO_s_mem());
+ i2d_X509_bio(mem, certs[depth].cert);
+ BIO_get_mem_ptr(mem, &buf[depth]);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+
+ chain.certs[depth].der = (uint8_t *)buf[depth]->data;
+ chain.certs[depth].der_length = buf[depth]->length;
}
msg.type = FETCH_CERTS;
- msg.data.certs.certs = ssl_certs;
- msg.data.certs.num_certs = depth;
+ msg.data.chain = &chain;
fetch_send_callback(&msg, f->fetch_handle);
+
+ /* release the openssl memory buffer */
+ for (depth = 0; depth < chain.depth; depth++) {
+ if (buf[depth] != NULL) {
+ BUF_MEM_free(buf[depth]);
+ }
+ }
}
@@ -667,7 +601,7 @@ fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx)
fetch = X509_STORE_CTX_get_app_data(x509_ctx);
/* certificate chain is excessively deep so fail verification */
- if (depth >= MAX_SSL_CERTS) {
+ if (depth >= MAX_CERT_DEPTH) {
X509_STORE_CTX_set_error(x509_ctx,
X509_V_ERR_CERT_CHAIN_TOO_LONG);
return 0;
@@ -1098,8 +1032,11 @@ static void fetch_curl_free(void *vf)
curl_formfree(f->post_multipart);
}
- for (i = 0; i < MAX_SSL_CERTS && f->cert_data[i].cert; i++) {
- ns_X509_free(f->cert_data[i].cert);
+ /* free certificate data */
+ for (i = 0; i < MAX_CERT_DEPTH; i++) {
+ if (f->cert_data[i].cert != NULL) {
+ ns_X509_free(f->cert_data[i].cert);
+ }
}
free(f);
diff --git a/content/hlcache.c b/content/hlcache.c
index ec011ec..23dbc57 100644
--- a/content/hlcache.c
+++ b/content/hlcache.c
@@ -423,8 +423,10 @@ static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
* \param pw Pointer to client-specific data
* \return NSERROR_OK on success, appropriate error otherwise
*/
-static nserror hlcache_llcache_callback(llcache_handle *handle,
- const llcache_event *event, void *pw)
+static nserror
+hlcache_llcache_callback(llcache_handle *handle,
+ const llcache_event *event,
+ void *pw)
{
hlcache_retrieval_ctx *ctx = pw;
lwc_string *effective_type = NULL;
@@ -439,8 +441,7 @@ static nserror hlcache_llcache_callback(llcache_handle *handle,
hlcache_event hlevent;
hlevent.type = CONTENT_MSG_SSL_CERTS;
- hlevent.data.certs.certs = event->data.certs.certs;
- hlevent.data.certs.num = event->data.certs.num;
+ hlevent.data.chain = event->data.chain;
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
}
diff --git a/content/llcache.c b/content/llcache.c
index 8a71f18..3a75bf9 100644
--- a/content/llcache.c
+++ b/content/llcache.c
@@ -36,6 +36,7 @@
#include <string.h>
#include <strings.h>
#include <nsutils/time.h>
+#include <nsutils/base64.h>
#include "netsurf/inttypes.h"
#include "utils/config.h"
@@ -175,8 +176,7 @@ struct llcache_object {
size_t source_len; /**< Byte length of source data */
size_t source_alloc; /**< Allocated size of source buffer */
- size_t ssl_cert_count; /**< The number of SSL certificates stored */
- struct ssl_cert_info *ssl_certs; /**< SSL certificate information if count is non-zero */
+ struct cert_chain *chain; /**< Certificate chain from the fetch */
llcache_store_state store_state; /**< where the data for the object is stored */
@@ -969,11 +969,7 @@ static nserror llcache_object_destroy(llcache_object *object)
NSLOG(llcache, DEBUG, "Destroying object %p, %s", object,
nsurl_access(object->url));
- if (object->ssl_cert_count != 0) {
- free(object->ssl_certs);
- object->ssl_certs = NULL;
- object->ssl_cert_count = 0;
- }
+ cert_chain_free(object->chain);
if (object->source_data != NULL) {
if (object->store_state == LLCACHE_STATE_DISC) {
@@ -1240,6 +1236,13 @@ llcache_serialise_metadata(llcache_object *object,
char *op;
unsigned int hloop;
int use;
+ size_t cert_chain_depth;
+
+ if (object->chain != NULL) {
+ cert_chain_depth = object->chain->depth;
+ } else {
+ cert_chain_depth = 0;
+ }
allocsize = 10 + 1; /* object length */
@@ -1251,22 +1254,19 @@ llcache_serialise_metadata(llcache_object *object,
allocsize += 10 + 1; /* space for number of header entries */
- allocsize += 10 + 1; /* space for number of SSL certificates */
-
- allocsize += nsurl_length(object->url) + 1;
-
for (hloop = 0 ; hloop < object->num_headers ; hloop++) {
allocsize += strlen(object->headers[hloop].name) + 1;
allocsize += strlen(object->headers[hloop].value) + 1;
}
- for (hloop = 0; hloop < object->ssl_cert_count; hloop++) {
- allocsize += (10 + 1) * 4; /* version, sig_type, cert_type, err */
- allocsize += strlen(object->ssl_certs[hloop].not_before) + 1;
- allocsize += strlen(object->ssl_certs[hloop].not_after) + 1;
- allocsize += strlen(object->ssl_certs[hloop].serialnum) + 1;
- allocsize += strlen(object->ssl_certs[hloop].issuer) + 1;
- allocsize += strlen(object->ssl_certs[hloop].subject) + 1;
+ allocsize += nsurl_length(object->url) + 1;
+
+ /* space for number of DER formatted certificates */
+ allocsize += 10 + 1;
+
+ for (hloop = 0; hloop < cert_chain_depth; hloop++) {
+ allocsize += 10 + 1; /* error status */
+ allocsize += 4 * ((object->chain->certs[hloop].der_length + 2) / 3);
}
data = malloc(allocsize);
@@ -1351,8 +1351,8 @@ llcache_serialise_metadata(llcache_object *object,
datasize -= use;
}
- /* number of ssl certificates */
- use = snprintf(op, datasize, "%" PRIsizet, object->ssl_cert_count);
+ /* number of DER formatted ssl certificates */
+ use = snprintf(op, datasize, "%" PRIsizet, cert_chain_depth);
if (use < 0) {
goto operror;
}
@@ -1363,80 +1363,13 @@ llcache_serialise_metadata(llcache_object *object,
datasize -= use;
/* SSL certificates */
- for (hloop = 0; hloop < object->ssl_cert_count; hloop++) {
- struct ssl_cert_info *cert = &(object->ssl_certs[hloop]);
- /* Certificate version */
- use = snprintf(op, datasize, "%ld", cert->version);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* not_before */
- use = snprintf(op, datasize, "%s", cert->not_before);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* not_after */
- use = snprintf(op, datasize, "%s", cert->not_after);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* Signature type */
- use = snprintf(op, datasize, "%d", cert->sig_type);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* serialnum */
- use = snprintf(op, datasize, "%s", cert->serialnum);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* issuer */
- use = snprintf(op, datasize, "%s", cert->issuer);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* subject */
- use = snprintf(op, datasize, "%s", cert->subject);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* Certificate type */
- use = snprintf(op, datasize, "%d", cert->cert_type);
+ for (hloop = 0; hloop < cert_chain_depth; hloop++) {
+ size_t output_length;
+ nsuerror res;
+
+ /* Certificate error code */
+ use = snprintf(op, datasize, "%d",
+ (int)(object->chain->certs[hloop].err));
if (use < 0) {
goto operror;
}
@@ -1445,14 +1378,26 @@ llcache_serialise_metadata(llcache_object *object,
goto overflow;
op += use;
datasize -= use;
- /* Certificate error code */
- use = snprintf(op, datasize, "%d", (int)(cert->err));
- if (use < 0) {
- goto operror;
+
+ /* DER certificate data in base64 encoding */
+ if (object->chain->certs[hloop].der != NULL) {
+ output_length = datasize;
+ res = nsu_base64_encode(
+ object->chain->certs[hloop].der,
+ object->chain->certs[hloop].der_length,
+ (uint8_t *)op,
+ &output_length);
+ if (res != NSUERROR_OK) {
+ goto operror;
+ }
+ use = output_length;
+ } else {
+ use = 0;
}
- use++; /* does not count the null */
+ use++; /* allow for null */
if (use > datasize)
goto overflow;
+ *(op + output_length) = 0;
op += use;
datasize -= use;
}
@@ -1510,7 +1455,7 @@ llcache_process_metadata(llcache_object *object)
size_t num_headers;
size_t hloop;
size_t ssl_cert_count = 0;
- struct ssl_cert_info *ssl_certs = NULL;
+ struct cert_chain *chain = NULL;
NSLOG(llcache, INFO, "Retrieving metadata");
@@ -1642,7 +1587,7 @@ llcache_process_metadata(llcache_object *object)
goto skip_ssl_certificates;
}
- /* Next line is the number of SSL certificates*/
+ /* Next line is the number of DER base64 encoded certificates */
line++;
ln += lnsize + 1;
lnsize = strlen(ln);
@@ -1657,72 +1602,20 @@ llcache_process_metadata(llcache_object *object)
goto skip_ssl_certificates;
}
- ssl_certs = calloc(sizeof(struct ssl_cert_info), ssl_cert_count);
- if (ssl_certs == NULL) {
- res = NSERROR_NOMEM;
+ if (ssl_cert_count > MAX_CERT_DEPTH) {
+ res = NSERROR_INVALID;
+ goto format_error;
+ }
+
+ res = cert_chain_alloc(ssl_cert_count, &chain);
+ if (res != NSERROR_OK) {
goto format_error;
}
for (hloop = 0; hloop < ssl_cert_count; hloop++) {
- struct ssl_cert_info *cert = &ssl_certs[hloop];
int errcode;
- /* Certificate version */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%ld", &cert->version) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
- /* Not before */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->not_before, ln, lnsize);
- /* Not after */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->not_after, ln, lnsize);
- /* Signature type */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%d", &cert->sig_type) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
- /* Serial Number */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->serialnum, ln, lnsize);
- /* issuer */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->issuer, ln, lnsize);
- /* subject */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->subject, ln, lnsize);
- /* Certificate type */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%d", &cert->cert_type) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
+ nsuerror nsures;
+
/* Certificate error code */
line++;
ln += lnsize + 1;
@@ -1735,9 +1628,25 @@ llcache_process_metadata(llcache_object *object)
if (errcode < SSL_CERT_ERR_OK ||
errcode > SSL_CERT_ERR_MAX_KNOWN) {
/* Error with the cert code, assume UNKNOWN */
- cert->err = SSL_CERT_ERR_UNKNOWN;
+ chain->certs[hloop].err = SSL_CERT_ERR_UNKNOWN;
} else {
- cert->err = (ssl_cert_err)errcode;
+ chain->certs[hloop].err = (ssl_cert_err)errcode;
+ }
+
+ /* base64 encoded DER certificate data */
+ line++;
+ ln += lnsize + 1;
+ lnsize = strlen(ln);
+ remaining -= lnsize + 1;
+ if (lnsize > 0) {
+ nsures = nsu_base64_decode_alloc((const uint8_t *)ln,
+ lnsize,
+ &chain->certs[hloop].der,
+ &chain->certs[hloop].der_length);
+ if (nsures != NSUERROR_OK) {
+ res = NSERROR_NOMEM;
+ goto format_error;
+ }
}
}
@@ -1754,8 +1663,7 @@ skip_ssl_certificates:
object->cache.res_time = response_time;
object->cache.fin_time = completion_time;
- object->ssl_cert_count = ssl_cert_count;
- object->ssl_certs = ssl_certs;
+ object->chain = chain;
/* object stored in backing store */
object->store_state = LLCACHE_STATE_DISC;
@@ -1768,9 +1676,7 @@ format_error:
line, res);
guit->llcache->release(object->url, BACKING_STORE_META);
- if (ssl_certs != NULL) {
- free(ssl_certs);
- }
+ cert_chain_free(chain);
return res;
}
@@ -3173,21 +3079,15 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p)
case FETCH_CERTS:
/* Certificate information from the fetch */
- /* Persist the data onto our object */
- object->ssl_certs = calloc(sizeof(struct ssl_cert_info),
- msg->data.certs.num_certs);
- if (object->ssl_certs != NULL) {
- object->ssl_cert_count = msg->data.certs.num_certs;
- memcpy(object->ssl_certs, msg->data.certs.certs,
- sizeof(struct ssl_cert_info) * object->ssl_cert_count);
- }
-
- /* Now pass on the event */
- event.type = LLCACHE_EVENT_GOT_CERTS;
- event.data.certs.certs = msg->data.certs.certs;
- event.data.certs.num = msg->data.certs.num_certs;
+ /* Persist the chain onto our object */
+ error = cert_chain_dup(msg->data.chain, &object->chain);
+ if (error != NSERROR_OK) {
+ /* Now pass on the event */
+ event.type = LLCACHE_EVENT_GOT_CERTS;
+ event.data.chain = msg->data.chain;
- error = llcache_send_event_to_users(object, &event);
+ error = llcache_send_event_to_users(object, &event);
+ }
break;
/* Events requiring action */
@@ -3388,10 +3288,9 @@ static nserror llcache_object_notify_users(llcache_object *object)
handle->state = LLCACHE_FETCH_HEADERS;
/* Emit any certificate data we hold */
- if (object->ssl_cert_count > 0) {
+ if (object->chain != NULL) {
event.type = LLCACHE_EVENT_GOT_CERTS;
- event.data.certs.certs = object->ssl_certs;
- event.data.certs.num = object->ssl_cert_count;
+ event.data.chain = object->chain;
error = handle->cb(handle, &event, handle->pw);
} else {
error = NSERROR_OK;
@@ -3613,16 +3512,12 @@ llcache_object_snapshot(llcache_object *object, llcache_object **snapshot)
}
}
- if (object->ssl_cert_count != 0) {
- newobj->ssl_certs = calloc(sizeof(struct ssl_cert_info),
- object->ssl_cert_count);
- if (newobj->ssl_certs == NULL) {
+ if (object->chain != NULL) {
+ error = cert_chain_dup(object->chain, &newobj->chain);
+ if (error != NSERROR_OK) {
llcache_object_destroy(newobj);
- return NSERROR_NOMEM;
+ return error;
}
- memcpy(newobj->ssl_certs, object->ssl_certs,
- sizeof(struct ssl_cert_info) * object->ssl_cert_count);
- newobj->ssl_cert_count = object->ssl_cert_count;
}
newobj->fetch.state = LLCACHE_FETCH_COMPLETE;
@@ -3662,7 +3557,7 @@ total_object_size(llcache_object *object)
}
}
- tot += object->ssl_cert_count * sizeof(struct ssl_cert_info);
+ tot += cert_chain_size(object->chain);
return tot;
}
diff --git a/content/llcache.h b/content/llcache.h
index 8d2411e..514272f 100644
--- a/content/llcache.h
+++ b/content/llcache.h
@@ -30,7 +30,7 @@
#include "utils/errors.h"
#include "utils/nsurl.h"
-struct ssl_cert_info;
+struct cert_chain;
struct fetch_multipart_data;
/** Handle for low-level cache object */
@@ -83,26 +83,23 @@ typedef enum {
* and must be copied if it is desirable to retain.
*/
typedef struct {
- llcache_event_type type; /**< Type of event */
+ llcache_event_type type; /**< Type of event */
union {
struct {
- const uint8_t *buf; /**< Buffer of data */
- size_t len; /**< Length of buffer, in bytes */
- } data; /**< Received data */
+ const uint8_t *buf; /**< Buffer of data */
+ size_t len; /**< Byte length of buffer */
+ } data; /**< Received data */
struct {
- nserror code; /**< The error code */
- const char *msg; /**< Error message */
+ nserror code; /**< The error code */
+ const char *msg; /**< Error message */
} error;
- const char *progress_msg; /**< Progress message */
+ const char *progress_msg; /**< Progress message */
struct {
- nsurl *from; /**< Redirect origin */
- nsurl *to; /**< Redirect target */
- } redirect; /**< Fetch URL redirect occured */
- struct {
- const struct ssl_cert_info *certs; /**< The chain */
- size_t num; /**< Number of certs in chain */
- } certs;
- } data; /**< Event data */
+ nsurl *from; /**< Redirect origin */
+ nsurl *to; /**< Redirect target */
+ } redirect; /**< Fetch URL redirect occured */
+ const struct cert_chain *chain; /**< Certificate chain */
+ } data; /**< Event data */
} llcache_event;
/**
@@ -171,17 +168,17 @@ struct llcache_parameters {
size_t hysteresis; /**< The hysteresis around the target size */
/** The minimum lifetime to consider sending objects to backing store.*/
- int minimum_lifetime;
+ int minimum_lifetime;
/** The minimum bandwidth to allow the backing store to
* use in bytes/second
*/
- size_t minimum_bandwidth;
+ size_t minimum_bandwidth;
/** The maximum bandwidth to allow the backing store to use in
* bytes/second
*/
- size_t maximum_bandwidth;
+ size_t maximum_bandwidth;
/** The time quantum over which to calculate the bandwidth values
*/
diff --git a/desktop/browser_private.h b/desktop/browser_private.h
index 6e45052..41b8fef 100644
--- a/desktop/browser_private.h
+++ b/desktop/browser_private.h
@@ -89,13 +89,6 @@ struct browser_fetch_parameters {
bool parent_quirks; /**< Optional parent quirks */
};
-/**
- * The SSL context for a fetch, as provided by the fetchers
- */
-struct browser_ssl_info {
- struct ssl_cert_info certs[MAX_SSL_CERTS]; /**< The certificate chain */
- size_t num; /**< The number of certificates in the chain */
-};
/**
* Browser window data.
@@ -113,9 +106,9 @@ struct browser_window {
struct browser_fetch_parameters current_parameters;
/**
- * The SSL information for the current content
+ * The certificate chain for the current content
*/
- struct browser_ssl_info current_ssl_info;
+ struct cert_chain *current_cert_chain;
/**
* Content handle of page in process of being loaded or NULL
@@ -129,9 +122,9 @@ struct browser_window {
struct browser_fetch_parameters loading_parameters;
/**
- * The SSL information for the loading content
+ * The certificate chain for the loading content
*/
- struct browser_ssl_info loading_ssl_info;
+ struct cert_chain *loading_cert_chain;
/**
* Favicon
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 06f41dd..6defe01 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -743,9 +743,10 @@ static nserror browser_window_content_ready(struct browser_window *bw)
browser_window__free_fetch_parameters(&bw->current_parameters);
bw->current_parameters = bw->loading_parameters;
memset(&bw->loading_parameters, 0, sizeof(bw->loading_parameters));
- /* Transfer the SSL info */
- bw->current_ssl_info = bw->loading_ssl_info;
- bw->loading_ssl_info.num = 0;
+ /* Transfer the certificate chain */
+ cert_chain_free(bw->current_cert_chain);
+ bw->current_cert_chain = bw->loading_cert_chain;
+ bw->loading_cert_chain = NULL;
}
/* Format the new content to the correct dimensions */
@@ -1136,7 +1137,7 @@ browser_window__handle_bad_certs(struct browser_window *bw,
nserror err;
/* Initially we don't know WHY the SSL cert was bad */
const char *reason = messages_get_sslcode(SSL_CERT_ERR_UNKNOWN);
- size_t n;
+ size_t depth;
memset(¶ms, 0, sizeof(params));
@@ -1151,12 +1152,14 @@ browser_window__handle_bad_certs(struct browser_window *bw,
goto out;
}
- for (n = 0; n < bw->loading_ssl_info.num; ++n) {
- size_t idx = bw->loading_ssl_info.num - (n + 1);
- ssl_cert_err err = bw->loading_ssl_info.certs[idx].err;
- if (err != SSL_CERT_ERR_OK) {
- reason = messages_get_sslcode(err);
- break;
+ if (bw->loading_cert_chain != NULL) {
+ for (depth = 0; depth < bw->loading_cert_chain->depth; ++depth) {
+ size_t idx = bw->loading_cert_chain->depth - (depth + 1);
+ ssl_cert_err err = bw->loading_cert_chain->certs[idx].err;
+ if (err != SSL_CERT_ERR_OK) {
+ reason = messages_get_sslcode(err);
+ break;
+ }
}
}
@@ -1175,8 +1178,7 @@ browser_window__handle_bad_certs(struct browser_window *bw,
}
err = guit->misc->cert_verify(url,
- bw->loading_ssl_info.certs,
- bw->loading_ssl_info.num,
+ bw->loading_cert_chain,
browser_window__handle_ssl_query_response,
bw);
@@ -1352,11 +1354,8 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw)
switch (event->type) {
case CONTENT_MSG_SSL_CERTS:
/* SSL certificate information has arrived, store it */
- assert(event->data.certs.num < MAX_SSL_CERTS);
- memcpy(&bw->loading_ssl_info.certs[0],
- event->data.certs.certs,
- sizeof(struct ssl_cert_info) * event->data.certs.num);
- bw->loading_ssl_info.num = event->data.certs.num;
+ cert_chain_free(bw->loading_cert_chain);
+ cert_chain_dup(event->data.chain, &bw->loading_cert_chain);
break;
case CONTENT_MSG_LOG:
@@ -3431,7 +3430,8 @@ navigate_internal_real(struct browser_window *bw,
fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL);
/* Clear SSL info for load */
- bw->loading_ssl_info.num = 0;
+ cert_chain_free(bw->loading_cert_chain);
+ bw->loading_cert_chain = NULL;
/* Set up retrieval parameters */
if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) {
@@ -4707,17 +4707,17 @@ browser_window_page_info_state browser_window_get_page_info_state(
}
/* Exported interface, documented in browser_window.h */
-nserror browser_window_get_ssl_chain(struct browser_window *bw, size_t *num,
- struct ssl_cert_info **chain)
+nserror
+browser_window_get_ssl_chain(struct browser_window *bw,
+ struct cert_chain **chain)
{
assert(bw != NULL);
- if (bw->current_ssl_info.num == 0) {
+ if (bw->current_cert_chain == NULL) {
return NSERROR_NOT_FOUND;
}
- *num = bw->current_ssl_info.num;
- *chain = &(bw->current_ssl_info.certs[0]);
+ *chain = bw->current_cert_chain;
return NSERROR_OK;
}
diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c
index 8b52e54..7ef457a 100644
--- a/desktop/gui_factory.c
+++ b/desktop/gui_factory.c
@@ -639,8 +639,7 @@ static nserror gui_default_launch_url(struct nsurl *url)
static nserror gui_default_cert_verify(nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
diff --git a/desktop/sslcert_viewer.c b/desktop/sslcert_viewer.c
index 4d87257..ec0fd34 100644
--- a/desktop/sslcert_viewer.c
+++ b/desktop/sslcert_viewer.c
@@ -52,6 +52,21 @@ enum sslcert_viewer_field {
typedef nserror (*response_cb)(bool proceed, void *pw);
/**
+ * ssl certificate information for certificate error message
+ */
+struct ssl_cert_info {
+ long version; /**< Certificate version */
+ char not_before[32]; /**< Valid from date */
+ char not_after[32]; /**< Valid to date */
+ int sig_type; /**< Signature type */
+ char serialnum[64]; /**< Serial number */
+ char issuer[256]; /**< Issuer details */
+ char subject[256]; /**< Subject details */
+ int cert_type; /**< Certificate type */
+ ssl_cert_err err; /**< Whatever is wrong with this certificate */
+};
+
+/**
* ssl certificate verification context.
*/
struct sslcert_session_data {
@@ -473,38 +488,183 @@ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d)
return err;
}
+#ifdef WITH_OPENSSL
+
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+static nserror
+der_to_certinfo(const uint8_t *der,
+ size_t der_length,
+ struct ssl_cert_info *info)
+{
+ BIO *mem;
+ BUF_MEM *buf;
+ const ASN1_INTEGER *asn1_num;
+ BIGNUM *bignum;
+ X509 *cert; /**< Pointer to certificate */
+
+ if (der == NULL) {
+ return NSERROR_OK;
+ }
+
+ cert = d2i_X509(NULL, &der, der_length);
+ if (cert == NULL) {
+ return NSERROR_INVALID;
+ }
+
+ /* get certificate version */
+ info->version = X509_get_version(cert);
+
+ /* not before date */
+ mem = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(mem, X509_get_notBefore(cert));
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->not_before,
+ buf->data,
+ min(sizeof(info->not_before) - 1, (unsigned)buf->length));
+ info->not_before[min(sizeof(info->not_before) - 1, (unsigned)buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* not after date */
+ mem = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(mem,
+ X509_get_notAfter(cert));
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->not_after,
+ buf->data,
+ min(sizeof(info->not_after) - 1, (unsigned)buf->length));
+ info->not_after[min(sizeof(info->not_after) - 1, (unsigned)buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* signature type */
+ info->sig_type = X509_get_signature_type(cert);
+
+ /* serial number */
+ asn1_num = X509_get_serialNumber(cert);
+ if (asn1_num != NULL) {
+ bignum = ASN1_INTEGER_to_BN(asn1_num, NULL);
+ if (bignum != NULL) {
+ char *tmp = BN_bn2hex(bignum);
+ if (tmp != NULL) {
+ strncpy(info->serialnum,
+ tmp,
+ sizeof(info->serialnum));
+ info->serialnum[sizeof(info->serialnum)-1] = '\0';
+ OPENSSL_free(tmp);
+ }
+ BN_free(bignum);
+ bignum = NULL;
+ }
+ }
+
+ /* issuer name */
+ mem = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(mem,
+ X509_get_issuer_name(cert),
+ 0, XN_FLAG_SEP_CPLUS_SPC |
+ XN_FLAG_DN_REV | XN_FLAG_FN_NONE);
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->issuer,
+ buf->data,
+ min(sizeof(info->issuer) - 1, (unsigned) buf->length));
+ info->issuer[min(sizeof(info->issuer) - 1, (unsigned) buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* subject */
+ mem = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(mem,
+ X509_get_subject_name(cert),
+ 0,
+ XN_FLAG_SEP_CPLUS_SPC |
+ XN_FLAG_DN_REV |
+ XN_FLAG_FN_NONE);
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->subject,
+ buf->data,
+ min(sizeof(info->subject) - 1, (unsigned)buf->length));
+ info->subject[min(sizeof(info->subject) - 1, (unsigned) buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* type of certificate */
+ info->cert_type = X509_certificate_type(cert, X509_get_pubkey(cert));
+
+ X509_free(cert);
+
+ return NSERROR_OK;
+}
+#else
+static nserror
+der_to_certinfo(uint8_t *der, size_t der_length, struct ssl_cert_info *info)
+{
+ return NSERROR_NOT_IMPLEMENTED;
+}
+#endif
+
+/* copy certificate data */
+static nserror
+convert_chain_to_cert_info(const struct cert_chain *chain,
+ struct ssl_cert_info **cert_info_out)
+{
+ struct ssl_cert_info *certs;
+ size_t depth;
+ nserror res;
+
+ certs = calloc(chain->depth, sizeof(struct ssl_cert_info));
+ if (certs == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ for (depth = 0; depth < chain->depth;depth++) {
+ res = der_to_certinfo(chain->certs[depth].der,
+ chain->certs[depth].der_length,
+ certs + depth);
+ if (res != NSERROR_OK) {
+ free(certs);
+ return res;
+ }
+ certs[depth].err = chain->certs[depth].err;
+ }
+
+ *cert_info_out = certs;
+ return NSERROR_OK;
+}
/* Exported interface, documented in sslcert_viewer.h */
nserror
-sslcert_viewer_create_session_data(unsigned long num,
- struct nsurl *url,
+sslcert_viewer_create_session_data(struct nsurl *url,
nserror (*cb)(bool proceed, void *pw),
void *cbpw,
- const struct ssl_cert_info *certs,
+ const struct cert_chain *chain,
struct sslcert_session_data **ssl_d)
{
struct sslcert_session_data *data;
-
+ nserror res;
assert(url != NULL);
- assert(certs != NULL);
+ assert(chain != NULL);
data = malloc(sizeof(struct sslcert_session_data));
if (data == NULL) {
*ssl_d = NULL;
return NSERROR_NOMEM;
}
-
- /* copy certificate data */
- data->certs = malloc(num * sizeof(struct ssl_cert_info));
- if (data->certs == NULL) {
+ res = convert_chain_to_cert_info(chain, &data->certs);
+ if (res != NSERROR_OK) {
free(data);
*ssl_d = NULL;
- return NSERROR_NOMEM;
+ return res;
}
- memcpy(data->certs, certs, num * sizeof(struct ssl_cert_info));
data->url = nsurl_ref(url);
- data->num = num;
+ data->num = chain->depth;
data->cb = cb;
data->cbpw = cbpw;
diff --git a/desktop/sslcert_viewer.h b/desktop/sslcert_viewer.h
index 6955e01..8542840 100644
--- a/desktop/sslcert_viewer.h
+++ b/desktop/sslcert_viewer.h
@@ -32,16 +32,15 @@ struct redraw_context;
struct core_window_callback_table;
struct rect;
struct nsurl;
-struct ssl_cert_info;
+struct cert_chain;
/**
* Create ssl certificate viewer session data.
*
- * \param num The number of certificates in the chain
* \param url Address of the page we're inspecting certificates of
* \param cb Low level cache callback
* \param cbpw Low level cache private data
- * \param certs The SSL certificates
+ * \param chain The SSL certificate chain
* \param ssl_d Updated to SSL certificate session data
* \return NSERROR_OK on success, appropriate error otherwise
*
@@ -49,8 +48,10 @@ struct ssl_cert_info;
* sslcert_viewer_fini destroys the session data.
*/
nserror sslcert_viewer_create_session_data(
- unsigned long num, struct nsurl *url, nserror (*cb)(bool proceed, void *pw),
- void *cbpw, const struct ssl_cert_info *certs,
+ struct nsurl *url,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw,
+ const struct cert_chain *chain,
struct sslcert_session_data **ssl_d);
@@ -65,7 +66,8 @@ nserror sslcert_viewer_create_session_data(
* \return NSERROR_OK on success, appropriate error otherwise
*/
nserror sslcert_viewer_init(struct core_window_callback_table *cw_t,
- void *core_window_handle, struct sslcert_session_data *ssl_d);
+ void *core_window_handle,
+ struct sslcert_session_data *ssl_d);
/**
diff --git a/frontends/gtk/page_info.c b/frontends/gtk/page_info.c
index adc2dfa..1401fdc 100644
--- a/frontends/gtk/page_info.c
+++ b/frontends/gtk/page_info.c
@@ -52,6 +52,7 @@ struct nsgtk_crtvrfy_window {
struct sslcert_session_data *ssl_data;
};
+
/**
* destroy a previously created certificate view
*/
@@ -178,11 +179,14 @@ nserror nsgtk_page_info(struct browser_window *bw)
struct nsgtk_crtvrfy_window *ncwin;
nserror res;
- size_t num;
- struct ssl_cert_info *chain;
+ struct cert_chain *chain;
struct nsurl *url;
- browser_window_get_ssl_chain(bw, &num, &chain);
+ res = browser_window_get_ssl_chain(bw, &chain);
+ if (res != NSERROR_OK) {
+ NSLOG(netsurf, WARNING, "Unable to get certificate chain");
+ return NSERROR_INVALID;
+ }
url = browser_window_access_url(bw);
ncwin = malloc(sizeof(struct nsgtk_crtvrfy_window));
@@ -245,8 +249,8 @@ nserror nsgtk_page_info(struct browser_window *bw)
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, dummy_cb, NULL, chain,
- &ncwin->ssl_data);
+ res = sslcert_viewer_create_session_data(
+ url, dummy_cb, NULL, chain, &ncwin->ssl_data);
if (res != NSERROR_OK) {
g_object_unref(G_OBJECT(ncwin->dlg));
free(ncwin);
diff --git a/include/netsurf/browser_window.h b/include/netsurf/browser_window.h
index 7b2f652..e8faa18 100644
--- a/include/netsurf/browser_window.h
+++ b/include/netsurf/browser_window.h
@@ -42,7 +42,7 @@ struct form_control;
struct nsurl;
struct rect;
struct redraw_context;
-struct ssl_cert_info;
+struct cert_chain;
enum content_debug;
/**
@@ -784,11 +784,9 @@ browser_window_page_info_state browser_window_get_page_info_state(
* If there is no chain available, this will return NSERROR_NOT_FOUND
*
* \param bw The browser window
- * \param num Pointer to be filled out with chain length
- * \param chain Pointer to be filled out with chain base
+ * \param chain Pointer to be filled out with certificate chain
* \return Whether or not the chain is available
*/
-nserror browser_window_get_ssl_chain(struct browser_window *bw, size_t *num,
- struct ssl_cert_info **chain);
+nserror browser_window_get_ssl_chain(struct browser_window *bw, struct cert_chain **chain);
#endif
diff --git a/include/netsurf/misc.h b/include/netsurf/misc.h
index 8a79531..cc0b78d 100644
--- a/include/netsurf/misc.h
+++ b/include/netsurf/misc.h
@@ -27,7 +27,7 @@
struct form_control;
struct gui_window;
-struct ssl_cert_info;
+struct cert_chain;
struct nsurl;
/**
@@ -81,8 +81,7 @@ struct gui_misc_table {
* \return NSERROR_OK on sucess else error and cb never called
*/
nserror (*cert_verify)(struct nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw);
diff --git a/include/netsurf/ssl_certs.h b/include/netsurf/ssl_certs.h
index 0444678..1aaf485 100644
--- a/include/netsurf/ssl_certs.h
+++ b/include/netsurf/ssl_certs.h
@@ -48,22 +48,67 @@ typedef enum {
/** Always the max known ssl certificate error type */
#define SSL_CERT_ERR_MAX_KNOWN SSL_CERT_ERR_HOSTNAME_MISMATCH
+/** maximum number of X509 certificates in chain for TLS connection */
+#define MAX_CERT_DEPTH 10
+
/**
- * ssl certificate information for certificate error message
+ * X509 certificate chain
*/
-struct ssl_cert_info {
- long version; /**< Certificate version */
- char not_before[32]; /**< Valid from date */
- char not_after[32]; /**< Valid to date */
- int sig_type; /**< Signature type */
- char serialnum[64]; /**< Serial number */
- char issuer[256]; /**< Issuer details */
- char subject[256]; /**< Subject details */
- int cert_type; /**< Certificate type */
- ssl_cert_err err; /**< Whatever is wrong with this certificate */
+struct cert_chain {
+ /**
+ * the number of certificates in the chain
+ * */
+ size_t depth;
+ struct {
+ /**
+ * Whatever is wrong with this certificate
+ */
+ ssl_cert_err err;
+
+ /**
+ * data in Distinguished Encoding Rules (DER) format
+ */
+ uint8_t *der;
+
+ /**
+ * DER length
+ */
+ size_t der_length;
+ } certs[MAX_CERT_DEPTH];
};
-/** maximum number of X509 certificates in chain for TLS connection */
-#define MAX_SSL_CERTS 10
+/**
+ * create new certificate chain
+ *
+ * \param dpth the depth to set in the new chain.
+ * \param chain_out A pointer to recive the new chain.
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ */
+nserror cert_chain_alloc(size_t depth, struct cert_chain **chain_out);
+
+/**
+ * duplicate a certificate chain
+ *
+ * \param src The certificate chain to copy from
+ * \param dst_out A pointer to recive the duplicated chain
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ */
+nserror cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out);
+
+/**
+ * free a certificate chain
+ *
+ * \param chain The certificate chain to free
+ * \return NSERROR_OK on success
+ */
+nserror cert_chain_free(struct cert_chain *chain);
+
+/**
+ * total number of data bytes in a chain
+ *
+ * \param chain The chain to size
+ * \return the number of bytes used by the chain
+ */
+size_t cert_chain_size(const struct cert_chain *chain);
#endif /* NETSURF_SSL_CERTS_H_ */
diff --git a/utils/Makefile b/utils/Makefile
index 2f59501..e0fbd8e 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -13,6 +13,7 @@ S_UTILS := \
messages.c \
nsoption.c \
punycode.c \
+ ssl_certs.c \
talloc.c \
time.c \
url.c \
diff --git a/utils/ssl_certs.c b/utils/ssl_certs.c
new file mode 100644
index 0000000..7154561
--- /dev/null
+++ b/utils/ssl_certs.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * helpers for X509 certificate chains
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+
+#include "netsurf/ssl_certs.h"
+
+/*
+ * create new certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
+{
+ struct cert_chain* chain;
+
+ chain = calloc(1, sizeof(struct cert_chain));
+ if (chain == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ chain->depth = depth;
+
+ *chain_out = chain;
+
+ return NSERROR_OK;
+}
+
+
+/*
+ * duplicate certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out)
+{
+ struct cert_chain* dst;
+ size_t depth;
+ nserror res;
+
+ res = cert_chain_alloc(src->depth, &dst);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ for (depth = 0; depth < src->depth; depth++) {
+ dst->certs[depth].err = src->certs[depth].err;
+ dst->certs[depth].der_length = src->certs[depth].der_length;
+ if (src->certs[depth].der != NULL) {
+ dst->certs[depth].der = malloc(src->certs[depth].der_length);
+ if (dst->certs[depth].der == NULL) {
+ cert_chain_free(dst);
+ return NSERROR_NOMEM;
+ }
+ memcpy(dst->certs[depth].der,
+ src->certs[depth].der,
+ src->certs[depth].der_length);
+ }
+
+ }
+
+ *dst_out = dst;
+ return NSERROR_OK;
+}
+
+
+/*
+ * free certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror cert_chain_free(struct cert_chain* chain)
+{
+ size_t depth;
+
+ if (chain != NULL) {
+ for (depth = 0; depth < chain->depth; depth++) {
+ if (chain->certs[depth].der != NULL) {
+ free(chain->certs[depth].der);
+ }
+ }
+
+ free(chain);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/*
+ * calculate storage used of certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+size_t cert_chain_size(const struct cert_chain *chain)
+{
+ size_t size = 0;
+ size_t depth;
+
+ if (chain != NULL) {
+ size += sizeof(struct cert_chain);
+
+ for (depth = 0; depth < chain->depth; depth++) {
+ if (chain->certs[depth].der != NULL) {
+ size += chain->certs[depth].der_length;
+ }
+ }
+ }
+
+ return size;
+}
--
NetSurf Browser
2 years, 11 months