summaryrefslogtreecommitdiff
path: root/content/fetchers
diff options
context:
space:
mode:
Diffstat (limited to 'content/fetchers')
-rw-r--r--content/fetchers/Makefile12
-rw-r--r--content/fetchers/about.c2976
-rw-r--r--content/fetchers/about/Makefile20
-rw-r--r--content/fetchers/about/about.c758
-rw-r--r--content/fetchers/about/about.h (renamed from content/fetchers/about.h)9
-rw-r--r--content/fetchers/about/atestament.h35
-rw-r--r--content/fetchers/about/blank.c60
-rw-r--r--content/fetchers/about/blank.h35
-rw-r--r--content/fetchers/about/certificate.c1291
-rw-r--r--content/fetchers/about/certificate.h35
-rw-r--r--content/fetchers/about/chart.c630
-rw-r--r--content/fetchers/about/chart.h37
-rw-r--r--content/fetchers/about/choices.c93
-rw-r--r--content/fetchers/about/choices.h35
-rw-r--r--content/fetchers/about/config.c133
-rw-r--r--content/fetchers/about/config.h35
-rw-r--r--content/fetchers/about/imagecache.c188
-rw-r--r--content/fetchers/about/imagecache.h37
-rw-r--r--content/fetchers/about/nscolours.c81
-rw-r--r--content/fetchers/about/nscolours.h35
-rw-r--r--content/fetchers/about/private.h77
-rw-r--r--content/fetchers/about/query.c60
-rw-r--r--content/fetchers/about/query.h36
-rw-r--r--content/fetchers/about/query_auth.c246
-rw-r--r--content/fetchers/about/query_auth.h35
-rw-r--r--content/fetchers/about/query_fetcherror.c158
-rw-r--r--content/fetchers/about/query_fetcherror.h35
-rw-r--r--content/fetchers/about/query_privacy.c174
-rw-r--r--content/fetchers/about/query_privacy.h35
-rw-r--r--content/fetchers/about/query_timeout.c158
-rw-r--r--content/fetchers/about/query_timeout.h35
-rw-r--r--content/fetchers/about/testament.c129
-rw-r--r--content/fetchers/curl.c676
-rw-r--r--content/fetchers/file/dirlist.c3
34 files changed, 5226 insertions, 3166 deletions
diff --git a/content/fetchers/Makefile b/content/fetchers/Makefile
index e87a4e891..8f6e5211f 100644
--- a/content/fetchers/Makefile
+++ b/content/fetchers/Makefile
@@ -1,15 +1,15 @@
# Content fetchers sources
-S_FETCHERS_YES := data.c about.c resource.c
+S_FETCHERS_YES := data.c resource.c
S_FETCHERS_NO :=
S_FETCHERS_$(NETSURF_USE_CURL) += curl.c
S_FETCHERS := $(addprefix fetchers/,$(S_FETCHERS_YES))
-# File fetcher
-include content/fetchers/file/Makefile
+# about fetcher
+include content/fetchers/about/Makefile
+S_FETCHERS += $(addprefix fetchers/about/,$(S_FETCHER_ABOUT))
+# file fetcher
+include content/fetchers/file/Makefile
S_FETCHERS += $(addprefix fetchers/file/,$(S_FETCHER_FILE))
-
-# The following files depend on the testament
-content/fetchers/about.c: testament $(OBJROOT)/testament.h
diff --git a/content/fetchers/about.c b/content/fetchers/about.c
deleted file mode 100644
index 28f0d1ee4..000000000
--- a/content/fetchers/about.c
+++ /dev/null
@@ -1,2976 +0,0 @@
-/*
- * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
- *
- * This file is part of NetSurf.
- *
- * 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
- *
- * URL handling for the "about" scheme.
- *
- * Based on the data fetcher by Rob Kendrick
- * This fetcher provides a simple scheme for the user to access
- * information from the browser from a known, fixed URL.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "netsurf/inttypes.h"
-#include "netsurf/plot_style.h"
-
-#include "utils/log.h"
-#include "testament.h"
-#include "utils/corestrings.h"
-#include "utils/nscolour.h"
-#include "utils/nsoption.h"
-#include "utils/utils.h"
-#include "utils/messages.h"
-#include "utils/ring.h"
-
-#include "content/fetch.h"
-#include "content/fetchers.h"
-#include "content/fetchers/about.h"
-#include "image/image_cache.h"
-
-#include "desktop/system_colour.h"
-
-struct fetch_about_context;
-
-typedef bool (*fetch_about_handler)(struct fetch_about_context *);
-
-/**
- * Context for an about fetch
- */
-struct fetch_about_context {
- struct fetch_about_context *r_next, *r_prev;
-
- struct fetch *fetchh; /**< Handle for this fetch */
-
- bool aborted; /**< Flag indicating fetch has been aborted */
- bool locked; /**< Flag indicating entry is already entered */
-
- nsurl *url; /**< The full url the fetch refers to */
-
- const struct fetch_multipart_data *multipart; /**< post data */
-
- fetch_about_handler handler;
-};
-
-static struct fetch_about_context *ring = NULL;
-
-/**
- * handler info for about scheme
- */
-struct about_handlers {
- const char *name; /**< name to match in url */
- int name_len;
- lwc_string *lname; /**< Interned name */
- fetch_about_handler handler; /**< handler for the url */
- bool hidden; /**< If entry should be hidden in listing */
-};
-
-
-/**
- * issue fetch callbacks with locking
- */
-static inline bool
-fetch_about_send_callback(const fetch_msg *msg, struct fetch_about_context *ctx)
-{
- ctx->locked = true;
- fetch_send_callback(msg, ctx->fetchh);
- ctx->locked = false;
-
- return ctx->aborted;
-}
-
-static inline bool
-fetch_about_send_finished(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
- msg.type = FETCH_FINISHED;
- return fetch_about_send_callback(&msg, ctx);
-}
-
-static bool
-fetch_about_send_header(struct fetch_about_context *ctx, const char *fmt, ...)
-{
- char header[64];
- fetch_msg msg;
- va_list ap;
-
- va_start(ap, fmt);
-
- vsnprintf(header, sizeof header, fmt, ap);
-
- va_end(ap);
-
- msg.type = FETCH_HEADER;
- msg.data.header_or_data.buf = (const uint8_t *) header;
- msg.data.header_or_data.len = strlen(header);
-
- return fetch_about_send_callback(&msg, ctx);
-}
-
-/**
- * send formatted data on a fetch
- */
-static nserror ssenddataf(struct fetch_about_context *ctx, const char *fmt, ...)
-{
- char buffer[1024];
- char *dbuff;
- fetch_msg msg;
- va_list ap;
- int slen;
-
- va_start(ap, fmt);
-
- slen = vsnprintf(buffer, sizeof(buffer), fmt, ap);
-
- va_end(ap);
-
- if (slen < (int)sizeof(buffer)) {
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buffer;
- msg.data.header_or_data.len = slen;
-
- if (fetch_about_send_callback(&msg, ctx)) {
- return NSERROR_INVALID;
- }
-
- return NSERROR_OK;
- }
-
- dbuff = malloc(slen + 1);
- if (dbuff == NULL) {
- return NSERROR_NOSPACE;
- }
-
- va_start(ap, fmt);
-
- slen = vsnprintf(dbuff, slen + 1, fmt, ap);
-
- va_end(ap);
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *)dbuff;
- msg.data.header_or_data.len = slen;
-
- if (fetch_about_send_callback(&msg, ctx)) {
- free(dbuff);
- return NSERROR_INVALID;
- }
-
- free(dbuff);
- return NSERROR_OK;
-}
-
-
-/**
- * Generate a 500 server error respnse
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_srverror(struct fetch_about_context *ctx)
-{
- nserror res;
-
- fetch_set_http_code(ctx->fetchh, 500);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
- return false;
-
- res = ssenddataf(ctx, "Server error 500");
- if (res != NSERROR_OK) {
- return false;
- }
-
- fetch_about_send_finished(ctx);
-
- return true;
-}
-
-
-/**
- * Handler to generate about scheme cache page.
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_blank_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
- const char buffer[2] = { ' ', '\0' };
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html"))
- goto fetch_about_blank_handler_aborted;
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buffer;
- msg.data.header_or_data.len = strlen(buffer);
-
- if (fetch_about_send_callback(&msg, ctx))
- goto fetch_about_blank_handler_aborted;
-
- msg.type = FETCH_FINISHED;
-
- fetch_about_send_callback(&msg, ctx);
-
- return true;
-
-fetch_about_blank_handler_aborted:
- return false;
-}
-
-
-/**
- * Handler to generate about scheme credits page.
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_credits_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
-
- /* content is going to return redirect */
- fetch_set_http_code(ctx->fetchh, 302);
-
- msg.type = FETCH_REDIRECT;
- msg.data.redirect = "resource:credits.html";
-
- fetch_about_send_callback(&msg, ctx);
-
- return true;
-}
-
-
-/**
- * Handler to generate about scheme licence page.
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_licence_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
-
- /* content is going to return redirect */
- fetch_set_http_code(ctx->fetchh, 302);
-
- msg.type = FETCH_REDIRECT;
- msg.data.redirect = "resource:licence.html";
-
- fetch_about_send_callback(&msg, ctx);
-
- return true;
-}
-
-
-/**
- * Handler to generate about:imagecache page.
- *
- * Shows details of current image cache.
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_imagecache_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
- char buffer[2048]; /* output buffer */
- int code = 200;
- int slen;
- unsigned int cent_loop = 0;
- int elen = 0; /* entry length */
- nserror res;
- bool even = false;
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, code);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html"))
- goto fetch_about_imagecache_handler_aborted;
-
- /* page head */
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>Image Cache Status</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body id =\"cachelist\" class=\"ns-even-bg ns-even-fg ns-border\">\n"
- "<h1 class=\"ns-border\">Image Cache Status</h1>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_imagecache_handler_aborted;
- }
-
- /* image cache summary */
- slen = image_cache_snsummaryf(buffer, sizeof(buffer),
- "<p>Configured limit of %a hysteresis of %b</p>\n"
- "<p>Total bitmap size in use %c (in %d)</p>\n"
- "<p>Age %es</p>\n"
- "<p>Peak size %f (in %g)</p>\n"
- "<p>Peak image count %h (size %i)</p>\n"
- "<p>Cache total/hit/miss/fail (counts) %j/%k/%l/%m "
- "(%pj%%/%pk%%/%pl%%/%pm%%)</p>\n"
- "<p>Cache total/hit/miss/fail (size) %n/%o/%q/%r "
- "(%pn%%/%po%%/%pq%%/%pr%%)</p>\n"
- "<p>Total images never rendered: %s "
- "(includes %t that were converted)</p>\n"
- "<p>Total number of excessive conversions: %u "
- "(from %v images converted more than once)"
- "</p>\n"
- "<p>Bitmap of size %w had most (%x) conversions</p>\n"
- "<h2 class=\"ns-border\">Current contents</h2>\n");
- if (slen >= (int) (sizeof(buffer))) {
- goto fetch_about_imagecache_handler_aborted; /* overflow */
- }
-
- /* send image cache summary */
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buffer;
- msg.data.header_or_data.len = slen;
- if (fetch_about_send_callback(&msg, ctx)) {
- goto fetch_about_imagecache_handler_aborted;
- }
-
- /* image cache entry table */
- res = ssenddataf(ctx, "<p class=\"imagecachelist\">\n"
- "<strong>"
- "<span>Entry</span>"
- "<span>Content Key</span>"
- "<span>Redraw Count</span>"
- "<span>Conversion Count</span>"
- "<span>Last Redraw</span>"
- "<span>Bitmap Age</span>"
- "<span>Bitmap Size</span>"
- "<span>Source</span>"
- "</strong>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_imagecache_handler_aborted;
- }
-
- slen = 0;
- do {
- if (even) {
- elen = image_cache_snentryf(buffer + slen,
- sizeof buffer - slen,
- cent_loop,
- "<a href=\"%U\">"
- "<span class=\"ns-border\">%e</span>"
- "<span class=\"ns-border\">%k</span>"
- "<span class=\"ns-border\">%r</span>"
- "<span class=\"ns-border\">%c</span>"
- "<span class=\"ns-border\">%a</span>"
- "<span class=\"ns-border\">%g</span>"
- "<span class=\"ns-border\">%s</span>"
- "<span class=\"ns-border\">%o</span>"
- "</a>\n");
- } else {
- elen = image_cache_snentryf(buffer + slen,
- sizeof buffer - slen,
- cent_loop,
- "<a class=\"ns-odd-bg\" href=\"%U\">"
- "<span class=\"ns-border\">%e</span>"
- "<span class=\"ns-border\">%k</span>"
- "<span class=\"ns-border\">%r</span>"
- "<span class=\"ns-border\">%c</span>"
- "<span class=\"ns-border\">%a</span>"
- "<span class=\"ns-border\">%g</span>"
- "<span class=\"ns-border\">%s</span>"
- "<span class=\"ns-border\">%o</span>"
- "</a>\n");
- }
- if (elen <= 0)
- break; /* last option */
-
- if (elen >= (int) (sizeof buffer - slen)) {
- /* last entry would not fit in buffer, submit buffer */
- msg.data.header_or_data.len = slen;
- if (fetch_about_send_callback(&msg, ctx))
- goto fetch_about_imagecache_handler_aborted;
- slen = 0;
- } else {
- /* normal addition */
- slen += elen;
- cent_loop++;
- even = !even;
- }
- } while (elen > 0);
-
- slen += snprintf(buffer + slen, sizeof buffer - slen,
- "</p>\n</body>\n</html>\n");
-
- msg.data.header_or_data.len = slen;
- if (fetch_about_send_callback(&msg, ctx))
- goto fetch_about_imagecache_handler_aborted;
-
- fetch_about_send_finished(ctx);
-
- return true;
-
-fetch_about_imagecache_handler_aborted:
- return false;
-}
-
-/**
- * certificate name parameters
- */
-struct ns_cert_name {
- char *common_name;
- char *organisation;
- char *organisation_unit;
- char *locality;
- char *province;
- char *country;
-};
-
-/**
- * Certificate public key parameters
- */
-struct ns_cert_pkey {
- char *algor;
- int size;
- char *modulus;
- char *exponent;
- char *curve;
- char *public;
-};
-
-/**
- * Certificate subject alternative name
- */
-struct ns_cert_san {
- struct ns_cert_san *next;
- char *name;
-};
-
-/**
- * certificate information for certificate chain
- */
-struct ns_cert_info {
- struct ns_cert_name subject_name; /**< Subject details */
- struct ns_cert_name issuer_name; /**< Issuer details */
- struct ns_cert_pkey public_key; /**< public key details */
- long version; /**< Certificate version */
- char *not_before; /**< Valid from date */
- char *not_after; /**< Valid to date */
- int sig_type; /**< Signature type */
- char *sig_algor; /**< Signature Algorithm */
- char *serialnum; /**< Serial number */
- char *sha1fingerprint; /**< fingerprint shar1 encoded */
- char *sha256fingerprint; /**< fingerprint shar256 encoded */
- struct ns_cert_san *san; /**< subject alternative names */
- ssl_cert_err err; /**< Whatever is wrong with this certificate */
-};
-
-/**
- * free all resources associated with a certificate information structure
- */
-static nserror free_ns_cert_info(struct ns_cert_info *cinfo)
-{
- struct ns_cert_san *san;
-
- free(cinfo->subject_name.common_name);
- free(cinfo->subject_name.organisation);
- free(cinfo->subject_name.organisation_unit);
- free(cinfo->subject_name.locality);
- free(cinfo->subject_name.province);
- free(cinfo->subject_name.country);
- free(cinfo->issuer_name.common_name);
- free(cinfo->issuer_name.organisation);
- free(cinfo->issuer_name.organisation_unit);
- free(cinfo->issuer_name.locality);
- free(cinfo->issuer_name.province);
- free(cinfo->issuer_name.country);
- free(cinfo->public_key.algor);
- free(cinfo->public_key.modulus);
- free(cinfo->public_key.exponent);
- free(cinfo->public_key.curve);
- free(cinfo->public_key.public);
- free(cinfo->not_before);
- free(cinfo->not_after);
- free(cinfo->sig_algor);
- free(cinfo->serialnum);
-
- /* free san list avoiding use after free */
- san = cinfo->san;
- while (san != NULL) {
- struct ns_cert_san *next;
- next = san->next;
- free(san);
- san = next;
- }
-
- free(cinfo);
-
- return NSERROR_OK;
-}
-
-#ifdef WITH_OPENSSL
-
-#include <openssl/ssl.h>
-#include <openssl/x509v3.h>
-
-/* OpenSSL 1.0.x, 1.0.2, 1.1.0 and 1.1.1 API all changed
- * LibreSSL declares its OpenSSL version as 2.1 but only supports 1.0.x API
- */
-#if (defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x1010000fL))
-/* 1.0.x */
-
-#if (defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x1000200fL))
-/* pre 1.0.2 */
-static int ns_X509_get_signature_nid(X509 *cert)
-{
- return OBJ_obj2nid(cert->cert_info->key->algor->algorithm);
-}
-#else
-#define ns_X509_get_signature_nid X509_get_signature_nid
-#endif
-
-static const unsigned char *ns_ASN1_STRING_get0_data(ASN1_STRING *asn1str)
-{
- return (const unsigned char *)ASN1_STRING_data(asn1str);
-}
-
-static const BIGNUM *ns_RSA_get0_n(const RSA *d)
-{
- return d->n;
-}
-
-static const BIGNUM *ns_RSA_get0_e(const RSA *d)
-{
- return d->e;
-}
-
-static int ns_RSA_bits(const RSA *rsa)
-{
- return RSA_size(rsa) * 8;
-}
-
-static int ns_DSA_bits(const DSA *dsa)
-{
- return DSA_size(dsa) * 8;
-}
-
-static int ns_DH_bits(const DH *dh)
-{
- return DH_size(dh) * 8;
-}
-
-#elif (OPENSSL_VERSION_NUMBER < 0x1010100fL)
-/* 1.1.0 */
-#define ns_X509_get_signature_nid X509_get_signature_nid
-#define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data
-
-static const BIGNUM *ns_RSA_get0_n(const RSA *r)
-{
- const BIGNUM *n;
- const BIGNUM *e;
- const BIGNUM *d;
- RSA_get0_key(r, &n, &e, &d);
- return n;
-}
-
-static const BIGNUM *ns_RSA_get0_e(const RSA *r)
-{
- const BIGNUM *n;
- const BIGNUM *e;
- const BIGNUM *d;
- RSA_get0_key(r, &n, &e, &d);
- return e;
-}
-
-#define ns_RSA_bits RSA_bits
-#define ns_DSA_bits DSA_bits
-#define ns_DH_bits DH_bits
-
-#else
-/* 1.1.1 and later */
-#define ns_X509_get_signature_nid X509_get_signature_nid
-#define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data
-#define ns_RSA_get0_n RSA_get0_n
-#define ns_RSA_get0_e RSA_get0_e
-#define ns_RSA_bits RSA_bits
-#define ns_DSA_bits DSA_bits
-#define ns_DH_bits DH_bits
-#endif
-
-/**
- * extract certificate name information
- *
- * \param xname The X509 name to convert. The reference is borrowed so is not freeed
- * \param iname The info structure to recive the extracted parameters.
- * \return NSERROR_OK on success else error code
- */
-static nserror
-xname_to_info(X509_NAME *xname, struct ns_cert_name *iname)
-{
- int entryidx;
- int entrycnt;
- X509_NAME_ENTRY *entry; /* current name entry */
- ASN1_STRING *value;
- const unsigned char *value_str;
- ASN1_OBJECT *name;
- int name_nid;
- char **field;
-
- entrycnt = X509_NAME_entry_count(xname);
-
- for (entryidx = 0; entryidx < entrycnt; entryidx++) {
- entry = X509_NAME_get_entry(xname, entryidx);
- name = X509_NAME_ENTRY_get_object(entry);
- name_nid = OBJ_obj2nid(name);
- value = X509_NAME_ENTRY_get_data(entry);
- value_str = ns_ASN1_STRING_get0_data(value);
- switch (name_nid) {
- case NID_commonName:
- field = &iname->common_name;
- break;
- case NID_countryName:
- field = &iname->country;
- break;
- case NID_localityName:
- field = &iname->locality;
- break;
- case NID_stateOrProvinceName:
- field = &iname->province;
- break;
- case NID_organizationName:
- field = &iname->organisation;
- break;
- case NID_organizationalUnitName:
- field = &iname->organisation_unit;
- break;
- default :
- field = NULL;
- break;
- }
- if (field != NULL) {
- *field = strdup((const char *)value_str);
- NSLOG(netsurf, DEEPDEBUG,
- "NID:%d value: %s", name_nid, *field);
- } else {
- NSLOG(netsurf, DEEPDEBUG, "NID:%d", name_nid);
- }
- }
-
- /*
- * ensure the common name is set to something, this being
- * missing means the certificate is broken but this should be
- * robust in the face of bad data
- */
- if (iname->common_name == NULL) {
- iname->common_name = strdup("Unknown");
- }
-
- return NSERROR_OK;
-}
-
-
-/**
- * duplicate a hex formatted string inserting the colons
- *
- * \todo only uses html entity as separator because netsurfs line breaking
- * fails otherwise.
- */
-static char *hexdup(const char *hex)
-{
- int hexlen;
- char *dst;
- char *out;
- int cn = 0;
-
- hexlen = strlen(hex);
- /* allow space fox XXYY to XX&#58;YY&#58; */
- dst = malloc(((hexlen * 7) + 6) / 2);
-
- if (dst != NULL) {
- for (out = dst; *hex != 0; hex++) {
- if (cn == 2) {
- cn = 0;
- *out++ = '&';
- *out++ = '#';
- *out++ = '5';
- *out++ = '8';
- *out++ = ';';
- }
- *out++ = *hex;
- cn++;
- }
- *out = 0;
- }
- return dst;
-}
-
-
-/**
- * create a hex formatted string inserting the colons from binary data
- *
- * \todo only uses html entity as separator because netsurfs line breaking
- * fails otherwise.
- */
-static char *bindup(unsigned char *bin, unsigned int binlen)
-{
- char *dst;
- char *out;
- unsigned int idx;
- const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-
- /* allow space fox XY to expand to XX&#58;YY&#58; */
- dst = malloc(binlen * 7);
-
- if (dst != NULL) {
- out = dst;
- for (idx = 0; idx < binlen; idx++) {
- *out++ = hex[(bin[idx] & 0xf0) >> 4];
- *out++ = hex[bin[idx] & 0xf];
-
- *out++ = '&';
- *out++ = '#';
- *out++ = '5';
- *out++ = '8';
- *out++ = ';';
- }
- out -= 5;
- *out = 0;
- }
- return dst;
-}
-
-
-/**
- * extract RSA key information to info structure
- *
- * \param rsa The RSA key to examine. The reference is dropped on return
- * \param ikey The public key info structure to fill
- * \rerun NSERROR_OK on success else error code.
- */
-static nserror
-rsa_to_info(RSA *rsa, struct ns_cert_pkey *ikey)
-{
- char *tmp;
-
- if (rsa == NULL) {
- return NSERROR_BAD_PARAMETER;
- }
-
- ikey->algor = strdup("RSA");
-
- ikey->size = ns_RSA_bits(rsa);
-
- tmp = BN_bn2hex(ns_RSA_get0_n(rsa));
- if (tmp != NULL) {
- ikey->modulus = hexdup(tmp);
- OPENSSL_free(tmp);
- }
-
- tmp = BN_bn2dec(ns_RSA_get0_e(rsa));
- if (tmp != NULL) {
- ikey->exponent = strdup(tmp);
- OPENSSL_free(tmp);
- }
-
- RSA_free(rsa);
-
- return NSERROR_OK;
-}
-
-
-/**
- * extract DSA key information to info structure
- *
- * \param dsa The RSA key to examine. The reference is dropped on return
- * \param ikey The public key info structure to fill
- * \rerun NSERROR_OK on success else error code.
- */
-static nserror
-dsa_to_info(DSA *dsa, struct ns_cert_pkey *ikey)
-{
- if (dsa == NULL) {
- return NSERROR_BAD_PARAMETER;
- }
-
- ikey->algor = strdup("DSA");
-
- ikey->size = ns_DSA_bits(dsa);
-
- DSA_free(dsa);
-
- return NSERROR_OK;
-}
-
-
-/**
- * extract DH key information to info structure
- *
- * \param dsa The RSA key to examine. The reference is dropped on return
- * \param ikey The public key info structure to fill
- * \rerun NSERROR_OK on success else error code.
- */
-static nserror
-dh_to_info(DH *dh, struct ns_cert_pkey *ikey)
-{
- if (dh == NULL) {
- return NSERROR_BAD_PARAMETER;
- }
-
- ikey->algor = strdup("Diffie Hellman");
-
- ikey->size = ns_DH_bits(dh);
-
- DH_free(dh);
-
- return NSERROR_OK;
-}
-
-
-/**
- * extract EC key information to info structure
- *
- * \param ec The EC key to examine. The reference is dropped on return
- * \param ikey The public key info structure to fill
- * \rerun NSERROR_OK on success else error code.
- */
-static nserror
-ec_to_info(EC_KEY *ec, struct ns_cert_pkey *ikey)
-{
- const EC_GROUP *ecgroup;
- const EC_POINT *ecpoint;
- BN_CTX *bnctx;
- char *ecpoint_hex;
-
- if (ec == NULL) {
- return NSERROR_BAD_PARAMETER;
- }
-
- ikey->algor = strdup("Elliptic Curve");
-
- ecgroup = EC_KEY_get0_group(ec);
-
- if (ecgroup != NULL) {
- ikey->size = EC_GROUP_get_degree(ecgroup);
-
- ikey->curve = strdup(OBJ_nid2ln(EC_GROUP_get_curve_name(ecgroup)));
-
- ecpoint = EC_KEY_get0_public_key(ec);
- if (ecpoint != NULL) {
- bnctx = BN_CTX_new();
- ecpoint_hex = EC_POINT_point2hex(ecgroup,
- ecpoint,
- POINT_CONVERSION_UNCOMPRESSED,
- bnctx);
- ikey->public = hexdup(ecpoint_hex);
- OPENSSL_free(ecpoint_hex);
- BN_CTX_free(bnctx);
- }
- }
- EC_KEY_free(ec);
-
- return NSERROR_OK;
-}
-
-
-/**
- * extract public key information to info structure
- *
- * \param pkey the public key to examine. The reference is dropped on return
- * \param ikey The public key info structure to fill
- * \rerun NSERROR_OK on success else error code.
- */
-static nserror
-pkey_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey)
-{
- nserror res;
-
- if (pkey == NULL) {
- return NSERROR_BAD_PARAMETER;
- }
-
- switch (EVP_PKEY_base_id(pkey)) {
- case EVP_PKEY_RSA:
- res = rsa_to_info(EVP_PKEY_get1_RSA(pkey), ikey);
- break;
-
- case EVP_PKEY_DSA:
- res = dsa_to_info(EVP_PKEY_get1_DSA(pkey), ikey);
- break;
-
- case EVP_PKEY_DH:
- res = dh_to_info(EVP_PKEY_get1_DH(pkey), ikey);
- break;
-
- case EVP_PKEY_EC:
- res = ec_to_info(EVP_PKEY_get1_EC_KEY(pkey), ikey);
- break;
-
- default:
- res = NSERROR_NOT_IMPLEMENTED;
- break;
- }
-
- EVP_PKEY_free(pkey);
-
- return res;
-}
-
-static nserror san_to_info(X509 *cert, struct ns_cert_san **prev_next)
-{
- int idx;
- int san_names_nb = -1;
- const GENERAL_NAME *current_name;
- const unsigned char *dns_name;
- struct ns_cert_san *isan;
-
- STACK_OF(GENERAL_NAME) *san_names = NULL;
-
- san_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
- if (san_names == NULL) {
- return NSERROR_OK;
- }
-
- san_names_nb = sk_GENERAL_NAME_num(san_names);
-
- /* Check each name within the extension */
- for (idx = 0; idx < san_names_nb; idx++) {
- current_name = sk_GENERAL_NAME_value(san_names, idx);
-
- if (current_name->type == GEN_DNS) {
- /* extract DNS name into info structure */
- dns_name = ns_ASN1_STRING_get0_data(current_name->d.dNSName);
-
- isan = malloc(sizeof(struct ns_cert_san));
- if (isan != NULL) {
- isan->name = strdup((const char *)dns_name);
- isan->next = NULL;
- *prev_next = isan;
- prev_next = &isan->next;
- }
- }
- }
-
- /* AmiSSL can't cope with the "correct" mechanism of freeing
- * the GENERAL_NAME stack, which is:
- * sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
- * So instead we do this open-coded loop which does the same:
- */
- for (idx = 0; idx < san_names_nb; idx++) {
- GENERAL_NAME *entry = sk_GENERAL_NAME_pop(san_names);
- GENERAL_NAME_free(entry);
- }
- sk_GENERAL_NAME_free(san_names);
-
- return NSERROR_OK;
-}
-
-static nserror
-der_to_certinfo(const uint8_t *der,
- size_t der_length,
- struct ns_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
- *
- * \note this is defined by standards (X.509 et al) to be one
- * less than the certificate version.
- */
- info->version = X509_get_version(cert) + 1;
-
- /* 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);
- info->not_before = calloc(1, buf->length + 1);
- if (info->not_before != NULL) {
- memcpy(info->not_before, buf->data, (unsigned)buf->length);
- }
- 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);
- info->not_after = calloc(1, buf->length + 1);
- if (info->not_after != NULL) {
- memcpy(info->not_after, buf->data, (unsigned)buf->length);
- }
- BUF_MEM_free(buf);
-
- /* signature type */
- info->sig_type = X509_get_signature_type(cert);
-
- /* signature algorithm */
- int pkey_nid = ns_X509_get_signature_nid(cert);
- if (pkey_nid != NID_undef) {
- const char* sslbuf = OBJ_nid2ln(pkey_nid);
- if (sslbuf != NULL) {
- info->sig_algor = strdup(sslbuf);
- }
- }
-
- /* 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) {
- info->serialnum = hexdup(tmp);
- OPENSSL_free(tmp);
- }
- BN_free(bignum);
- bignum = NULL;
- }
- }
-
- /* fingerprints */
- const EVP_MD *digest;
- unsigned int dig_len;
- unsigned char *buff;
- int rc;
-
- digest = EVP_sha1();
- buff = malloc(EVP_MD_size(digest));
- if (buff != NULL) {
- rc = X509_digest(cert, digest, buff, &dig_len);
- if ((rc == 1) && (dig_len == (unsigned int)EVP_MD_size(digest))) {
- info->sha1fingerprint = bindup(buff, dig_len);
- }
- free(buff);
- }
-
- digest = EVP_sha256();
- buff = malloc(EVP_MD_size(digest));
- if (buff != NULL) {
- rc = X509_digest(cert, digest, buff, &dig_len);
- if ((rc == 1) && (dig_len == (unsigned int)EVP_MD_size(digest))) {
- info->sha256fingerprint = bindup(buff, dig_len);
- }
- free(buff);
- }
-
- /* subject alternative names */
- san_to_info(cert, &info->san);
-
- /* issuer name */
- xname_to_info(X509_get_issuer_name(cert), &info->issuer_name);
-
- /* subject */
- xname_to_info(X509_get_subject_name(cert), &info->subject_name);
-
- /* public key */
- pkey_to_info(X509_get_pubkey(cert), &info->public_key);
-
- X509_free(cert);
-
- return NSERROR_OK;
-}
-
-/* copy certificate data */
-static nserror
-convert_chain_to_cert_info(const struct cert_chain *chain,
- struct ns_cert_info **cert_info_out)
-{
- struct ns_cert_info *certs;
- size_t depth;
- nserror res;
-
- certs = calloc(chain->depth, sizeof(struct ns_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;
-}
-
-#else
-static nserror
-convert_chain_to_cert_info(const struct cert_chain *chain,
- struct ns_cert_info **cert_info_out)
-{
- return NSERROR_NOT_IMPLEMENTED;
-}
-#endif
-
-
-static nserror
-format_certificate_name(struct fetch_about_context *ctx,
- struct ns_cert_name *cert_name)
-{
- nserror res;
- res = ssenddataf(ctx,
- "<tr><th>Common Name</th><td>%s</td></tr>\n",
- cert_name->common_name);
- if (res != NSERROR_OK) {
- return res;
- }
-
- if (cert_name->organisation != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Organisation</th><td>%s</td></tr>\n",
- cert_name->organisation);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (cert_name->organisation_unit != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Organisation Unit</th><td>%s</td></tr>\n",
- cert_name->organisation_unit);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (cert_name->locality != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Locality</th><td>%s</td></tr>\n",
- cert_name->locality);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (cert_name->province != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Privince</th><td>%s</td></tr>\n",
- cert_name->province);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (cert_name->country != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Country</th><td>%s</td></tr>\n",
- cert_name->country);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- return res;
-}
-
-/**
- * output formatted certificate subject alternate names
- */
-static nserror
-format_certificate_san(struct fetch_about_context *ctx,
- struct ns_cert_san *san)
-{
- nserror res;
-
- if (san == NULL) {
- return NSERROR_OK;
- }
-
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr><th>Alternative Names</th><td><hr></td></tr>\n");
- if (res != NSERROR_OK) {
- return res;
- }
-
- while (san != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>DNS Name</th><td>%s</td></tr>\n",
- san->name);
- if (res != NSERROR_OK) {
- return res;
- }
-
- san = san->next;
- }
-
- res = ssenddataf(ctx, "</table>\n");
-
- return res;
-
-}
-
-
-static nserror
-format_certificate_public_key(struct fetch_about_context *ctx,
- struct ns_cert_pkey *public_key)
-{
- nserror res;
-
- if (public_key->algor == NULL) {
- /* skip the table if no algorithm name */
- return NSERROR_OK;
- }
-
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr><th>Public Key</th><td><hr></td></tr>\n"
- "<tr><th>Algorithm</th><td>%s</td></tr>\n"
- "<tr><th>Key Size</th><td>%d</td></tr>\n",
- public_key->algor,
- public_key->size);
- if (res != NSERROR_OK) {
- return res;
- }
-
-
- if (public_key->exponent != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Exponent</th><td>%s</td></tr>\n",
- public_key->exponent);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (public_key->modulus != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Modulus</th><td class=\"data\">%s</td></tr>\n",
- public_key->modulus);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (public_key->curve != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Curve</th><td>%s</td></tr>\n",
- public_key->curve);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (public_key->public != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Public Value</th><td>%s</td></tr>\n",
- public_key->public);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- res = ssenddataf(ctx, "</table>\n");
-
- return res;
-}
-
-static nserror
-format_certificate_fingerprint(struct fetch_about_context *ctx,
- struct ns_cert_info *cert_info)
-{
- nserror res;
-
- if ((cert_info->sha1fingerprint == NULL) &&
- (cert_info->sha256fingerprint == NULL)) {
- /* skip the table if no fingerprints */
- return NSERROR_OK;
- }
-
-
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr><th>Fingerprints</th><td><hr></td></tr>\n");
- if (res != NSERROR_OK) {
- return res;
- }
-
- if (cert_info->sha256fingerprint != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>SHA-256</th><td class=\"data\">%s</td></tr>\n",
- cert_info->sha256fingerprint);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (cert_info->sha1fingerprint != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>SHA-1</th><td class=\"data\">%s</td></tr>\n",
- cert_info->sha1fingerprint);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- res = ssenddataf(ctx, "</table>\n");
-
- return res;
-}
-
-static nserror
-format_certificate(struct fetch_about_context *ctx,
- struct ns_cert_info *cert_info,
- size_t depth)
-{
- nserror res;
-
- res = ssenddataf(ctx,
- "<h2 id=\"%"PRIsizet"\" class=\"ns-border\">%s</h2>\n",
- depth, cert_info->subject_name.common_name);
- if (res != NSERROR_OK) {
- return res;
- }
-
- if (cert_info->err != SSL_CERT_ERR_OK) {
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr class=\"ns-even-fg-bad\">"
- "<th>Fault</th>"
- "<td>%s</td>"
- "</tr>"
- "</table>\n",
- messages_get_sslcode(cert_info->err));
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr><th>Issued To</th><td><hr></td></tr>\n");
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = format_certificate_name(ctx, &cert_info->subject_name);
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = ssenddataf(ctx,
- "</table>\n");
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr><th>Issued By</th><td><hr></td></tr>\n");
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = format_certificate_name(ctx, &cert_info->issuer_name);
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = ssenddataf(ctx,
- "</table>\n");
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr><th>Validity</th><td><hr></td></tr>\n"
- "<tr><th>Valid From</th><td>%s</td></tr>\n"
- "<tr><th>Valid Until</th><td>%s</td></tr>\n"
- "</table>\n",
- cert_info->not_before,
- cert_info->not_after);
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = format_certificate_san(ctx, cert_info->san);
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = format_certificate_public_key(ctx, &cert_info->public_key);
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = ssenddataf(ctx,
- "<table class=\"info\">\n"
- "<tr><th>Miscellaneous</th><td><hr></td></tr>\n");
- if (res != NSERROR_OK) {
- return res;
- }
-
- if (cert_info->serialnum != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Serial Number</th><td>%s</td></tr>\n",
- cert_info->serialnum);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- if (cert_info->sig_algor != NULL) {
- res = ssenddataf(ctx,
- "<tr><th>Signature Algorithm</th>"
- "<td>%s</td></tr>\n",
- cert_info->sig_algor);
- if (res != NSERROR_OK) {
- return res;
- }
- }
-
- res = ssenddataf(ctx,
- "<tr><th>Version</th><td>%ld</td></tr>\n"
- "</table>\n",
- cert_info->version);
- if (res != NSERROR_OK) {
- return res;
- }
-
- res = format_certificate_fingerprint(ctx, cert_info);
- if (res != NSERROR_OK) {
- return res;
- }
-
- return res;
-}
-
-/**
- * Handler to generate about:certificate page.
- *
- * Shows details of a certificate chain
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_certificate_handler(struct fetch_about_context *ctx)
-{
- int code = 200;
- nserror res;
- struct cert_chain *chain = NULL;
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, code);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html"))
- goto fetch_about_certificate_handler_aborted;
-
- /* page head */
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>NetSurf Browser Certificate Viewer</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body id=\"certificate\" class=\"ns-even-bg ns-even-fg ns-border\">\n"
- "<h1 class=\"ns-border\">Certificate</h1>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_certificate_handler_aborted;
- }
-
- res = cert_chain_from_query(ctx->url, &chain);
- if (res != NSERROR_OK) {
- res = ssenddataf(ctx, "<p>Could not process that</p>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_certificate_handler_aborted;
- }
- } else {
- struct ns_cert_info *cert_info;
- res = convert_chain_to_cert_info(chain, &cert_info);
- if (res == NSERROR_OK) {
- size_t depth;
- res = ssenddataf(ctx, "<ul>\n");
- if (res != NSERROR_OK) {
- free_ns_cert_info(cert_info);
- goto fetch_about_certificate_handler_aborted;
- }
-
- for (depth = 0; depth < chain->depth; depth++) {
- res = ssenddataf(ctx, "<li><a href=\"#%"PRIsizet"\">%s</a></li>\n",
- depth, (cert_info + depth)
- ->subject_name
- .common_name);
- if (res != NSERROR_OK) {
- free_ns_cert_info(cert_info);
- goto fetch_about_certificate_handler_aborted;
- }
-
- }
-
- res = ssenddataf(ctx, "</ul>\n");
- if (res != NSERROR_OK) {
- free_ns_cert_info(cert_info);
- goto fetch_about_certificate_handler_aborted;
- }
-
- for (depth = 0; depth < chain->depth; depth++) {
- res = format_certificate(ctx, cert_info + depth,
- depth);
- if (res != NSERROR_OK) {
- free_ns_cert_info(cert_info);
- goto fetch_about_certificate_handler_aborted;
- }
-
- }
- free_ns_cert_info(cert_info);
-
- } else {
- res = ssenddataf(ctx,
- "<p>Invalid certificate data</p>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_certificate_handler_aborted;
- }
- }
- }
-
-
- /* page footer */
- res = ssenddataf(ctx, "</body>\n</html>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_certificate_handler_aborted;
- }
-
- fetch_about_send_finished(ctx);
-
- cert_chain_free(chain);
-
- return true;
-
-fetch_about_certificate_handler_aborted:
- cert_chain_free(chain);
- return false;
-}
-
-
-/**
- * Handler to generate about scheme config page
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_config_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
- char buffer[1024];
- int slen = 0;
- unsigned int opt_loop = 0;
- int elen = 0; /* entry length */
- nserror res;
- bool even = false;
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html")) {
- goto fetch_about_config_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>NetSurf Browser Config</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body "
- "id =\"configlist\" "
- "class=\"ns-even-bg ns-even-fg ns-border\" "
- "style=\"overflow: hidden;\">\n"
- "<h1 class=\"ns-border\">NetSurf Browser Config</h1>\n"
- "<table class=\"config\">\n"
- "<tr><th>Option</th>"
- "<th>Type</th>"
- "<th>Provenance</th>"
- "<th>Setting</th></tr>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_config_handler_aborted;
- }
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buffer;
-
- do {
- if (even) {
- elen = nsoption_snoptionf(buffer + slen,
- sizeof buffer - slen,
- opt_loop,
- "<tr class=\"ns-even-bg\">"
- "<th class=\"ns-border\">%k</th>"
- "<td class=\"ns-border\">%t</td>"
- "<td class=\"ns-border\">%p</td>"
- "<td class=\"ns-border\">%V</td>"
- "</tr>\n");
- } else {
- elen = nsoption_snoptionf(buffer + slen,
- sizeof buffer - slen,
- opt_loop,
- "<tr class=\"ns-odd-bg\">"
- "<th class=\"ns-border\">%k</th>"
- "<td class=\"ns-border\">%t</td>"
- "<td class=\"ns-border\">%p</td>"
- "<td class=\"ns-border\">%V</td>"
- "</tr>\n");
- }
- if (elen <= 0)
- break; /* last option */
-
- if (elen >= (int) (sizeof buffer - slen)) {
- /* last entry would not fit in buffer, submit buffer */
- msg.data.header_or_data.len = slen;
- if (fetch_about_send_callback(&msg, ctx))
- goto fetch_about_config_handler_aborted;
- slen = 0;
- } else {
- /* normal addition */
- slen += elen;
- opt_loop++;
- even = !even;
- }
- } while (elen > 0);
-
- slen += snprintf(buffer + slen, sizeof buffer - slen,
- "</table>\n</body>\n</html>\n");
-
- msg.data.header_or_data.len = slen;
- if (fetch_about_send_callback(&msg, ctx))
- goto fetch_about_config_handler_aborted;
-
- fetch_about_send_finished(ctx);
-
- return true;
-
-fetch_about_config_handler_aborted:
- return false;
-}
-
-
-/**
- * Handler to generate the nscolours stylesheet
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_nscolours_handler(struct fetch_about_context *ctx)
-{
- nserror res;
- const char *stylesheet;
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/css; charset=utf-8")) {
- goto aborted;
- }
-
- res = nscolour_get_stylesheet(&stylesheet);
- if (res != NSERROR_OK) {
- goto aborted;
- }
-
- res = ssenddataf(ctx,
- "html {\n"
- "\tbackground-color: #%06x;\n"
- "}\n"
- "%s",
- colour_rb_swap(nscolours[NSCOLOUR_WIN_ODD_BG]),
- stylesheet);
- if (res != NSERROR_OK) {
- goto aborted;
- }
-
- fetch_about_send_finished(ctx);
-
- return true;
-
-aborted:
-
- return false;
-}
-
-
-/**
- * Generate the text of a Choices file which represents the current
- * in use options.
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_choices_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
- char buffer[1024];
- int code = 200;
- int slen;
- unsigned int opt_loop = 0;
- int res = 0;
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, code);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
- goto fetch_about_choices_handler_aborted;
-
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf = (const uint8_t *) buffer;
-
- slen = snprintf(buffer, sizeof buffer,
- "# Automatically generated current NetSurf browser Choices\n");
-
- do {
- res = nsoption_snoptionf(buffer + slen,
- sizeof buffer - slen,
- opt_loop,
- "%k:%v\n");
- if (res <= 0)
- break; /* last option */
-
- if (res >= (int) (sizeof buffer - slen)) {
- /* last entry would not fit in buffer, submit buffer */
- msg.data.header_or_data.len = slen;
- if (fetch_about_send_callback(&msg, ctx))
- goto fetch_about_choices_handler_aborted;
- slen = 0;
- } else {
- /* normal addition */
- slen += res;
- opt_loop++;
- }
- } while (res > 0);
-
- msg.data.header_or_data.len = slen;
- if (fetch_about_send_callback(&msg, ctx))
- goto fetch_about_choices_handler_aborted;
-
- fetch_about_send_finished(ctx);
-
- return true;
-
-fetch_about_choices_handler_aborted:
- return false;
-}
-
-
-typedef struct {
- const char *leaf;
- const char *modtype;
-} modification_t;
-
-/**
- * Generate the text of an svn testament which represents the current
- * build-tree status
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_testament_handler(struct fetch_about_context *ctx)
-{
- nserror res;
- static modification_t modifications[] = WT_MODIFICATIONS;
- int modidx; /* midification index */
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
- goto fetch_about_testament_handler_aborted;
-
- res = ssenddataf(ctx,
- "# Automatically generated by NetSurf build system\n\n");
- if (res != NSERROR_OK) {
- goto fetch_about_testament_handler_aborted;
- }
-
- res = ssenddataf(ctx,
-#if defined(WT_BRANCHISTRUNK) || defined(WT_BRANCHISMASTER)
- "# This is a *DEVELOPMENT* build from the main line.\n\n"
-#elif defined(WT_BRANCHISTAG) && (WT_MODIFIED == 0)
- "# This is a tagged build of NetSurf\n"
-#ifdef WT_TAGIS
- "# The tag used was '" WT_TAGIS "'\n\n"
-#else
- "\n"
-#endif
-#elif defined(WT_NO_SVN) || defined(WT_NO_GIT)
- "# This NetSurf was built outside of our revision "
- "control environment.\n"
- "# This testament is therefore not very useful.\n\n"
-#else
- "# This NetSurf was built from a branch (" WT_BRANCHPATH ").\n\n"
-#endif
-#if defined(CI_BUILD)
- "# This build carries the CI build number '" CI_BUILD "'\n\n"
-#endif
- );
- if (res != NSERROR_OK) {
- goto fetch_about_testament_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "Built by %s (%s) from %s at revision %s on %s\n\n",
- GECOS, USERNAME, WT_BRANCHPATH, WT_REVID, WT_COMPILEDATE);
- if (res != NSERROR_OK) {
- goto fetch_about_testament_handler_aborted;
- }
-
- res = ssenddataf(ctx, "Built on %s in %s\n\n", WT_HOSTNAME, WT_ROOT);
- if (res != NSERROR_OK) {
- goto fetch_about_testament_handler_aborted;
- }
-
- if (WT_MODIFIED > 0) {
- res = ssenddataf(ctx,
- "Working tree has %d modification%s\n\n",
- WT_MODIFIED, WT_MODIFIED == 1 ? "" : "s");
- } else {
- res = ssenddataf(ctx, "Working tree is not modified.\n");
- }
- if (res != NSERROR_OK) {
- goto fetch_about_testament_handler_aborted;
- }
-
- for (modidx = 0; modidx < WT_MODIFIED; ++modidx) {
- res = ssenddataf(ctx,
- " %s %s\n",
- modifications[modidx].modtype,
- modifications[modidx].leaf);
- if (res != NSERROR_OK) {
- goto fetch_about_testament_handler_aborted;
- }
- }
-
- fetch_about_send_finished(ctx);
-
- return true;
-
-fetch_about_testament_handler_aborted:
- return false;
-}
-
-
-/**
- * Handler to generate about scheme logo page
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_logo_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
-
- /* content is going to return redirect */
- fetch_set_http_code(ctx->fetchh, 302);
-
- msg.type = FETCH_REDIRECT;
- msg.data.redirect = "resource:netsurf.png";
-
- fetch_about_send_callback(&msg, ctx);
-
- return true;
-}
-
-
-/**
- * Handler to generate about scheme welcome page
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_welcome_handler(struct fetch_about_context *ctx)
-{
- fetch_msg msg;
-
- /* content is going to return redirect */
- fetch_set_http_code(ctx->fetchh, 302);
-
- msg.type = FETCH_REDIRECT;
- msg.data.redirect = "resource:welcome.html";
-
- fetch_about_send_callback(&msg, ctx);
-
- return true;
-}
-
-
-/**
- * generate the description of the login query
- */
-static nserror
-get_authentication_description(struct nsurl *url,
- const char *realm,
- const char *username,
- const char *password,
- char **out_str)
-{
- nserror res;
- char *url_s;
- size_t url_l;
- char *str = NULL;
- const char *key;
-
- res = nsurl_get(url, NSURL_HOST, &url_s, &url_l);
- if (res != NSERROR_OK) {
- return res;
- }
-
- if ((*username == 0) && (*password == 0)) {
- key = "LoginDescription";
- } else {
- key = "LoginAgain";
- }
-
- str = messages_get_buff(key, url_s, realm);
- if (str != NULL) {
- NSLOG(netsurf, INFO,
- "key:%s url:%s realm:%s str:%s",
- key, url_s, realm, str);
- *out_str = str;
- } else {
- res = NSERROR_NOMEM;
- }
-
- free(url_s);
-
- return res;
-}
-
-
-/**
- * generate a generic query description
- */
-static nserror
-get_query_description(struct nsurl *url,
- const char *key,
- char **out_str)
-{
- nserror res;
- char *url_s;
- size_t url_l;
- char *str = NULL;
-
- /* get the host in question */
- res = nsurl_get(url, NSURL_HOST, &url_s, &url_l);
- if (res != NSERROR_OK) {
- return res;
- }
-
- /* obtain the description with the url substituted */
- str = messages_get_buff(key, url_s);
- if (str == NULL) {
- res = NSERROR_NOMEM;
- } else {
- *out_str = str;
- }
-
- free(url_s);
-
- return res;
-}
-
-
-/**
- * Handler to generate about scheme authentication query page
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_query_auth_handler(struct fetch_about_context *ctx)
-{
- nserror res;
- char *url_s;
- size_t url_l;
- const char *realm = "";
- const char *username = "";
- const char *password = "";
- const char *title;
- char *description = NULL;
- struct nsurl *siteurl = NULL;
- const struct fetch_multipart_data *curmd; /* mutipart data iterator */
-
- /* extract parameters from multipart post data */
- curmd = ctx->multipart;
- while (curmd != NULL) {
- if (strcmp(curmd->name, "siteurl") == 0) {
- res = nsurl_create(curmd->value, &siteurl);
- if (res != NSERROR_OK) {
- return fetch_about_srverror(ctx);
- }
- } else if (strcmp(curmd->name, "realm") == 0) {
- realm = curmd->value;
- } else if (strcmp(curmd->name, "username") == 0) {
- username = curmd->value;
- } else if (strcmp(curmd->name, "password") == 0) {
- password = curmd->value;
- }
- curmd = curmd->next;
- }
-
- if (siteurl == NULL) {
- return fetch_about_srverror(ctx);
- }
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- title = messages_get("LoginTitle");
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>%s</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"authentication\">\n"
- "<h1 class=\"ns-border\">%s</h1>\n",
- title, title);
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<form method=\"post\""
- " enctype=\"multipart/form-data\">");
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = get_authentication_description(siteurl,
- realm,
- username,
- password,
- &description);
- if (res == NSERROR_OK) {
- res = ssenddataf(ctx, "<p>%s</p>", description);
- free(description);
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
- }
-
- res = ssenddataf(ctx, "<table>");
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<tr>"
- "<th><label for=\"name\">%s:</label></th>"
- "<td><input type=\"text\" id=\"username\" "
- "name=\"username\" value=\"%s\"></td>"
- "</tr>",
- messages_get("Username"), username);
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<tr>"
- "<th><label for=\"password\">%s:</label></th>"
- "<td><input type=\"password\" id=\"password\" "
- "name=\"password\" value=\"%s\"></td>"
- "</tr>",
- messages_get("Password"), password);
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = ssenddataf(ctx, "</table>");
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<div id=\"buttons\">"
- "<input type=\"submit\" id=\"login\" name=\"login\" "
- "value=\"%s\" class=\"default-action\">"
- "<input type=\"submit\" id=\"cancel\" name=\"cancel\" "
- "value=\"%s\">"
- "</div>",
- messages_get("Login"),
- messages_get("Cancel"));
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
- if (res != NSERROR_OK) {
- url_s = strdup("");
- }
- res = ssenddataf(ctx,
- "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
- url_s);
- free(url_s);
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<input type=\"hidden\" name=\"realm\" value=\"%s\">",
- realm);
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- res = ssenddataf(ctx, "</form></body>\n</html>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_query_auth_handler_aborted;
- }
-
- fetch_about_send_finished(ctx);
-
- nsurl_unref(siteurl);
-
- return true;
-
-fetch_about_query_auth_handler_aborted:
-
- nsurl_unref(siteurl);
-
- return false;
-}
-
-
-/**
- * Handler to generate about scheme privacy query page
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_query_privacy_handler(struct fetch_about_context *ctx)
-{
- nserror res;
- char *url_s;
- size_t url_l;
- const char *reason = "";
- const char *title;
- struct nsurl *siteurl = NULL;
- char *description = NULL;
- const char *chainurl = NULL;
- const struct fetch_multipart_data *curmd; /* mutipart data iterator */
-
- /* extract parameters from multipart post data */
- curmd = ctx->multipart;
- while (curmd != NULL) {
- if (strcmp(curmd->name, "siteurl") == 0) {
- res = nsurl_create(curmd->value, &siteurl);
- if (res != NSERROR_OK) {
- return fetch_about_srverror(ctx);
- }
- } else if (strcmp(curmd->name, "reason") == 0) {
- reason = curmd->value;
- } else if (strcmp(curmd->name, "chainurl") == 0) {
- chainurl = curmd->value;
- }
- curmd = curmd->next;
- }
-
- if (siteurl == NULL) {
- return fetch_about_srverror(ctx);
- }
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
- goto fetch_about_query_ssl_handler_aborted;
- }
-
- title = messages_get("PrivacyTitle");
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>%s</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"privacy\">\n"
- "<h1 class=\"ns-border ns-odd-fg-bad\">%s</h1>\n",
- title, title);
- if (res != NSERROR_OK) {
- goto fetch_about_query_ssl_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<form method=\"post\""
- " enctype=\"multipart/form-data\">");
- if (res != NSERROR_OK) {
- goto fetch_about_query_ssl_handler_aborted;
- }
-
- res = get_query_description(siteurl,
- "PrivacyDescription",
- &description);
- if (res == NSERROR_OK) {
- res = ssenddataf(ctx, "<div><p>%s</p></div>", description);
- free(description);
- if (res != NSERROR_OK) {
- goto fetch_about_query_ssl_handler_aborted;
- }
- }
-
- if (chainurl == NULL) {
- res = ssenddataf(ctx,
- "<div><p>%s</p></div>"
- "<div><p>%s</p></div>",
- reason,
- messages_get("ViewCertificatesNotPossible"));
- } else {
- res = ssenddataf(ctx,
- "<div><p>%s</p></div>"
- "<div><p><a href=\"%s\" target=\"_blank\">%s</a></p></div>",
- reason,
- chainurl,
- messages_get("ViewCertificates"));
- }
- if (res != NSERROR_OK) {
- goto fetch_about_query_ssl_handler_aborted;
- }
- res = ssenddataf(ctx,
- "<div id=\"buttons\">"
- "<input type=\"submit\" id=\"back\" name=\"back\" "
- "value=\"%s\" class=\"default-action\">"
- "<input type=\"submit\" id=\"proceed\" name=\"proceed\" "
- "value=\"%s\">"
- "</div>",
- messages_get("Backtosafety"),
- messages_get("Proceed"));
- if (res != NSERROR_OK) {
- goto fetch_about_query_ssl_handler_aborted;
- }
-
- res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
- if (res != NSERROR_OK) {
- url_s = strdup("");
- }
- res = ssenddataf(ctx,
- "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
- url_s);
- free(url_s);
- if (res != NSERROR_OK) {
- goto fetch_about_query_ssl_handler_aborted;
- }
-
- res = ssenddataf(ctx, "</form></body>\n</html>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_query_ssl_handler_aborted;
- }
-
- fetch_about_send_finished(ctx);
-
- nsurl_unref(siteurl);
-
- return true;
-
-fetch_about_query_ssl_handler_aborted:
- nsurl_unref(siteurl);
-
- return false;
-}
-
-
-/**
- * Handler to generate about scheme timeout query page
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool fetch_about_query_timeout_handler(struct fetch_about_context *ctx)
-{
- nserror res;
- char *url_s;
- size_t url_l;
- const char *reason = "";
- const char *title;
- struct nsurl *siteurl = NULL;
- char *description = NULL;
- const struct fetch_multipart_data *curmd; /* mutipart data iterator */
-
- /* extract parameters from multipart post data */
- curmd = ctx->multipart;
- while (curmd != NULL) {
- if (strcmp(curmd->name, "siteurl") == 0) {
- res = nsurl_create(curmd->value, &siteurl);
- if (res != NSERROR_OK) {
- return fetch_about_srverror(ctx);
- }
- } else if (strcmp(curmd->name, "reason") == 0) {
- reason = curmd->value;
- }
- curmd = curmd->next;
- }
-
- if (siteurl == NULL) {
- return fetch_about_srverror(ctx);
- }
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
- goto fetch_about_query_timeout_handler_aborted;
- }
-
- title = messages_get("TimeoutTitle");
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>%s</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"timeout\">\n"
- "<h1 class=\"ns-border ns-odd-fg-bad\">%s</h1>\n",
- title, title);
- if (res != NSERROR_OK) {
- goto fetch_about_query_timeout_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<form method=\"post\""
- " enctype=\"multipart/form-data\">");
- if (res != NSERROR_OK) {
- goto fetch_about_query_timeout_handler_aborted;
- }
-
- res = get_query_description(siteurl,
- "TimeoutDescription",
- &description);
- if (res == NSERROR_OK) {
- res = ssenddataf(ctx, "<div><p>%s</p></div>", description);
- free(description);
- if (res != NSERROR_OK) {
- goto fetch_about_query_timeout_handler_aborted;
- }
- }
- res = ssenddataf(ctx, "<div><p>%s</p></div>", reason);
- if (res != NSERROR_OK) {
- goto fetch_about_query_timeout_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<div id=\"buttons\">"
- "<input type=\"submit\" id=\"back\" name=\"back\" "
- "value=\"%s\" class=\"default-action\">"
- "<input type=\"submit\" id=\"retry\" name=\"retry\" "
- "value=\"%s\">"
- "</div>",
- messages_get("Backtoprevious"),
- messages_get("TryAgain"));
- if (res != NSERROR_OK) {
- goto fetch_about_query_timeout_handler_aborted;
- }
-
- res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
- if (res != NSERROR_OK) {
- url_s = strdup("");
- }
- res = ssenddataf(ctx,
- "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
- url_s);
- free(url_s);
- if (res != NSERROR_OK) {
- goto fetch_about_query_timeout_handler_aborted;
- }
-
- res = ssenddataf(ctx, "</form></body>\n</html>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_query_timeout_handler_aborted;
- }
-
- fetch_about_send_finished(ctx);
-
- nsurl_unref(siteurl);
-
- return true;
-
-fetch_about_query_timeout_handler_aborted:
- nsurl_unref(siteurl);
-
- return false;
-}
-
-
-/**
- * Handler to generate about scheme fetch error query page
- *
- * \param ctx The fetcher context.
- * \return true if handled false if aborted.
- */
-static bool
-fetch_about_query_fetcherror_handler(struct fetch_about_context *ctx)
-{
- nserror res;
- char *url_s;
- size_t url_l;
- const char *reason = "";
- const char *title;
- struct nsurl *siteurl = NULL;
- char *description = NULL;
- const struct fetch_multipart_data *curmd; /* mutipart data iterator */
-
- /* extract parameters from multipart post data */
- curmd = ctx->multipart;
- while (curmd != NULL) {
- if (strcmp(curmd->name, "siteurl") == 0) {
- res = nsurl_create(curmd->value, &siteurl);
- if (res != NSERROR_OK) {
- return fetch_about_srverror(ctx);
- }
- } else if (strcmp(curmd->name, "reason") == 0) {
- reason = curmd->value;
- }
- curmd = curmd->next;
- }
-
- if (siteurl == NULL) {
- return fetch_about_srverror(ctx);
- }
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
-
- title = messages_get("FetchErrorTitle");
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>%s</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"fetcherror\">\n"
- "<h1 class=\"ns-border ns-odd-fg-bad\">%s</h1>\n",
- title, title);
- if (res != NSERROR_OK) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<form method=\"post\""
- " enctype=\"multipart/form-data\">");
- if (res != NSERROR_OK) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
-
- res = get_query_description(siteurl,
- "FetchErrorDescription",
- &description);
- if (res == NSERROR_OK) {
- res = ssenddataf(ctx, "<div><p>%s</p></div>", description);
- free(description);
- if (res != NSERROR_OK) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
- }
- res = ssenddataf(ctx, "<div><p>%s</p></div>", reason);
- if (res != NSERROR_OK) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
-
- res = ssenddataf(ctx,
- "<div id=\"buttons\">"
- "<input type=\"submit\" id=\"back\" name=\"back\" "
- "value=\"%s\" class=\"default-action\">"
- "<input type=\"submit\" id=\"retry\" name=\"retry\" "
- "value=\"%s\">"
- "</div>",
- messages_get("Backtoprevious"),
- messages_get("TryAgain"));
- if (res != NSERROR_OK) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
-
- res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
- if (res != NSERROR_OK) {
- url_s = strdup("");
- }
- res = ssenddataf(ctx,
- "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
- url_s);
- free(url_s);
- if (res != NSERROR_OK) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
-
- res = ssenddataf(ctx, "</form></body>\n</html>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_query_fetcherror_handler_aborted;
- }
-
- fetch_about_send_finished(ctx);
-
- nsurl_unref(siteurl);
-
- return true;
-
-fetch_about_query_fetcherror_handler_aborted:
- nsurl_unref(siteurl);
-
- return false;
-}
-
-
-/* Forward declaration because this handler requires the handler table. */
-static bool fetch_about_about_handler(struct fetch_about_context *ctx);
-
-/**
- * List of about paths and their handlers
- */
-struct about_handlers about_handler_list[] = {
- {
- "credits",
- SLEN("credits"),
- NULL,
- fetch_about_credits_handler,
- false
- },
- {
- "licence",
- SLEN("licence"),
- NULL,
- fetch_about_licence_handler,
- false
- },
- {
- "license",
- SLEN("license"),
- NULL,
- fetch_about_licence_handler,
- true
- },
- {
- "welcome",
- SLEN("welcome"),
- NULL,
- fetch_about_welcome_handler,
- false
- },
- {
- "config",
- SLEN("config"),
- NULL,
- fetch_about_config_handler,
- false
- },
- {
- "Choices",
- SLEN("Choices"),
- NULL,
- fetch_about_choices_handler,
- false
- },
- {
- "testament",
- SLEN("testament"),
- NULL,
- fetch_about_testament_handler,
- false
- },
- {
- "about",
- SLEN("about"),
- NULL,
- fetch_about_about_handler,
- true
- },
- {
- "nscolours.css",
- SLEN("nscolours.css"),
- NULL,
- fetch_about_nscolours_handler,
- true
- },
- {
- "logo",
- SLEN("logo"),
- NULL,
- fetch_about_logo_handler,
- true
- },
- {
- /* details about the image cache */
- "imagecache",
- SLEN("imagecache"),
- NULL,
- fetch_about_imagecache_handler,
- true
- },
- {
- /* The default blank page */
- "blank",
- SLEN("blank"),
- NULL,
- fetch_about_blank_handler,
- true
- },
- {
- /* details about a certificate */
- "certificate",
- SLEN("certificate"),
- NULL,
- fetch_about_certificate_handler,
- true
- },
- {
- "query/auth",
- SLEN("query/auth"),
- NULL,
- fetch_about_query_auth_handler,
- true
- },
- {
- "query/ssl",
- SLEN("query/ssl"),
- NULL,
- fetch_about_query_privacy_handler,
- true
- },
- {
- "query/timeout",
- SLEN("query/timeout"),
- NULL,
- fetch_about_query_timeout_handler,
- true
- },
- {
- "query/fetcherror",
- SLEN("query/fetcherror"),
- NULL,
- fetch_about_query_fetcherror_handler,
- true
- }
-};
-
-#define about_handler_list_len \
- (sizeof(about_handler_list) / sizeof(struct about_handlers))
-
-/**
- * List all the valid about: paths available
- *
- * \param ctx The fetch context.
- * \return true for sucess or false to generate an error.
- */
-static bool fetch_about_about_handler(struct fetch_about_context *ctx)
-{
- nserror res;
- unsigned int abt_loop = 0;
-
- /* content is going to return ok */
- fetch_set_http_code(ctx->fetchh, 200);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/html"))
- goto fetch_about_config_handler_aborted;
-
- res = ssenddataf(ctx,
- "<html>\n<head>\n"
- "<title>List of NetSurf pages</title>\n"
- "<link rel=\"stylesheet\" type=\"text/css\" "
- "href=\"resource:internal.css\">\n"
- "</head>\n"
- "<body class=\"ns-even-bg ns-even-fg ns-border\">\n"
- "<h1 class =\"ns-border\">List of NetSurf pages</h1>\n"
- "<ul>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_config_handler_aborted;
- }
-
- for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
-
- /* Skip over hidden entries */
- if (about_handler_list[abt_loop].hidden)
- continue;
-
- res = ssenddataf(ctx,
- "<li><a href=\"about:%s\">about:%s</a></li>\n",
- about_handler_list[abt_loop].name,
- about_handler_list[abt_loop].name);
- if (res != NSERROR_OK) {
- goto fetch_about_config_handler_aborted;
- }
- }
-
- res = ssenddataf(ctx, "</ul>\n</body>\n</html>\n");
- if (res != NSERROR_OK) {
- goto fetch_about_config_handler_aborted;
- }
-
- fetch_about_send_finished(ctx);
-
- return true;
-
-fetch_about_config_handler_aborted:
- return false;
-}
-
-static bool
-fetch_about_404_handler(struct fetch_about_context *ctx)
-{
- nserror res;
-
- /* content is going to return 404 */
- fetch_set_http_code(ctx->fetchh, 404);
-
- /* content type */
- if (fetch_about_send_header(ctx, "Content-Type: text/plain; charset=utf-8")) {
- return false;
- }
-
- res = ssenddataf(ctx, "Unknown page: %s", nsurl_access(ctx->url));
- if (res != NSERROR_OK) {
- return false;
- }
-
- fetch_about_send_finished(ctx);
-
- return true;
-}
-
-/**
- * callback to initialise the about scheme fetcher.
- */
-static bool fetch_about_initialise(lwc_string *scheme)
-{
- unsigned int abt_loop = 0;
- lwc_error error;
-
- for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
- error = lwc_intern_string(about_handler_list[abt_loop].name,
- about_handler_list[abt_loop].name_len,
- &about_handler_list[abt_loop].lname);
- if (error != lwc_error_ok) {
- while (abt_loop-- != 0) {
- lwc_string_unref(about_handler_list[abt_loop].lname);
- }
- return false;
- }
- }
-
- return true;
-}
-
-
-/**
- * callback to finalise the about scheme fetcher.
- */
-static void fetch_about_finalise(lwc_string *scheme)
-{
- unsigned int abt_loop = 0;
- for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
- lwc_string_unref(about_handler_list[abt_loop].lname);
- }
-}
-
-
-static bool fetch_about_can_fetch(const nsurl *url)
-{
- return true;
-}
-
-
-/**
- * callback to set up a about scheme fetch.
- *
- * \param post_urlenc post data in urlenc format, owned by the llcache object
- * hence valid the entire lifetime of the fetch.
- * \param post_multipart post data in multipart format, owned by the llcache
- * object hence valid the entire lifetime of the fetch.
- */
-static void *
-fetch_about_setup(struct fetch *fetchh,
- nsurl *url,
- bool only_2xx,
- bool downgrade_tls,
- const char *post_urlenc,
- const struct fetch_multipart_data *post_multipart,
- const char **headers)
-{
- struct fetch_about_context *ctx;
- unsigned int handler_loop;
- lwc_string *path;
- bool match;
-
- ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL)
- return NULL;
-
- path = nsurl_get_component(url, NSURL_PATH);
-
- for (handler_loop = 0;
- handler_loop < about_handler_list_len;
- handler_loop++) {
- if (lwc_string_isequal(path,
- about_handler_list[handler_loop].lname,
- &match) == lwc_error_ok && match) {
- ctx->handler = about_handler_list[handler_loop].handler;
- break;
- }
- }
-
- if (path != NULL)
- lwc_string_unref(path);
-
- ctx->fetchh = fetchh;
- ctx->url = nsurl_ref(url);
- ctx->multipart = post_multipart;
-
- RING_INSERT(ring, ctx);
-
- return ctx;
-}
-
-
-/**
- * callback to free a about scheme fetch
- */
-static void fetch_about_free(void *ctx)
-{
- struct fetch_about_context *c = ctx;
- nsurl_unref(c->url);
- free(ctx);
-}
-
-
-/**
- * callback to start an about scheme fetch
- */
-static bool fetch_about_start(void *ctx)
-{
- return true;
-}
-
-
-/**
- * callback to abort a about fetch
- */
-static void fetch_about_abort(void *ctx)
-{
- struct fetch_about_context *c = ctx;
-
- /* To avoid the poll loop having to deal with the fetch context
- * disappearing from under it, we simply flag the abort here.
- * The poll loop itself will perform the appropriate cleanup.
- */
- c->aborted = true;
-}
-
-
-/**
- * callback to poll for additional about fetch contents
- */
-static void fetch_about_poll(lwc_string *scheme)
-{
- struct fetch_about_context *c, *save_ring = NULL;
-
- /* Iterate over ring, processing each pending fetch */
- while (ring != NULL) {
- /* Take the first entry from the ring */
- c = ring;
- RING_REMOVE(ring, c);
-
- /* Ignore fetches that have been flagged as locked.
- * This allows safe re-entrant calls to this function.
- * Re-entrancy can occur if, as a result of a callback,
- * the interested party causes fetch_poll() to be called
- * again.
- */
- if (c->locked == true) {
- RING_INSERT(save_ring, c);
- continue;
- }
-
- /* Only process non-aborted fetches */
- if (c->aborted == false) {
- /* about fetches can be processed in one go */
- if (c->handler == NULL) {
- fetch_about_404_handler(c);
- } else {
- c->handler(c);
- }
- }
-
- /* And now finish */
- fetch_remove_from_queues(c->fetchh);
- fetch_free(c->fetchh);
- }
-
- /* Finally, if we saved any fetches which were locked, put them back
- * into the ring for next time
- */
- ring = save_ring;
-}
-
-
-nserror fetch_about_register(void)
-{
- lwc_string *scheme = lwc_string_ref(corestring_lwc_about);
- const struct fetcher_operation_table fetcher_ops = {
- .initialise = fetch_about_initialise,
- .acceptable = fetch_about_can_fetch,
- .setup = fetch_about_setup,
- .start = fetch_about_start,
- .abort = fetch_about_abort,
- .free = fetch_about_free,
- .poll = fetch_about_poll,
- .finalise = fetch_about_finalise
- };
-
- return fetcher_add(scheme, &fetcher_ops);
-}
diff --git a/content/fetchers/about/Makefile b/content/fetchers/about/Makefile
new file mode 100644
index 000000000..4f12a0626
--- /dev/null
+++ b/content/fetchers/about/Makefile
@@ -0,0 +1,20 @@
+# about fetcher sources
+
+S_FETCHER_ABOUT := \
+ about.c \
+ blank.c \
+ certificate.c \
+ chart.c \
+ choices.c \
+ config.c \
+ imagecache.c \
+ nscolours.c \
+ query.c \
+ query_auth.c \
+ query_fetcherror.c \
+ query_privacy.c \
+ query_timeout.c \
+ testament.c
+
+# The following files depend on the testament
+content/fetchers/about/testament.c: testament $(OBJROOT)/testament.h
diff --git a/content/fetchers/about/about.c b/content/fetchers/about/about.c
new file mode 100644
index 000000000..651894249
--- /dev/null
+++ b/content/fetchers/about/about.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ *
+ * URL handling for the "about" scheme.
+ *
+ * Based on the data fetcher by Rob Kendrick
+ * This fetcher provides a simple scheme for the user to access
+ * information from the browser from a known, fixed URL.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "netsurf/inttypes.h"
+
+#include "utils/errors.h"
+#include "utils/nsurl.h"
+#include "utils/corestrings.h"
+#include "utils/utils.h"
+#include "utils/ring.h"
+
+#include "content/fetch.h"
+#include "content/fetchers.h"
+
+#include "private.h"
+#include "about.h"
+#include "blank.h"
+#include "certificate.h"
+#include "config.h"
+#include "chart.h"
+#include "choices.h"
+#include "imagecache.h"
+#include "nscolours.h"
+#include "query.h"
+#include "query_auth.h"
+#include "query_fetcherror.h"
+#include "query_privacy.h"
+#include "query_timeout.h"
+#include "atestament.h"
+
+typedef bool (*fetch_about_handler)(struct fetch_about_context *);
+
+/**
+ * Context for an about fetch
+ */
+struct fetch_about_context {
+ struct fetch_about_context *r_next, *r_prev;
+
+ struct fetch *fetchh; /**< Handle for this fetch */
+
+ bool aborted; /**< Flag indicating fetch has been aborted */
+ bool locked; /**< Flag indicating entry is already entered */
+
+ nsurl *url; /**< The full url the fetch refers to */
+
+ const struct fetch_multipart_data *multipart; /**< post data */
+
+ fetch_about_handler handler;
+};
+
+static struct fetch_about_context *ring = NULL;
+
+/**
+ * handler info for about scheme
+ */
+struct about_handlers {
+ const char *name; /**< name to match in url */
+ int name_len;
+ lwc_string *lname; /**< Interned name */
+ fetch_about_handler handler; /**< handler for the url */
+ bool hidden; /**< If entry should be hidden in listing */
+};
+
+
+
+/**
+ * issue fetch callbacks with locking
+ */
+static bool
+fetch_about_send_callback(const fetch_msg *msg, struct fetch_about_context *ctx)
+{
+ ctx->locked = true;
+ fetch_send_callback(msg, ctx->fetchh);
+ ctx->locked = false;
+
+ return ctx->aborted;
+}
+
+/* exported interface documented in about/private.h */
+bool
+fetch_about_send_finished(struct fetch_about_context *ctx)
+{
+ fetch_msg msg;
+ msg.type = FETCH_FINISHED;
+ return fetch_about_send_callback(&msg, ctx);
+}
+
+/* exported interface documented in about/private.h */
+bool fetch_about_set_http_code(struct fetch_about_context *ctx, long code)
+{
+ fetch_set_http_code(ctx->fetchh, code);
+
+ return ctx->aborted;
+}
+
+/* exported interface documented in about/private.h */
+bool
+fetch_about_send_header(struct fetch_about_context *ctx, const char *fmt, ...)
+{
+ char header[64];
+ fetch_msg msg;
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsnprintf(header, sizeof header, fmt, ap);
+
+ va_end(ap);
+
+ msg.type = FETCH_HEADER;
+ msg.data.header_or_data.buf = (const uint8_t *) header;
+ msg.data.header_or_data.len = strlen(header);
+
+ return fetch_about_send_callback(&msg, ctx);
+}
+
+/* exported interface documented in about/private.h */
+nserror
+fetch_about_senddata(struct fetch_about_context *ctx, const uint8_t *data, size_t data_len)
+{
+ fetch_msg msg;
+
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = data;
+ msg.data.header_or_data.len = data_len;
+
+ if (fetch_about_send_callback(&msg, ctx)) {
+ return NSERROR_INVALID;
+ }
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in about/private.h */
+nserror
+fetch_about_ssenddataf(struct fetch_about_context *ctx, const char *fmt, ...)
+{
+ char buffer[1024];
+ char *dbuff;
+ fetch_msg msg;
+ va_list ap;
+ int slen;
+
+ va_start(ap, fmt);
+
+ slen = vsnprintf(buffer, sizeof(buffer), fmt, ap);
+
+ va_end(ap);
+
+ if (slen < (int)sizeof(buffer)) {
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = (const uint8_t *) buffer;
+ msg.data.header_or_data.len = slen;
+
+ if (fetch_about_send_callback(&msg, ctx)) {
+ return NSERROR_INVALID;
+ }
+
+ return NSERROR_OK;
+ }
+
+ dbuff = malloc(slen + 1);
+ if (dbuff == NULL) {
+ return NSERROR_NOSPACE;
+ }
+
+ va_start(ap, fmt);
+
+ slen = vsnprintf(dbuff, slen + 1, fmt, ap);
+
+ va_end(ap);
+
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf = (const uint8_t *)dbuff;
+ msg.data.header_or_data.len = slen;
+
+ if (fetch_about_send_callback(&msg, ctx)) {
+ free(dbuff);
+ return NSERROR_INVALID;
+ }
+
+ free(dbuff);
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in about/private.h */
+nsurl *fetch_about_get_url(struct fetch_about_context *ctx)
+{
+ return ctx->url;
+}
+
+
+/* exported interface documented in about/private.h */
+const struct fetch_multipart_data *
+fetch_about_get_multipart(struct fetch_about_context *ctx)
+{
+ return ctx->multipart;
+}
+
+
+/* exported interface documented in about/private.h */
+bool fetch_about_srverror(struct fetch_about_context *ctx)
+{
+ nserror res;
+
+ fetch_set_http_code(ctx->fetchh, 500);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
+ return false;
+
+ res = fetch_about_ssenddataf(ctx, "Server error 500");
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+}
+
+
+/**
+ * Handler to generate about scheme credits page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+static bool fetch_about_credits_handler(struct fetch_about_context *ctx)
+{
+ fetch_msg msg;
+
+ /* content is going to return redirect */
+ fetch_set_http_code(ctx->fetchh, 302);
+
+ msg.type = FETCH_REDIRECT;
+ msg.data.redirect = "resource:credits.html";
+
+ fetch_about_send_callback(&msg, ctx);
+
+ return true;
+}
+
+
+/**
+ * Handler to generate about scheme licence page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+static bool fetch_about_licence_handler(struct fetch_about_context *ctx)
+{
+ fetch_msg msg;
+
+ /* content is going to return redirect */
+ fetch_set_http_code(ctx->fetchh, 302);
+
+ msg.type = FETCH_REDIRECT;
+ msg.data.redirect = "resource:licence.html";
+
+ fetch_about_send_callback(&msg, ctx);
+
+ return true;
+}
+
+
+/**
+ * Handler to generate about scheme logo page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+static bool fetch_about_logo_handler(struct fetch_about_context *ctx)
+{
+ fetch_msg msg;
+
+ /* content is going to return redirect */
+ fetch_set_http_code(ctx->fetchh, 302);
+
+ msg.type = FETCH_REDIRECT;
+ msg.data.redirect = "resource:netsurf.png";
+
+ fetch_about_send_callback(&msg, ctx);
+
+ return true;
+}
+
+
+/**
+ * Handler to generate about scheme welcome page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+static bool fetch_about_welcome_handler(struct fetch_about_context *ctx)
+{
+ fetch_msg msg;
+
+ /* content is going to return redirect */
+ fetch_set_http_code(ctx->fetchh, 302);
+
+ msg.type = FETCH_REDIRECT;
+ msg.data.redirect = "resource:welcome.html";
+
+ fetch_about_send_callback(&msg, ctx);
+
+ return true;
+}
+
+
+/* Forward declaration because this handler requires the handler table. */
+static bool fetch_about_about_handler(struct fetch_about_context *ctx);
+
+/**
+ * List of about paths and their handlers
+ */
+struct about_handlers about_handler_list[] = {
+ {
+ "credits",
+ SLEN("credits"),
+ NULL,
+ fetch_about_credits_handler,
+ false
+ },
+ {
+ "licence",
+ SLEN("licence"),
+ NULL,
+ fetch_about_licence_handler,
+ false
+ },
+ {
+ "license",
+ SLEN("license"),
+ NULL,
+ fetch_about_licence_handler,
+ true
+ },
+ {
+ "welcome",
+ SLEN("welcome"),
+ NULL,
+ fetch_about_welcome_handler,
+ false
+ },
+ {
+ "config",
+ SLEN("config"),
+ NULL,
+ fetch_about_config_handler,
+ false
+ },
+ {
+ "Choices",
+ SLEN("Choices"),
+ NULL,
+ fetch_about_choices_handler,
+ false
+ },
+ {
+ "testament",
+ SLEN("testament"),
+ NULL,
+ fetch_about_testament_handler,
+ false
+ },
+ {
+ "about",
+ SLEN("about"),
+ NULL,
+ fetch_about_about_handler,
+ true
+ },
+ {
+ "nscolours.css",
+ SLEN("nscolours.css"),
+ NULL,
+ fetch_about_nscolours_handler,
+ true
+ },
+ {
+ "logo",
+ SLEN("logo"),
+ NULL,
+ fetch_about_logo_handler,
+ true
+ },
+ {
+ /* details about the image cache */
+ "imagecache",
+ SLEN("imagecache"),
+ NULL,
+ fetch_about_imagecache_handler,
+ true
+ },
+ {
+ /* The default blank page */
+ "blank",
+ SLEN("blank"),
+ NULL,
+ fetch_about_blank_handler,
+ true
+ },
+ {
+ /* details about a certificate */
+ "certificate",
+ SLEN("certificate"),
+ NULL,
+ fetch_about_certificate_handler,
+ true
+ },
+ {
+ /* chart generator */
+ "chart",
+ SLEN("chart"),
+ NULL,
+ fetch_about_chart_handler,
+ true
+ },
+ {
+ "query/auth",
+ SLEN("query/auth"),
+ NULL,
+ fetch_about_query_auth_handler,
+ true
+ },
+ {
+ "query/ssl",
+ SLEN("query/ssl"),
+ NULL,
+ fetch_about_query_privacy_handler,
+ true
+ },
+ {
+ "query/timeout",
+ SLEN("query/timeout"),
+ NULL,
+ fetch_about_query_timeout_handler,
+ true
+ },
+ {
+ "query/fetcherror",
+ SLEN("query/fetcherror"),
+ NULL,
+ fetch_about_query_fetcherror_handler,
+ true
+ }
+};
+
+#define about_handler_list_len \
+ (sizeof(about_handler_list) / sizeof(struct about_handlers))
+
+/**
+ * List all the valid about: paths available
+ *
+ * \param ctx The fetch context.
+ * \return true for sucess or false to generate an error.
+ */
+static bool fetch_about_about_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ unsigned int abt_loop = 0;
+
+ /* content is going to return ok */
+ fetch_set_http_code(ctx->fetchh, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_about_config_handler_aborted;
+
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>List of NetSurf pages</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body class=\"ns-even-bg ns-even-fg ns-border\">\n"
+ "<h1 class =\"ns-border\">List of NetSurf pages</h1>\n"
+ "<ul>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_config_handler_aborted;
+ }
+
+ for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
+
+ /* Skip over hidden entries */
+ if (about_handler_list[abt_loop].hidden)
+ continue;
+
+ res = fetch_about_ssenddataf(ctx,
+ "<li><a href=\"about:%s\">about:%s</a></li>\n",
+ about_handler_list[abt_loop].name,
+ about_handler_list[abt_loop].name);
+ if (res != NSERROR_OK) {
+ goto fetch_about_config_handler_aborted;
+ }
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</ul>\n</body>\n</html>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_config_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+fetch_about_config_handler_aborted:
+ return false;
+}
+
+static bool
+fetch_about_404_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+
+ /* content is going to return 404 */
+ fetch_set_http_code(ctx->fetchh, 404);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/plain; charset=utf-8")) {
+ return false;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "Unknown page: %s", nsurl_access(ctx->url));
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+}
+
+/**
+ * callback to initialise the about scheme fetcher.
+ */
+static bool fetch_about_initialise(lwc_string *scheme)
+{
+ unsigned int abt_loop = 0;
+ lwc_error error;
+
+ for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
+ error = lwc_intern_string(about_handler_list[abt_loop].name,
+ about_handler_list[abt_loop].name_len,
+ &about_handler_list[abt_loop].lname);
+ if (error != lwc_error_ok) {
+ while (abt_loop-- != 0) {
+ lwc_string_unref(about_handler_list[abt_loop].lname);
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * callback to finalise the about scheme fetcher.
+ */
+static void fetch_about_finalise(lwc_string *scheme)
+{
+ unsigned int abt_loop = 0;
+ for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
+ lwc_string_unref(about_handler_list[abt_loop].lname);
+ }
+}
+
+
+static bool fetch_about_can_fetch(const nsurl *url)
+{
+ return true;
+}
+
+
+/**
+ * callback to set up a about scheme fetch.
+ *
+ * \param post_urlenc post data in urlenc format, owned by the llcache object
+ * hence valid the entire lifetime of the fetch.
+ * \param post_multipart post data in multipart format, owned by the llcache
+ * object hence valid the entire lifetime of the fetch.
+ */
+static void *
+fetch_about_setup(struct fetch *fetchh,
+ nsurl *url,
+ bool only_2xx,
+ bool downgrade_tls,
+ const char *post_urlenc,
+ const struct fetch_multipart_data *post_multipart,
+ const char **headers)
+{
+ struct fetch_about_context *ctx;
+ unsigned int handler_loop;
+ lwc_string *path;
+ bool match;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ path = nsurl_get_component(url, NSURL_PATH);
+
+ for (handler_loop = 0;
+ handler_loop < about_handler_list_len;
+ handler_loop++) {
+ if (lwc_string_isequal(path,
+ about_handler_list[handler_loop].lname,
+ &match) == lwc_error_ok && match) {
+ ctx->handler = about_handler_list[handler_loop].handler;
+ break;
+ }
+ }
+
+ if (path != NULL)
+ lwc_string_unref(path);
+
+ ctx->fetchh = fetchh;
+ ctx->url = nsurl_ref(url);
+ ctx->multipart = post_multipart;
+
+ RING_INSERT(ring, ctx);
+
+ return ctx;
+}
+
+
+/**
+ * callback to free a about scheme fetch
+ */
+static void fetch_about_free(void *ctx)
+{
+ struct fetch_about_context *c = ctx;
+ nsurl_unref(c->url);
+ free(ctx);
+}
+
+
+/**
+ * callback to start an about scheme fetch
+ */
+static bool fetch_about_start(void *ctx)
+{
+ return true;
+}
+
+
+/**
+ * callback to abort a about fetch
+ */
+static void fetch_about_abort(void *ctx)
+{
+ struct fetch_about_context *c = ctx;
+
+ /* To avoid the poll loop having to deal with the fetch context
+ * disappearing from under it, we simply flag the abort here.
+ * The poll loop itself will perform the appropriate cleanup.
+ */
+ c->aborted = true;
+}
+
+
+/**
+ * callback to poll for additional about fetch contents
+ */
+static void fetch_about_poll(lwc_string *scheme)
+{
+ struct fetch_about_context *c, *save_ring = NULL;
+
+ /* Iterate over ring, processing each pending fetch */
+ while (ring != NULL) {
+ /* Take the first entry from the ring */
+ c = ring;
+ RING_REMOVE(ring, c);
+
+ /* Ignore fetches that have been flagged as locked.
+ * This allows safe re-entrant calls to this function.
+ * Re-entrancy can occur if, as a result of a callback,
+ * the interested party causes fetch_poll() to be called
+ * again.
+ */
+ if (c->locked == true) {
+ RING_INSERT(save_ring, c);
+ continue;
+ }
+
+ /* Only process non-aborted fetches */
+ if (c->aborted == false) {
+ /* about fetches can be processed in one go */
+ if (c->handler == NULL) {
+ fetch_about_404_handler(c);
+ } else {
+ c->handler(c);
+ }
+ }
+
+ /* And now finish */
+ fetch_remove_from_queues(c->fetchh);
+ fetch_free(c->fetchh);
+ }
+
+ /* Finally, if we saved any fetches which were locked, put them back
+ * into the ring for next time
+ */
+ ring = save_ring;
+}
+
+
+nserror fetch_about_register(void)
+{
+ lwc_string *scheme = lwc_string_ref(corestring_lwc_about);
+ const struct fetcher_operation_table fetcher_ops = {
+ .initialise = fetch_about_initialise,
+ .acceptable = fetch_about_can_fetch,
+ .setup = fetch_about_setup,
+ .start = fetch_about_start,
+ .abort = fetch_about_abort,
+ .free = fetch_about_free,
+ .poll = fetch_about_poll,
+ .finalise = fetch_about_finalise
+ };
+
+ return fetcher_add(scheme, &fetcher_ops);
+}
diff --git a/content/fetchers/about.h b/content/fetchers/about/about.h
index 944f84a59..bf6379709 100644
--- a/content/fetchers/about.h
+++ b/content/fetchers/about/about.h
@@ -16,12 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/** \file
- * about: URL method handler
+/**
+ * \file
+ * about scheme URL method handler
*/
-#ifndef NETSURF_CONTENT_FETCHERS_FETCH_ABOUT_H
-#define NETSURF_CONTENT_FETCHERS_FETCH_ABOUT_H
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_ABOUT_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_ABOUT_H
/**
* Register about scheme handler.
diff --git a/content/fetchers/about/atestament.h b/content/fetchers/about/atestament.h
new file mode 100644
index 000000000..1851e8f3c
--- /dev/null
+++ b/content/fetchers/about/atestament.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme testament handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_TESTAMENT_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_TESTAMENT_H
+
+/**
+ * Handler to generate about scheme testament page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_testament_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/blank.c b/content/fetchers/about/blank.c
new file mode 100644
index 000000000..8ad774512
--- /dev/null
+++ b/content/fetchers/about/blank.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme blank page
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "netsurf/types.h"
+#include "utils/errors.h"
+
+#include "private.h"
+#include "blank.h"
+
+/**
+ * Handler to generate about scheme cache page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_blank_handler(struct fetch_about_context *ctx)
+{
+ const char buffer[2] = { ' ', '\0' };
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_about_blank_handler_aborted;
+
+ if (fetch_about_senddata(ctx, (const uint8_t *) buffer, strlen(buffer)) != NSERROR_OK)
+ goto fetch_about_blank_handler_aborted;
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+fetch_about_blank_handler_aborted:
+ return false;
+}
diff --git a/content/fetchers/about/blank.h b/content/fetchers/about/blank.h
new file mode 100644
index 000000000..09dcc1f6a
--- /dev/null
+++ b/content/fetchers/about/blank.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme blank handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_BLANK_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_BLANK_H
+
+/**
+ * Handler to generate about scheme blank page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_blank_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/certificate.c b/content/fetchers/about/certificate.c
new file mode 100644
index 000000000..6f634d22a
--- /dev/null
+++ b/content/fetchers/about/certificate.c
@@ -0,0 +1,1291 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme certificate page
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "netsurf/inttypes.h"
+#include "netsurf/ssl_certs.h"
+
+#include "private.h"
+#include "certificate.h"
+
+/**
+ * certificate name parameters
+ */
+struct ns_cert_name {
+ char *common_name;
+ char *organisation;
+ char *organisation_unit;
+ char *locality;
+ char *province;
+ char *country;
+};
+
+/**
+ * Certificate public key parameters
+ */
+struct ns_cert_pkey {
+ char *algor;
+ int size;
+ char *modulus;
+ char *exponent;
+ char *curve;
+ char *public;
+};
+
+/**
+ * Certificate subject alternative name
+ */
+struct ns_cert_san {
+ struct ns_cert_san *next;
+ char *name;
+};
+
+/**
+ * certificate information for certificate chain
+ */
+struct ns_cert_info {
+ struct ns_cert_name subject_name; /**< Subject details */
+ struct ns_cert_name issuer_name; /**< Issuer details */
+ struct ns_cert_pkey public_key; /**< public key details */
+ long version; /**< Certificate version */
+ char *not_before; /**< Valid from date */
+ char *not_after; /**< Valid to date */
+ int sig_type; /**< Signature type */
+ char *sig_algor; /**< Signature Algorithm */
+ char *serialnum; /**< Serial number */
+ char *sha1fingerprint; /**< fingerprint shar1 encoded */
+ char *sha256fingerprint; /**< fingerprint shar256 encoded */
+ struct ns_cert_san *san; /**< subject alternative names */
+ ssl_cert_err err; /**< Whatever is wrong with this certificate */
+};
+
+/**
+ * free all resources associated with a certificate information structure
+ */
+static nserror free_ns_cert_info(struct ns_cert_info *cinfo)
+{
+ struct ns_cert_san *san;
+
+ free(cinfo->subject_name.common_name);
+ free(cinfo->subject_name.organisation);
+ free(cinfo->subject_name.organisation_unit);
+ free(cinfo->subject_name.locality);
+ free(cinfo->subject_name.province);
+ free(cinfo->subject_name.country);
+ free(cinfo->issuer_name.common_name);
+ free(cinfo->issuer_name.organisation);
+ free(cinfo->issuer_name.organisation_unit);
+ free(cinfo->issuer_name.locality);
+ free(cinfo->issuer_name.province);
+ free(cinfo->issuer_name.country);
+ free(cinfo->public_key.algor);
+ free(cinfo->public_key.modulus);
+ free(cinfo->public_key.exponent);
+ free(cinfo->public_key.curve);
+ free(cinfo->public_key.public);
+ free(cinfo->not_before);
+ free(cinfo->not_after);
+ free(cinfo->sig_algor);
+ free(cinfo->serialnum);
+
+ /* free san list avoiding use after free */
+ san = cinfo->san;
+ while (san != NULL) {
+ struct ns_cert_san *next;
+ next = san->next;
+ free(san);
+ san = next;
+ }
+
+ free(cinfo);
+
+ return NSERROR_OK;
+}
+
+#ifdef WITH_OPENSSL
+
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
+/* OpenSSL 1.1.1 or LibreSSL */
+
+# if defined(LIBRESSL_VERSION_NUMBER)
+ /* LibreSSL */
+# if (LIBRESSL_VERSION_NUMBER < 0x3050000fL)
+ /* LibreSSL <3.5.0 */
+
+# if (LIBRESSL_VERSION_NUMBER < 0x2070000fL)
+ /* LibreSSL <2.7.0 */
+static int ns_X509_get_signature_nid(X509 *cert)
+{
+ return OBJ_obj2nid(cert->cert_info->key->algor->algorithm);
+}
+
+static const unsigned char *ns_ASN1_STRING_get0_data(ASN1_STRING *asn1str)
+{
+ return (const unsigned char *)ASN1_STRING_data(asn1str);
+}
+# else
+# define ns_X509_get_signature_nid X509_get_signature_nid
+# define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data
+# endif
+
+static const BIGNUM *ns_RSA_get0_n(const RSA *d)
+{
+ return d->n;
+}
+
+static const BIGNUM *ns_RSA_get0_e(const RSA *d)
+{
+ return d->e;
+}
+# else
+ /* LibreSSL >= 3.5.0 */
+# define ns_X509_get_signature_nid X509_get_signature_nid
+# define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data
+# define ns_RSA_get0_n RSA_get0_n
+# define ns_RSA_get0_e RSA_get0_e
+# endif
+# else
+ /* OpenSSL 1.1.1 */
+# define ns_X509_get_signature_nid X509_get_signature_nid
+# define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data
+# define ns_RSA_get0_n RSA_get0_n
+# define ns_RSA_get0_e RSA_get0_e
+# endif
+
+static int ns_EVP_PKEY_get_bn_param(const EVP_PKEY *pkey,
+ const char *key_name, BIGNUM **bn) {
+ RSA *rsa;
+ BIGNUM *result = NULL;
+
+ /* Check parameters: only support allocation-form *bn */
+ if (pkey == NULL || key_name == NULL || bn == NULL || *bn != NULL)
+ return 0;
+
+ /* Only support RSA keys */
+ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA)
+ return 0;
+
+ rsa = EVP_PKEY_get1_RSA((EVP_PKEY *) pkey);
+ if (rsa == NULL)
+ return 0;
+
+ if (strcmp(key_name, "n") == 0) {
+ const BIGNUM *n = ns_RSA_get0_n(rsa);
+ if (n != NULL)
+ result = BN_dup(n);
+ } else if (strcmp(key_name, "e") == 0) {
+ const BIGNUM *e = ns_RSA_get0_e(rsa);
+ if (e != NULL)
+ result = BN_dup(e);
+ }
+
+ RSA_free(rsa);
+
+ *bn = result;
+
+ return (result != NULL) ? 1 : 0;
+}
+
+static int ns_EVP_PKEY_get_utf8_string_param(const EVP_PKEY *pkey,
+ const char *key_name, char *str, size_t max_len,
+ size_t *out_len)
+{
+ const EC_GROUP *ecgroup;
+ const char *group;
+ EC_KEY *ec;
+ int ret = 0;
+
+ if (pkey == NULL || key_name == NULL)
+ return 0;
+
+ /* Only support EC keys */
+ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC)
+ return 0;
+
+ /* Only support fetching the group */
+ if (strcmp(key_name, "group") != 0)
+ return 0;
+
+ ec = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) pkey);
+
+ ecgroup = EC_KEY_get0_group(ec);
+ if (ecgroup == NULL) {
+ group = "";
+ } else {
+ group = OBJ_nid2ln(EC_GROUP_get_curve_name(ecgroup));
+ }
+
+ if (str != NULL && max_len > strlen(group)) {
+ strcpy(str, group);
+ str[strlen(group)] = '\0';
+ ret = 1;
+ }
+ if (out_len != NULL)
+ *out_len = strlen(group);
+
+ EC_KEY_free(ec);
+
+ return ret;
+}
+
+static int ns_EVP_PKEY_get_octet_string_param(const EVP_PKEY *pkey,
+ const char *key_name, unsigned char *buf, size_t max_len,
+ size_t *out_len)
+{
+ const EC_GROUP *ecgroup;
+ const EC_POINT *ecpoint;
+ size_t len;
+ BN_CTX *bnctx;
+ EC_KEY *ec;
+ int ret = 0;
+
+ if (pkey == NULL || key_name == NULL)
+ return 0;
+
+ /* Only support EC keys */
+ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC)
+ return 0;
+
+ if (strcmp(key_name, "encoded-pub-key") != 0)
+ return 0;
+
+ ec = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) pkey);
+ if (ec == NULL)
+ return 0;
+
+ ecgroup = EC_KEY_get0_group(ec);
+ if (ecgroup != NULL) {
+ ecpoint = EC_KEY_get0_public_key(ec);
+ if (ecpoint != NULL) {
+ bnctx = BN_CTX_new();
+ len = EC_POINT_point2oct(ecgroup,
+ ecpoint,
+ POINT_CONVERSION_UNCOMPRESSED,
+ NULL,
+ 0,
+ bnctx);
+ if (len != 0 && len <= max_len) {
+ if (EC_POINT_point2oct(ecgroup,
+ ecpoint,
+ POINT_CONVERSION_UNCOMPRESSED,
+ buf,
+ len,
+ bnctx) == len)
+ ret = 1;
+ }
+ if (out_len != NULL)
+ *out_len = len;
+ BN_CTX_free(bnctx);
+ }
+ }
+
+ EC_KEY_free(ec);
+
+ return ret;
+}
+#else
+/* OpenSSL 3.x and later */
+#define ns_X509_get_signature_nid X509_get_signature_nid
+#define ns_ASN1_STRING_get0_data ASN1_STRING_get0_data
+#define ns_RSA_get0_n RSA_get0_n
+#define ns_RSA_get0_e RSA_get0_e
+#define ns_EVP_PKEY_get_bn_param EVP_PKEY_get_bn_param
+#define ns_EVP_PKEY_get_octet_string_param EVP_PKEY_get_octet_string_param
+#define ns_EVP_PKEY_get_utf8_string_param EVP_PKEY_get_utf8_string_param
+#endif
+
+/**
+ * extract certificate name information
+ *
+ * \param xname The X509 name to convert. The reference is borrowed so is not freeed
+ * \param iname The info structure to recive the extracted parameters.
+ * \return NSERROR_OK on success else error code
+ */
+static nserror
+xname_to_info(X509_NAME *xname, struct ns_cert_name *iname)
+{
+ int entryidx;
+ int entrycnt;
+ X509_NAME_ENTRY *entry; /* current name entry */
+ ASN1_STRING *value;
+ const unsigned char *value_str;
+ ASN1_OBJECT *name;
+ int name_nid;
+ char **field;
+
+ entrycnt = X509_NAME_entry_count(xname);
+
+ for (entryidx = 0; entryidx < entrycnt; entryidx++) {
+ entry = X509_NAME_get_entry(xname, entryidx);
+ name = X509_NAME_ENTRY_get_object(entry);
+ name_nid = OBJ_obj2nid(name);
+ value = X509_NAME_ENTRY_get_data(entry);
+ value_str = ns_ASN1_STRING_get0_data(value);
+ switch (name_nid) {
+ case NID_commonName:
+ field = &iname->common_name;
+ break;
+ case NID_countryName:
+ field = &iname->country;
+ break;
+ case NID_localityName:
+ field = &iname->locality;
+ break;
+ case NID_stateOrProvinceName:
+ field = &iname->province;
+ break;
+ case NID_organizationName:
+ field = &iname->organisation;
+ break;
+ case NID_organizationalUnitName:
+ field = &iname->organisation_unit;
+ break;
+ default :
+ field = NULL;
+ break;
+ }
+ if (field != NULL) {
+ *field = strdup((const char *)value_str);
+ NSLOG(netsurf, DEEPDEBUG,
+ "NID:%d value: %s", name_nid, *field);
+ } else {
+ NSLOG(netsurf, DEEPDEBUG, "NID:%d", name_nid);
+ }
+ }
+
+ /*
+ * ensure the common name is set to something, this being
+ * missing means the certificate is broken but this should be
+ * robust in the face of bad data
+ */
+ if (iname->common_name == NULL) {
+ iname->common_name = strdup("Unknown");
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * duplicate a hex formatted string inserting the colons
+ *
+ * \todo only uses html entity as separator because netsurfs line breaking
+ * fails otherwise.
+ */
+static char *hexdup(const char *hex)
+{
+ int hexlen;
+ char *dst;
+ char *out;
+ int cn = 0;
+
+ hexlen = strlen(hex);
+ /* allow space fox XXYY to XX&#58;YY&#58; */
+ dst = malloc(((hexlen * 7) + 6) / 2);
+
+ if (dst != NULL) {
+ for (out = dst; *hex != 0; hex++) {
+ if (cn == 2) {
+ cn = 0;
+ *out++ = '&';
+ *out++ = '#';
+ *out++ = '5';
+ *out++ = '8';
+ *out++ = ';';
+ }
+ *out++ = *hex;
+ cn++;
+ }
+ *out = 0;
+ }
+ return dst;
+}
+
+
+/**
+ * create a hex formatted string inserting the colons from binary data
+ *
+ * \todo only uses html entity as separator because netsurfs line breaking
+ * fails otherwise.
+ */
+static char *bindup(unsigned char *bin, unsigned int binlen)
+{
+ char *dst;
+ char *out;
+ unsigned int idx;
+ const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ /* allow space fox XY to expand to XX&#58;YY&#58; */
+ dst = malloc(binlen * 7);
+
+ if (dst != NULL) {
+ out = dst;
+ for (idx = 0; idx < binlen; idx++) {
+ *out++ = hex[(bin[idx] & 0xf0) >> 4];
+ *out++ = hex[bin[idx] & 0xf];
+
+ *out++ = '&';
+ *out++ = '#';
+ *out++ = '5';
+ *out++ = '8';
+ *out++ = ';';
+ }
+ out -= 5;
+ *out = 0;
+ }
+ return dst;
+}
+
+
+/**
+ * extract RSA key information to info structure
+ *
+ * \param pkey The RSA key to examine.
+ * \param ikey The public key info structure to fill
+ * \rerun NSERROR_OK on success else error code.
+ */
+static nserror
+rsa_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey)
+{
+ BIGNUM *n = NULL, *e = NULL;
+ char *tmp;
+
+ if (ns_EVP_PKEY_get_bn_param(pkey, "n", &n) != 1) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if (ns_EVP_PKEY_get_bn_param(pkey, "e", &e) != 1) {
+ BN_free(n);
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ ikey->algor = strdup("RSA");
+
+ ikey->size = EVP_PKEY_bits(pkey);
+
+ tmp = BN_bn2hex(n);
+ if (tmp != NULL) {
+ ikey->modulus = hexdup(tmp);
+ OPENSSL_free(tmp);
+ }
+
+ tmp = BN_bn2dec(e);
+ if (tmp != NULL) {
+ ikey->exponent = strdup(tmp);
+ OPENSSL_free(tmp);
+ }
+
+ BN_free(e);
+ BN_free(n);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * extract DSA key information to info structure
+ *
+ * \param pkey The DSA key to examine.
+ * \param ikey The public key info structure to fill
+ * \rerun NSERROR_OK on success else error code.
+ */
+static nserror
+dsa_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey)
+{
+ ikey->algor = strdup("DSA");
+
+ ikey->size = EVP_PKEY_bits(pkey);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * extract DH key information to info structure
+ *
+ * \param pkey The DH key to examine.
+ * \param ikey The public key info structure to fill
+ * \rerun NSERROR_OK on success else error code.
+ */
+static nserror
+dh_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey)
+{
+ ikey->algor = strdup("Diffie Hellman");
+
+ ikey->size = EVP_PKEY_bits(pkey);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * extract EC key information to info structure
+ *
+ * \param pkey The EC key to examine.
+ * \param ikey The public key info structure to fill
+ * \rerun NSERROR_OK on success else error code.
+ */
+static nserror
+ec_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey)
+{
+ size_t len;
+
+ ikey->algor = strdup("Elliptic Curve");
+
+ ikey->size = EVP_PKEY_bits(pkey);
+
+ len = 0;
+ ns_EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &len);
+ if (len != 0) {
+ ikey->curve = malloc(len + 1);
+ if (ikey->curve != NULL) {
+ if (ns_EVP_PKEY_get_utf8_string_param(pkey, "group",
+ ikey->curve, len + 1, NULL) == 0) {
+ free(ikey->curve);
+ ikey->curve = NULL;
+ }
+ }
+ }
+
+ len = 0;
+ ns_EVP_PKEY_get_octet_string_param(pkey, "encoded-pub-key",
+ NULL, 0, &len);
+ if (len != 0) {
+ unsigned char *point = malloc(len);
+ if (point != NULL) {
+ if (ns_EVP_PKEY_get_octet_string_param(pkey,
+ "encoded-pub-key", point, len,
+ NULL) == 1) {
+ ikey->public = bindup(point, len);
+ }
+ free(point);
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * extract public key information to info structure
+ *
+ * \param pkey the public key to examine. The reference is dropped on return
+ * \param ikey The public key info structure to fill
+ * \rerun NSERROR_OK on success else error code.
+ */
+static nserror
+pkey_to_info(EVP_PKEY *pkey, struct ns_cert_pkey *ikey)
+{
+ nserror res;
+
+ if (pkey == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ switch (EVP_PKEY_base_id(pkey)) {
+ case EVP_PKEY_RSA:
+ res = rsa_to_info(pkey, ikey);
+ break;
+
+ case EVP_PKEY_DSA:
+ res = dsa_to_info(pkey, ikey);
+ break;
+
+ case EVP_PKEY_DH:
+ res = dh_to_info(pkey, ikey);
+ break;
+
+ case EVP_PKEY_EC:
+ res = ec_to_info(pkey, ikey);
+ break;
+
+ default:
+ res = NSERROR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ EVP_PKEY_free(pkey);
+
+ return res;
+}
+
+static nserror san_to_info(X509 *cert, struct ns_cert_san **prev_next)
+{
+ int idx;
+ int san_names_nb = -1;
+ const GENERAL_NAME *current_name;
+ const unsigned char *dns_name;
+ struct ns_cert_san *isan;
+
+ STACK_OF(GENERAL_NAME) *san_names = NULL;
+
+ san_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (san_names == NULL) {
+ return NSERROR_OK;
+ }
+
+ san_names_nb = sk_GENERAL_NAME_num(san_names);
+
+ /* Check each name within the extension */
+ for (idx = 0; idx < san_names_nb; idx++) {
+ current_name = sk_GENERAL_NAME_value(san_names, idx);
+
+ if (current_name->type == GEN_DNS) {
+ /* extract DNS name into info structure */
+ dns_name = ns_ASN1_STRING_get0_data(current_name->d.dNSName);
+
+ isan = malloc(sizeof(struct ns_cert_san));
+ if (isan != NULL) {
+ isan->name = strdup((const char *)dns_name);
+ isan->next = NULL;
+ *prev_next = isan;
+ prev_next = &isan->next;
+ }
+ }
+ }
+
+ /* AmiSSL can't cope with the "correct" mechanism of freeing
+ * the GENERAL_NAME stack, which is:
+ * sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
+ * So instead we do this open-coded loop which does the same:
+ */
+ for (idx = 0; idx < san_names_nb; idx++) {
+ GENERAL_NAME *entry = sk_GENERAL_NAME_pop(san_names);
+ GENERAL_NAME_free(entry);
+ }
+ sk_GENERAL_NAME_free(san_names);
+
+ return NSERROR_OK;
+}
+
+static nserror
+der_to_certinfo(const uint8_t *der,
+ size_t der_length,
+ struct ns_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
+ *
+ * \note this is defined by standards (X.509 et al) to be one
+ * less than the certificate version.
+ */
+ info->version = X509_get_version(cert) + 1;
+
+ /* 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);
+ info->not_before = calloc(1, buf->length + 1);
+ if (info->not_before != NULL) {
+ memcpy(info->not_before, buf->data, (unsigned)buf->length);
+ }
+ 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);
+ info->not_after = calloc(1, buf->length + 1);
+ if (info->not_after != NULL) {
+ memcpy(info->not_after, buf->data, (unsigned)buf->length);
+ }
+ BUF_MEM_free(buf);
+
+ /* signature type */
+ info->sig_type = X509_get_signature_type(cert);
+
+ /* signature algorithm */
+ int pkey_nid = ns_X509_get_signature_nid(cert);
+ if (pkey_nid != NID_undef) {
+ const char* sslbuf = OBJ_nid2ln(pkey_nid);
+ if (sslbuf != NULL) {
+ info->sig_algor = strdup(sslbuf);
+ }
+ }
+
+ /* 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) {
+ info->serialnum = hexdup(tmp);
+ OPENSSL_free(tmp);
+ }
+ BN_free(bignum);
+ bignum = NULL;
+ }
+ }
+
+ /* fingerprints */
+ const EVP_MD *digest;
+ unsigned int dig_len;
+ unsigned char *buff;
+ int rc;
+
+ digest = EVP_sha1();
+ buff = malloc(EVP_MD_size(digest));
+ if (buff != NULL) {
+ rc = X509_digest(cert, digest, buff, &dig_len);
+ if ((rc == 1) && (dig_len == (unsigned int)EVP_MD_size(digest))) {
+ info->sha1fingerprint = bindup(buff, dig_len);
+ }
+ free(buff);
+ }
+
+ digest = EVP_sha256();
+ buff = malloc(EVP_MD_size(digest));
+ if (buff != NULL) {
+ rc = X509_digest(cert, digest, buff, &dig_len);
+ if ((rc == 1) && (dig_len == (unsigned int)EVP_MD_size(digest))) {
+ info->sha256fingerprint = bindup(buff, dig_len);
+ }
+ free(buff);
+ }
+
+ /* subject alternative names */
+ san_to_info(cert, &info->san);
+
+ /* issuer name */
+ xname_to_info(X509_get_issuer_name(cert), &info->issuer_name);
+
+ /* subject */
+ xname_to_info(X509_get_subject_name(cert), &info->subject_name);
+
+ /* public key */
+ pkey_to_info(X509_get_pubkey(cert), &info->public_key);
+
+ X509_free(cert);
+
+ return NSERROR_OK;
+}
+
+/* copy certificate data */
+static nserror
+convert_chain_to_cert_info(const struct cert_chain *chain,
+ struct ns_cert_info **cert_info_out)
+{
+ struct ns_cert_info *certs;
+ size_t depth;
+ nserror res;
+
+ certs = calloc(chain->depth, sizeof(struct ns_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;
+}
+
+#else
+static nserror
+convert_chain_to_cert_info(const struct cert_chain *chain,
+ struct ns_cert_info **cert_info_out)
+{
+ return NSERROR_NOT_IMPLEMENTED;
+}
+#endif
+
+
+static nserror
+format_certificate_name(struct fetch_about_context *ctx,
+ struct ns_cert_name *cert_name)
+{
+ nserror res;
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Common Name</th><td>%s</td></tr>\n",
+ cert_name->common_name);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ if (cert_name->organisation != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Organisation</th><td>%s</td></tr>\n",
+ cert_name->organisation);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (cert_name->organisation_unit != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Organisation Unit</th><td>%s</td></tr>\n",
+ cert_name->organisation_unit);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (cert_name->locality != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Locality</th><td>%s</td></tr>\n",
+ cert_name->locality);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (cert_name->province != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Privince</th><td>%s</td></tr>\n",
+ cert_name->province);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (cert_name->country != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Country</th><td>%s</td></tr>\n",
+ cert_name->country);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * output formatted certificate subject alternate names
+ */
+static nserror
+format_certificate_san(struct fetch_about_context *ctx,
+ struct ns_cert_san *san)
+{
+ nserror res;
+
+ if (san == NULL) {
+ return NSERROR_OK;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr><th>Alternative Names</th><td><hr></td></tr>\n");
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ while (san != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>DNS Name</th><td>%s</td></tr>\n",
+ san->name);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ san = san->next;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</table>\n");
+
+ return res;
+
+}
+
+
+static nserror
+format_certificate_public_key(struct fetch_about_context *ctx,
+ struct ns_cert_pkey *public_key)
+{
+ nserror res;
+
+ if (public_key->algor == NULL) {
+ /* skip the table if no algorithm name */
+ return NSERROR_OK;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr><th>Public Key</th><td><hr></td></tr>\n"
+ "<tr><th>Algorithm</th><td>%s</td></tr>\n"
+ "<tr><th>Key Size</th><td>%d</td></tr>\n",
+ public_key->algor,
+ public_key->size);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+
+ if (public_key->exponent != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Exponent</th><td>%s</td></tr>\n",
+ public_key->exponent);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (public_key->modulus != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Modulus</th><td class=\"data\">%s</td></tr>\n",
+ public_key->modulus);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (public_key->curve != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Curve</th><td>%s</td></tr>\n",
+ public_key->curve);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (public_key->public != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Public Value</th><td>%s</td></tr>\n",
+ public_key->public);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</table>\n");
+
+ return res;
+}
+
+static nserror
+format_certificate_fingerprint(struct fetch_about_context *ctx,
+ struct ns_cert_info *cert_info)
+{
+ nserror res;
+
+ if ((cert_info->sha1fingerprint == NULL) &&
+ (cert_info->sha256fingerprint == NULL)) {
+ /* skip the table if no fingerprints */
+ return NSERROR_OK;
+ }
+
+
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr><th>Fingerprints</th><td><hr></td></tr>\n");
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ if (cert_info->sha256fingerprint != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>SHA-256</th><td class=\"data\">%s</td></tr>\n",
+ cert_info->sha256fingerprint);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (cert_info->sha1fingerprint != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>SHA-1</th><td class=\"data\">%s</td></tr>\n",
+ cert_info->sha1fingerprint);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</table>\n");
+
+ return res;
+}
+
+static nserror
+format_certificate(struct fetch_about_context *ctx,
+ struct ns_cert_info *cert_info,
+ size_t depth)
+{
+ nserror res;
+
+ res = fetch_about_ssenddataf(ctx,
+ "<h2 id=\"%"PRIsizet"\" class=\"ns-border\">%s</h2>\n",
+ depth, cert_info->subject_name.common_name);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ if (cert_info->err != SSL_CERT_ERR_OK) {
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr class=\"ns-even-fg-bad\">"
+ "<th>Fault</th>"
+ "<td>%s</td>"
+ "</tr>"
+ "</table>\n",
+ messages_get_sslcode(cert_info->err));
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr><th>Issued To</th><td><hr></td></tr>\n");
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = format_certificate_name(ctx, &cert_info->subject_name);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "</table>\n");
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr><th>Issued By</th><td><hr></td></tr>\n");
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = format_certificate_name(ctx, &cert_info->issuer_name);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "</table>\n");
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr><th>Validity</th><td><hr></td></tr>\n"
+ "<tr><th>Valid From</th><td>%s</td></tr>\n"
+ "<tr><th>Valid Until</th><td>%s</td></tr>\n"
+ "</table>\n",
+ cert_info->not_before,
+ cert_info->not_after);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = format_certificate_san(ctx, cert_info->san);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = format_certificate_public_key(ctx, &cert_info->public_key);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<table class=\"info\">\n"
+ "<tr><th>Miscellaneous</th><td><hr></td></tr>\n");
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ if (cert_info->serialnum != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Serial Number</th><td>%s</td></tr>\n",
+ cert_info->serialnum);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ if (cert_info->sig_algor != NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Signature Algorithm</th>"
+ "<td>%s</td></tr>\n",
+ cert_info->sig_algor);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<tr><th>Version</th><td>%ld</td></tr>\n"
+ "</table>\n",
+ cert_info->version);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ res = format_certificate_fingerprint(ctx, cert_info);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ return res;
+}
+
+/**
+ * Handler to generate about:certificate page.
+ *
+ * Shows details of a certificate chain
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_certificate_handler(struct fetch_about_context *ctx)
+{
+ int code = 200;
+ nserror res;
+ struct cert_chain *chain = NULL;
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, code);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_about_certificate_handler_aborted;
+
+ /* page head */
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>NetSurf Browser Certificate Viewer</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body id=\"certificate\" class=\"ns-even-bg ns-even-fg ns-border\">\n"
+ "<h1 class=\"ns-border\">Certificate</h1>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_certificate_handler_aborted;
+ }
+
+ res = cert_chain_from_query(fetch_about_get_url(ctx), &chain);
+ if (res != NSERROR_OK) {
+ res = fetch_about_ssenddataf(ctx, "<p>Could not process that</p>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_certificate_handler_aborted;
+ }
+ } else {
+ struct ns_cert_info *cert_info;
+ res = convert_chain_to_cert_info(chain, &cert_info);
+ if (res == NSERROR_OK) {
+ size_t depth;
+ res = fetch_about_ssenddataf(ctx, "<ul>\n");
+ if (res != NSERROR_OK) {
+ free_ns_cert_info(cert_info);
+ goto fetch_about_certificate_handler_aborted;
+ }
+
+ for (depth = 0; depth < chain->depth; depth++) {
+ res = fetch_about_ssenddataf(ctx, "<li><a href=\"#%"PRIsizet"\">%s</a></li>\n",
+ depth, (cert_info + depth)
+ ->subject_name
+ .common_name);
+ if (res != NSERROR_OK) {
+ free_ns_cert_info(cert_info);
+ goto fetch_about_certificate_handler_aborted;
+ }
+
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</ul>\n");
+ if (res != NSERROR_OK) {
+ free_ns_cert_info(cert_info);
+ goto fetch_about_certificate_handler_aborted;
+ }
+
+ for (depth = 0; depth < chain->depth; depth++) {
+ res = format_certificate(ctx, cert_info + depth,
+ depth);
+ if (res != NSERROR_OK) {
+ free_ns_cert_info(cert_info);
+ goto fetch_about_certificate_handler_aborted;
+ }
+
+ }
+ free_ns_cert_info(cert_info);
+
+ } else {
+ res = fetch_about_ssenddataf(ctx,
+ "<p>Invalid certificate data</p>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_certificate_handler_aborted;
+ }
+ }
+ }
+
+
+ /* page footer */
+ res = fetch_about_ssenddataf(ctx, "</body>\n</html>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_certificate_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ cert_chain_free(chain);
+
+ return true;
+
+fetch_about_certificate_handler_aborted:
+ cert_chain_free(chain);
+ return false;
+}
diff --git a/content/fetchers/about/certificate.h b/content/fetchers/about/certificate.h
new file mode 100644
index 000000000..80ac06853
--- /dev/null
+++ b/content/fetchers/about/certificate.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme certificate handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_CERTIFICATE_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_CERTIFICATE_H
+
+/**
+ * Handler to generate about scheme certificate page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_certificate_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/chart.c b/content/fetchers/about/chart.c
new file mode 100644
index 000000000..c030c12b4
--- /dev/null
+++ b/content/fetchers/about/chart.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme chart page
+ *
+ * A chart consists of the figure area in which a chart a title and a
+ * key are placed.
+ *
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "utils/config.h"
+#include "netsurf/inttypes.h"
+#include "utils/config.h"
+#include "utils/utils.h"
+#include "utils/errors.h"
+#include "utils/nsurl.h"
+
+#include "private.h"
+#include "chart.h"
+
+/** minimum figure dimension */
+#define FIGURE_MIN_WIDTH 150
+#define FIGURE_MIN_HEIGHT 100
+
+enum chart_type {
+ CHART_TYPE_UNKNOWN,
+ CHART_TYPE_PIE,
+};
+
+/* type of chart key */
+enum key_type {
+ CHART_KEY_UNSET,
+ CHART_KEY_NONE,
+ CHART_KEY_LEFT,
+ CHART_KEY_RIGHT,
+ CHART_KEY_TOP,
+ CHART_KEY_BOT,
+ CHART_KEY_END
+};
+
+
+struct chart_label {
+ char *title; /* label title */
+ unsigned int colour; /* colour */
+};
+
+struct chart_series {
+ unsigned int len; /* number of values in the series */
+ float *value; /* array of values */
+};
+
+#define MAX_SERIES 4
+
+struct chart_data {
+ unsigned int series_len;
+ struct chart_series series[MAX_SERIES];
+
+ unsigned int label_len; /* number of labels */
+ struct chart_label *label;
+
+};
+
+/**
+ * parameters for a chart figure
+ */
+struct chart_param {
+ enum chart_type type;
+ enum key_type key; /* what type of key to use */
+ unsigned int width; /* width of figure */
+ unsigned int height; /* height of figure */
+ char *title; /* title */
+ struct {
+ unsigned int x;
+ unsigned int y;
+ unsigned int width;
+ unsigned int height;
+ } area; /* chart area within figure */
+ struct chart_data data;
+};
+
+#define DEF_COLOUR_NUM 8
+/** default colour series */
+static unsigned int colour_series[DEF_COLOUR_NUM] =
+ {
+ 0x00ff00, /* green */
+ 0x0000ff, /* blue */
+ 0xff0000, /* red */
+ 0xffff00, /* yellow */
+ 0x00ffff, /* cyan */
+ 0xff00ff, /* pink */
+ 0x777777, /* grey */
+ 0x000000, /* black */
+ };
+
+
+/* ensures there are labels present for every value */
+static nserror ensure_label_count(struct chart_param *chart, unsigned int count)
+{
+ unsigned int lidx;
+ int deltac;
+ struct chart_label *nlabels;
+
+ deltac = count - chart->data.label_len;
+ if (deltac <= 0) {
+ /* there are enough labels */
+ return NSERROR_OK;
+ }
+
+ nlabels = realloc(chart->data.label,
+ count * sizeof(struct chart_label));
+ if (nlabels == NULL) {
+ return NSERROR_NOMEM;
+ }
+ chart->data.label = nlabels;
+
+ for (lidx = chart->data.label_len; lidx < count; lidx++) {
+ chart->data.label[lidx].title = calloc(1, 20);
+ snprintf(chart->data.label[lidx].title, 19, "item %d", lidx + 1);
+ chart->data.label[lidx].colour = colour_series[lidx % DEF_COLOUR_NUM];
+ }
+
+ chart->data.label_len = count;
+
+ return NSERROR_OK;
+}
+
+/**
+ * extract values for a series
+ */
+static nserror
+extract_series_values(struct chart_param *chart,
+ unsigned int series_num,
+ const char *valstr,
+ size_t valstrlen)
+{
+ nserror res;
+ unsigned int valcur;
+ size_t valstart;/* value start in valstr */
+ size_t vallen; /* value end in valstr */
+ struct chart_series *series;
+
+ series = chart->data.series + series_num;
+
+ /* ensure we do not leak any data in this series */
+ if (series->value != NULL) {
+ free(series->value);
+ }
+
+ /* count how many values present */
+ for (series->len = 1, valstart=0; valstart < valstrlen; valstart++) {
+ if (valstr[valstart] == ',') {
+ series->len++;
+ }
+ }
+
+ /* allocate storage for values */
+ series->value = calloc(series->len, sizeof(float));
+ if (series->value == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ /* extract values from query string */
+ for (valcur = 0, vallen = 0, valstart = 0;
+ (valstart < valstrlen) && (valcur < series->len);
+ valstart += vallen, valcur++) {
+ /* get query section length */
+ vallen = 0;
+ while (((valstart + vallen) < valstrlen) &&
+ (valstr[valstart + vallen] != ',')) {
+ vallen++;
+ }
+
+ series->value[valcur] = strtof(valstr + valstart, NULL);
+ vallen++; /* account for , separator */
+ }
+
+ res = ensure_label_count(chart, series->len);
+
+ return res;
+}
+
+
+/**
+ * extract values for next series
+ */
+static nserror
+extract_next_series_values(struct chart_param *chart,
+ const char *valstr,
+ size_t valstrlen)
+{
+ nserror res;
+
+ if (chart->data.series_len >= MAX_SERIES) {
+ return NSERROR_NOSPACE;
+ }
+
+ res = extract_series_values(chart,
+ chart->data.series_len,
+ valstr,
+ valstrlen);
+ if (res == NSERROR_OK) {
+ chart->data.series_len++;
+ }
+
+ return res;
+}
+
+
+/**
+ * extract label title
+ */
+static nserror
+extract_series_labels(struct chart_param *chart,
+ const char *valstr,
+ size_t valstrlen)
+{
+ nserror res;
+ unsigned int valcount; /* count of values in valstr */
+ unsigned int valcur;
+ size_t valstart;/* value start in valstr */
+ size_t vallen; /* value end in valstr */
+
+ for (valcount = 1, valstart=0; valstart < valstrlen; valstart++) {
+ if (valstr[valstart] == ',') {
+ valcount++;
+ }
+ }
+
+ res = ensure_label_count(chart, valcount);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+
+ for (valcur = 0, vallen = 0, valstart = 0;
+ (valstart < valstrlen) && (valcur < chart->data.label_len);
+ valstart += vallen, valcur++) {
+ /* get query section length */
+ vallen = 0;
+ while (((valstart + vallen) < valstrlen) &&
+ (valstr[valstart + vallen] != ',')) {
+ vallen++;
+ }
+
+ chart->data.label[valcur].title = strndup(valstr + valstart, vallen);
+ vallen++; /* account for , separator */
+ }
+ return NSERROR_OK;
+}
+
+
+/**
+ * extract labels colour
+ */
+static nserror
+extract_series_colours(struct chart_param *chart,
+ const char *valstr,
+ size_t valstrlen)
+{
+ return NSERROR_OK;
+}
+
+/**
+ * process a part of a query
+ */
+static nserror
+process_query_section(const char *str, size_t len, struct chart_param *chart)
+{
+ nserror res = NSERROR_OK;
+
+ if ((len > 6) &&
+ (strncmp(str, "width=", 6) == 0)) {
+ /* figure width */
+ chart->width = strtoul(str + 6, NULL, 10);
+ } else if ((len > 7) &&
+ (strncmp(str, "height=", 7) == 0)) {
+ /* figure height */
+ chart->height = strtoul(str + 7, NULL, 10);
+ } else if ((len > 8) &&
+ (strncmp(str, "cawidth=", 8) == 0)) {
+ /* chart area width */
+ chart->area.width = strtoul(str + 8, NULL, 10);
+ } else if ((len > 9) &&
+ (strncmp(str, "caheight=", 9) == 0)) {
+ /* chart area height */
+ chart->area.height = strtoul(str + 9, NULL, 10);
+ } else if ((len > 4) &&
+ (strncmp(str, "key=", 4) == 0)) {
+ /* figure has key */
+ chart->key = strtoul(str + 4, NULL, 10);
+ } else if ((len > 6) &&
+ (strncmp(str, "title=", 6) == 0)) {
+ chart->title = strndup(str + 6, len - 6);
+ } else if ((len > 5) &&
+ (strncmp(str, "type=", 5) == 0)) {
+ if (strncmp(str + 5, "pie", len - 5) == 0) {
+ chart->type = CHART_TYPE_PIE;
+ } else {
+ chart->type = CHART_TYPE_UNKNOWN;
+ }
+ } else if ((len > 7) &&
+ (strncmp(str, "values=", 7) == 0)) {
+ res = extract_next_series_values(chart, str + 7, len - 7);
+ } else if ((len > 7) &&
+ (strncmp(str, "labels=", 7) == 0)) {
+ res = extract_series_labels(chart, str + 7, len - 7);
+ } else if ((len > 8) &&
+ (strncmp(str, "colours=", 8) == 0)) {
+ res = extract_series_colours(chart, str + 8, len - 8);
+ }
+
+ return res;
+}
+
+
+
+static nserror
+chart_from_query(struct nsurl *url, struct chart_param *chart)
+{
+ nserror res;
+ char *querystr;
+ size_t querylen;
+ size_t kvstart;/* key value start */
+ size_t kvlen; /* key value end */
+
+ res = nsurl_get(url, NSURL_QUERY, &querystr, &querylen);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ for (kvlen = 0, kvstart = 0; kvstart < querylen; kvstart += kvlen) {
+ /* get query section length */
+ kvlen = 0;
+ while (((kvstart + kvlen) < querylen) &&
+ (querystr[kvstart + kvlen] != '&')) {
+ kvlen++;
+ }
+
+ res = process_query_section(querystr + kvstart, kvlen, chart);
+ if (res != NSERROR_OK) {
+ break;
+ }
+ kvlen++; /* account for & separator */
+ }
+ free(querystr);
+
+ /* sanity check dimensions */
+ if (chart->width < FIGURE_MIN_WIDTH) {
+ /* bad width - check height */
+ if (chart->height < FIGURE_MIN_HEIGHT) {
+ /* both bad set to defaults */
+ chart->width = FIGURE_MIN_WIDTH;
+ chart->height = FIGURE_MIN_HEIGHT;
+ } else {
+ /* base width on valid height */
+ chart->width = (chart->height * 3) / 2;
+ }
+ } else {
+ /* good width check height */
+ if (chart->height < FIGURE_MIN_HEIGHT) {
+ /* base height on valid width */
+ chart->height = (chart->width * 2) / 3;
+ }
+ }
+
+ /* ensure legend type correct */
+ if ((chart->key == CHART_KEY_UNSET) ||
+ (chart->key >= CHART_KEY_END )) {
+ /* default to putting key on right */
+ chart->key = CHART_KEY_RIGHT;
+ }
+
+ return NSERROR_OK;
+}
+
+
+static nserror
+output_pie_legend(struct fetch_about_context *ctx, struct chart_param *chart)
+{
+ nserror res;
+ unsigned int lblidx;
+ unsigned int legend_width;
+ unsigned int legend_height;
+ unsigned int vertical_spacing;
+
+ switch (chart->key) {
+
+ case CHART_KEY_NONE:
+ break;
+ case CHART_KEY_RIGHT:
+ legend_width = chart->width - chart->area.width - chart->area.x;
+ legend_width -= 10; /* margin */
+ legend_height = chart->height;
+ vertical_spacing = legend_height / (chart->data.label_len + 1);
+
+ for(lblidx = 0; lblidx < chart->data.label_len ; lblidx++) {
+ res = fetch_about_ssenddataf(ctx,
+ "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"#%06x\" />",
+ chart->width - legend_width,
+ (vertical_spacing * lblidx) + (vertical_spacing/2),
+ vertical_spacing * 2 / 3,
+ vertical_spacing * 2 / 3,
+ chart->data.label[lblidx].colour);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ res = fetch_about_ssenddataf(ctx,
+ "<text x=\"%d\" y=\"%d\" fill=\"#%06x\" >%s</text>",
+ chart->width - legend_width + vertical_spacing,
+ vertical_spacing * (lblidx+1),
+ chart->data.label[lblidx].colour,
+ chart->data.label[lblidx].title);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NSERROR_OK;
+}
+
+static float
+compute_series_total(struct chart_param *chart, unsigned int series)
+{
+ float total;
+ unsigned int curdata;
+
+ for (total = 0, curdata = 0;
+ curdata < chart->data.series[series].len;
+ curdata++) {
+ total += chart->data.series[series].value[curdata];
+ }
+ return total;
+}
+
+/**
+ * render the data as a pie chart svg
+ */
+static bool
+pie_chart(struct fetch_about_context *ctx, struct chart_param *chart)
+{
+ nserror res;
+ float ra; /* pie a radius */
+ float rb; /* pie b radius */
+ float series_total;
+ unsigned int curdata; /* current data point index */
+ float last_x, last_y;
+ float end_x, end_y;
+ float start;
+ float extent;
+ bool large;
+ float circle_centre_x, circle_centre_y;
+
+ /* ensure there is data to render */
+ if ((chart->data.series_len < 1) || (chart->data.series[0].len < 2)) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* get the first series total value */
+ series_total = compute_series_total(chart, 0);
+ if (series_total == 0) {
+ /* dividing by zero is embarasing */
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /*
+ * need to ensure the chart area is setup correctly
+ *
+ * this is left to each chart type as different charts
+ * have differnt requirements
+ */
+ if ((chart->area.width == 0) || (chart->area.height == 0)) {
+ /*
+ * pie chart defaults to square of smaller of figure
+ * width and height
+ */
+ if (chart->width > chart->height) {
+ chart->area.width = chart->area.height = (chart->height - chart->area.x);
+ } else {
+ chart->area.width = chart->area.height = (chart->width - chart->area.y);
+ }
+ }
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx,
+ "Content-Type: image/svg; charset=utf-8")) {
+ goto aborted;
+ }
+
+ /* get the pie charts elipse radii */
+ ra = chart->area.width / 2;
+ rb = chart->area.height / 2;
+
+ /* get the offset to the circle centre */
+ circle_centre_x = chart->area.x + ra;
+ circle_centre_y = chart->area.y + rb;
+
+
+ /* svg header */
+ res = fetch_about_ssenddataf(ctx,
+ "<svg width=\"%u\" height=\"%u\" "
+ "xmlns=\"http://www.w3.org/2000/svg\">\n",
+ chart->width, chart->height);
+ if (res != NSERROR_OK) {
+ goto aborted;
+ }
+
+ /* generate the legend */
+ res = output_pie_legend(ctx, chart);
+ if (res != NSERROR_OK) {
+ goto aborted;
+ }
+
+ /* plot the arcs */
+ start = -M_PI_2;
+ last_x = (ra * cos(start));
+ last_y = (rb * sin(start));
+
+ /* iterate over each data point creating a slice o pie */
+ for (curdata=0; curdata < chart->data.series[0].len; curdata++) {
+ extent = ((chart->data.series[0].value[curdata] / series_total) * 2 * M_PI);
+ end_x = (ra * cos(start + extent));
+ end_y = (rb * sin(start + extent));
+
+ if (extent > M_PI) {
+ large = true;
+ } else {
+ large = false;
+ }
+
+ res = fetch_about_ssenddataf(
+ ctx,
+ "<path d=\"M %g %g\n"
+ "A %g %g 0 %d 1 %g %g\n"
+ "L %g %g Z\" fill=\"#%06x\" />\n",
+ circle_centre_x + last_x,
+ circle_centre_y + last_y,
+ ra, rb, large?1:0,
+ circle_centre_x + end_x,
+ circle_centre_y + end_y,
+ circle_centre_x,
+ circle_centre_y,
+ chart->data.label[curdata].colour);
+ if (res != NSERROR_OK) {
+ goto aborted;
+ }
+ last_x = end_x;
+ last_y = end_y;
+ start +=extent;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</svg>\n");
+ if (res != NSERROR_OK) {
+ goto aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+ aborted:
+
+ return false;
+
+}
+
+/**
+ * Handler to generate about scheme chart page.
+ *
+ * generates an svg chart
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_chart_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ struct chart_param chart;
+ memset(&chart, 0, sizeof(struct chart_param));
+
+ res = chart_from_query(fetch_about_get_url(ctx), &chart);
+ if (res != NSERROR_OK) {
+ goto aborted;
+ }
+
+ switch (chart.type) {
+ case CHART_TYPE_PIE:
+ return pie_chart(ctx, &chart);
+
+
+ default:
+ break;
+ }
+
+aborted:
+
+ return false;
+
+}
diff --git a/content/fetchers/about/chart.h b/content/fetchers/about/chart.h
new file mode 100644
index 000000000..4eb7e7898
--- /dev/null
+++ b/content/fetchers/about/chart.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme chart handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_CHART_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_CHART_H
+
+/**
+ * Handler to generate about scheme chart page.
+ *
+ * generates an svg chart
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_chart_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/choices.c b/content/fetchers/about/choices.c
new file mode 100644
index 000000000..a95502e6e
--- /dev/null
+++ b/content/fetchers/about/choices.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme blank page
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "netsurf/types.h"
+#include "utils/errors.h"
+#include "utils/nsoption.h"
+
+#include "private.h"
+#include "choices.h"
+
+/**
+ * Generate the text of a Choices file which represents the current
+ * in use options.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_choices_handler(struct fetch_about_context *ctx)
+{
+ char buffer[1024];
+ int code = 200;
+ int slen;
+ unsigned int opt_loop = 0;
+ int res = 0;
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, code);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
+ goto fetch_about_choices_handler_aborted;
+
+ slen = snprintf(buffer, sizeof buffer,
+ "# Automatically generated current NetSurf browser Choices\n");
+
+ do {
+ res = nsoption_snoptionf(buffer + slen,
+ sizeof buffer - slen,
+ opt_loop,
+ "%k:%v\n");
+ if (res <= 0)
+ break; /* last option */
+
+ if (res >= (int) (sizeof buffer - slen)) {
+ /* last entry would not fit in buffer, submit buffer */
+ res = fetch_about_senddata(ctx, (const uint8_t *)buffer, slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_choices_handler_aborted;
+ }
+ slen = 0;
+ } else {
+ /* normal addition */
+ slen += res;
+ opt_loop++;
+ }
+ } while (res > 0);
+
+ res = fetch_about_senddata(ctx, (const uint8_t *)buffer, slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_choices_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+fetch_about_choices_handler_aborted:
+ return false;
+}
diff --git a/content/fetchers/about/choices.h b/content/fetchers/about/choices.h
new file mode 100644
index 000000000..0548f5b9c
--- /dev/null
+++ b/content/fetchers/about/choices.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme choices handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_CHOICES_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_CHOICES_H
+
+/**
+ * Handler to generate about scheme choices page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_choices_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/config.c b/content/fetchers/about/config.c
new file mode 100644
index 000000000..a18af956f
--- /dev/null
+++ b/content/fetchers/about/config.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme blank page
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "netsurf/types.h"
+#include "utils/errors.h"
+#include "utils/nsoption.h"
+
+#include "private.h"
+#include "config.h"
+
+/**
+ * Handler to generate about scheme config page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_config_handler(struct fetch_about_context *ctx)
+{
+ char buffer[1024];
+ int slen = 0;
+ unsigned int opt_loop = 0;
+ int elen = 0; /* entry length */
+ nserror res;
+ bool even = false;
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html")) {
+ goto fetch_about_config_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>NetSurf Browser Config</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body "
+ "id =\"configlist\" "
+ "class=\"ns-even-bg ns-even-fg ns-border\" "
+ "style=\"overflow: hidden;\">\n"
+ "<h1 class=\"ns-border\">NetSurf Browser Config</h1>\n"
+ "<table class=\"config\">\n"
+ "<tr><th>Option</th>"
+ "<th>Type</th>"
+ "<th>Provenance</th>"
+ "<th>Setting</th></tr>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_config_handler_aborted;
+ }
+
+
+ do {
+ if (even) {
+ elen = nsoption_snoptionf(buffer + slen,
+ sizeof buffer - slen,
+ opt_loop,
+ "<tr class=\"ns-even-bg\">"
+ "<th class=\"ns-border\">%k</th>"
+ "<td class=\"ns-border\">%t</td>"
+ "<td class=\"ns-border\">%p</td>"
+ "<td class=\"ns-border\">%V</td>"
+ "</tr>\n");
+ } else {
+ elen = nsoption_snoptionf(buffer + slen,
+ sizeof buffer - slen,
+ opt_loop,
+ "<tr class=\"ns-odd-bg\">"
+ "<th class=\"ns-border\">%k</th>"
+ "<td class=\"ns-border\">%t</td>"
+ "<td class=\"ns-border\">%p</td>"
+ "<td class=\"ns-border\">%V</td>"
+ "</tr>\n");
+ }
+ if (elen <= 0)
+ break; /* last option */
+
+ if (elen >= (int) (sizeof buffer - slen)) {
+ /* last entry would not fit in buffer, submit buffer */
+ res = fetch_about_senddata(ctx, (const uint8_t *)buffer, slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_config_handler_aborted;
+ }
+ slen = 0;
+ } else {
+ /* normal addition */
+ slen += elen;
+ opt_loop++;
+ even = !even;
+ }
+ } while (elen > 0);
+
+ slen += snprintf(buffer + slen, sizeof buffer - slen,
+ "</table>\n</body>\n</html>\n");
+
+ res = fetch_about_senddata(ctx, (const uint8_t *)buffer, slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_config_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+fetch_about_config_handler_aborted:
+ return false;
+}
diff --git a/content/fetchers/about/config.h b/content/fetchers/about/config.h
new file mode 100644
index 000000000..efef23971
--- /dev/null
+++ b/content/fetchers/about/config.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme config handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_CONFIG_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_CONFIG_H
+
+/**
+ * Handler to generate about scheme config page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_config_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/imagecache.c b/content/fetchers/about/imagecache.c
new file mode 100644
index 000000000..23d3ef41c
--- /dev/null
+++ b/content/fetchers/about/imagecache.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme imagecache page
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "netsurf/types.h"
+
+#include "image/image_cache.h"
+
+#include "private.h"
+#include "imagecache.h"
+
+/* exported interface documented in about/imagecache.h */
+bool fetch_about_imagecache_handler(struct fetch_about_context *ctx)
+{
+ char buffer[2048]; /* output buffer */
+ int code = 200;
+ int slen;
+ unsigned int cent_loop = 0;
+ int elen = 0; /* entry length */
+ nserror res;
+ bool even = false;
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, code);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_about_imagecache_handler_aborted;
+
+ /* page head */
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>Image Cache Status</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body id =\"cachelist\" class=\"ns-even-bg ns-even-fg ns-border\">\n"
+ "<h1 class=\"ns-border\">Image Cache Status</h1>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_imagecache_handler_aborted;
+ }
+
+ /* image cache summary */
+ slen = image_cache_snsummaryf(buffer, sizeof(buffer),
+ "<p>Configured limit of %a hysteresis of %b</p>\n"
+ "<p>Total bitmap size in use %c (in %d)</p>\n"
+ "<p>Age %es</p>\n"
+ "<p>Peak size %f (in %g)</p>\n"
+ "<p>Peak image count %h (size %i)</p>\n"
+ "<p>Cache total/hit/miss/fail (counts) %j/%k/%l/%m "
+ "(%pj%%/%pk%%/%pl%%/%pm%%)"
+ "<img width=200 height=100 src=\"about:chart?type=pie&width=200&height=100&labels=hit,miss,fail&values=%k,%l,%m\" />"
+ "</p>\n");
+ if (slen >= (int) (sizeof(buffer))) {
+ goto fetch_about_imagecache_handler_aborted; /* overflow */
+ }
+
+ res = fetch_about_senddata(ctx, (const uint8_t *)buffer, slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_imagecache_handler_aborted;
+ }
+
+ /* image cache summary */
+ slen = image_cache_snsummaryf(buffer, sizeof(buffer),
+ "<p>Cache total/hit/miss/fail (size) %n/%o/%q/%r "
+ "(%pn%%/%po%%/%pq%%/%pr%%)"
+ "<img width=200 height=100 src=\"about:chart?type=pie&width=200&height=100&labels=hit,miss,fail&values=%o,%q,%r\" /></p>\n"
+ "<p>Total images never rendered: %s "
+ "(includes %t that were converted)</p>\n"
+ "<p>Total number of excessive conversions: %u "
+ "(from %v images converted more than once)"
+ "</p>\n"
+ "<p>Bitmap of size %w had most (%x) conversions</p>\n"
+ "<h2 class=\"ns-border\">Current contents</h2>\n");
+ if (slen >= (int) (sizeof(buffer))) {
+ goto fetch_about_imagecache_handler_aborted; /* overflow */
+ }
+
+ res = fetch_about_senddata(ctx, (const uint8_t *)buffer, slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_imagecache_handler_aborted;
+ }
+
+ /* image cache entry table */
+ res = fetch_about_ssenddataf(ctx, "<p class=\"imagecachelist\">\n"
+ "<strong>"
+ "<span>Entry</span>"
+ "<span>Content Key</span>"
+ "<span>Redraw Count</span>"
+ "<span>Conversion Count</span>"
+ "<span>Last Redraw</span>"
+ "<span>Bitmap Age</span>"
+ "<span>Bitmap Size</span>"
+ "<span>Source</span>"
+ "</strong>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_imagecache_handler_aborted;
+ }
+
+ slen = 0;
+ do {
+ if (even) {
+ elen = image_cache_snentryf(buffer + slen,
+ sizeof buffer - slen,
+ cent_loop,
+ "<a href=\"%U\">"
+ "<span class=\"ns-border\">%e</span>"
+ "<span class=\"ns-border\">%k</span>"
+ "<span class=\"ns-border\">%r</span>"
+ "<span class=\"ns-border\">%c</span>"
+ "<span class=\"ns-border\">%a</span>"
+ "<span class=\"ns-border\">%g</span>"
+ "<span class=\"ns-border\">%s</span>"
+ "<span class=\"ns-border\">%o</span>"
+ "</a>\n");
+ } else {
+ elen = image_cache_snentryf(buffer + slen,
+ sizeof buffer - slen,
+ cent_loop,
+ "<a class=\"ns-odd-bg\" href=\"%U\">"
+ "<span class=\"ns-border\">%e</span>"
+ "<span class=\"ns-border\">%k</span>"
+ "<span class=\"ns-border\">%r</span>"
+ "<span class=\"ns-border\">%c</span>"
+ "<span class=\"ns-border\">%a</span>"
+ "<span class=\"ns-border\">%g</span>"
+ "<span class=\"ns-border\">%s</span>"
+ "<span class=\"ns-border\">%o</span>"
+ "</a>\n");
+ }
+ if (elen <= 0)
+ break; /* last option */
+
+ if (elen >= (int) (sizeof buffer - slen)) {
+ /* last entry would not fit in buffer, submit buffer */
+ res = fetch_about_senddata(ctx,
+ (const uint8_t *)buffer,
+ slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_imagecache_handler_aborted;
+ }
+
+ slen = 0;
+ } else {
+ /* normal addition */
+ slen += elen;
+ cent_loop++;
+ even = !even;
+ }
+ } while (elen > 0);
+
+ slen += snprintf(buffer + slen, sizeof buffer - slen,
+ "</p>\n</body>\n</html>\n");
+
+ res = fetch_about_senddata(ctx, (const uint8_t *)buffer, slen);
+ if (res != NSERROR_OK) {
+ goto fetch_about_imagecache_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+fetch_about_imagecache_handler_aborted:
+ return false;
+}
diff --git a/content/fetchers/about/imagecache.h b/content/fetchers/about/imagecache.h
new file mode 100644
index 000000000..d1419cea7
--- /dev/null
+++ b/content/fetchers/about/imagecache.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme imagecache handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_IMAGECACHE_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_IMAGECACHE_H
+
+/**
+ * Handler to generate about scheme imagecache page.
+ *
+ * Shows details of current image cache.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_imagecache_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/nscolours.c b/content/fetchers/about/nscolours.c
new file mode 100644
index 000000000..bd7a5ed7a
--- /dev/null
+++ b/content/fetchers/about/nscolours.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme query privacy page
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "netsurf/plot_style.h"
+
+#include "utils/errors.h"
+#include "utils/nscolour.h"
+
+#include "private.h"
+#include "nscolours.h"
+
+/**
+ * Handler to generate the nscolours stylesheet
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_nscolours_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ const char *stylesheet;
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/css; charset=utf-8")) {
+ goto aborted;
+ }
+
+ res = nscolour_get_stylesheet(&stylesheet);
+ if (res != NSERROR_OK) {
+ goto aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "html {\n"
+ "\tbackground-color: #%06x;\n"
+ "}\n"
+ "%s",
+ colour_rb_swap(nscolours[NSCOLOUR_WIN_ODD_BG]),
+ stylesheet);
+ if (res != NSERROR_OK) {
+ goto aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+aborted:
+
+ return false;
+}
diff --git a/content/fetchers/about/nscolours.h b/content/fetchers/about/nscolours.h
new file mode 100644
index 000000000..a602e4d99
--- /dev/null
+++ b/content/fetchers/about/nscolours.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme nscolours handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_NSCOLOURS_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_NSCOLOURS_H
+
+/**
+ * Handler to generate the nscolours stylesheet
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_nscolours_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/private.h b/content/fetchers/about/private.h
new file mode 100644
index 000000000..fcf9b1987
--- /dev/null
+++ b/content/fetchers/about/private.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@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
+ * Private interfaces for the about scheme fetcher.
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_PRIVATE_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_PRIVATE_H
+
+struct fetch_about_context;
+struct fetch_multipart_data;
+
+/**
+ * set http response code on about response
+ */
+bool fetch_about_set_http_code(struct fetch_about_context *ctx, long code);
+
+/**
+ * Send a header on the about response
+ *
+ * \param ctx The about fetch context
+ * \param fmt The format specifier of the header
+ * \return true if the fetch has been aborted else false
+ */
+bool fetch_about_send_header(struct fetch_about_context *ctx, const char *fmt, ...);
+
+/**
+ * send data on the about response
+ */
+nserror fetch_about_senddata(struct fetch_about_context *ctx, const uint8_t *data, size_t data_len);
+
+/**
+ * send formatted data on the about response
+ */
+nserror fetch_about_ssenddataf(struct fetch_about_context *ctx, const char *fmt, ...);
+
+/**
+ * complete the about fetch response
+ */
+bool fetch_about_send_finished(struct fetch_about_context *ctx);
+
+/**
+ * Generate a 500 server error respnse
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_srverror(struct fetch_about_context *ctx);
+
+/**
+ * get the fetch url
+ */
+struct nsurl *fetch_about_get_url(struct fetch_about_context *ctx);
+
+/**
+ * get multipart fetch data
+ */
+const struct fetch_multipart_data *fetch_about_get_multipart(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/query.c b/content/fetchers/about/query.c
new file mode 100644
index 000000000..7c4e9f442
--- /dev/null
+++ b/content/fetchers/about/query.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme query privacy page
+ */
+
+#include <stdlib.h>
+
+#include "utils/errors.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+
+#include "query.h"
+
+/* exported interface documented in about/query.h */
+nserror
+get_query_description(struct nsurl *url,
+ const char *key,
+ char **out_str)
+{
+ nserror res;
+ char *url_s;
+ size_t url_l;
+ char *str = NULL;
+
+ /* get the host in question */
+ res = nsurl_get(url, NSURL_HOST, &url_s, &url_l);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ /* obtain the description with the url substituted */
+ str = messages_get_buff(key, url_s);
+ if (str == NULL) {
+ res = NSERROR_NOMEM;
+ } else {
+ *out_str = str;
+ }
+
+ free(url_s);
+
+ return res;
+}
diff --git a/content/fetchers/about/query.h b/content/fetchers/about/query.h
new file mode 100644
index 000000000..889a9ecdd
--- /dev/null
+++ b/content/fetchers/about/query.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme query handlers support interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_H
+
+/**
+ * generate a generic query description
+ *
+ * \param url The site url
+ * \param key message key
+ * \param out_str pointer to buffer with output
+ */
+nserror get_query_description(struct nsurl *url, const char *key, char **out_str);
+
+#endif
diff --git a/content/fetchers/about/query_auth.c b/content/fetchers/about/query_auth.c
new file mode 100644
index 000000000..1ed2e80ee
--- /dev/null
+++ b/content/fetchers/about/query_auth.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme query privacy page
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/fetch.h"
+
+#include "private.h"
+#include "query_auth.h"
+
+
+/**
+ * generate the description of the login query
+ */
+static nserror
+get_authentication_description(struct nsurl *url,
+ const char *realm,
+ const char *username,
+ const char *password,
+ char **out_str)
+{
+ nserror res;
+ char *url_s;
+ size_t url_l;
+ char *str = NULL;
+ const char *key;
+
+ res = nsurl_get(url, NSURL_HOST, &url_s, &url_l);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ if ((*username == 0) && (*password == 0)) {
+ key = "LoginDescription";
+ } else {
+ key = "LoginAgain";
+ }
+
+ str = messages_get_buff(key, url_s, realm);
+ if (str != NULL) {
+ NSLOG(netsurf, INFO,
+ "key:%s url:%s realm:%s str:%s",
+ key, url_s, realm, str);
+ *out_str = str;
+ } else {
+ res = NSERROR_NOMEM;
+ }
+
+ free(url_s);
+
+ return res;
+}
+
+
+/**
+ * Handler to generate about scheme authentication query page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_auth_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ char *url_s;
+ size_t url_l;
+ const char *realm = "";
+ const char *username = "";
+ const char *password = "";
+ const char *title;
+ char *description = NULL;
+ struct nsurl *siteurl = NULL;
+ const struct fetch_multipart_data *curmd; /* mutipart data iterator */
+
+ /* extract parameters from multipart post data */
+ curmd = fetch_about_get_multipart(ctx);
+ while (curmd != NULL) {
+ if (strcmp(curmd->name, "siteurl") == 0) {
+ res = nsurl_create(curmd->value, &siteurl);
+ if (res != NSERROR_OK) {
+ return fetch_about_srverror(ctx);
+ }
+ } else if (strcmp(curmd->name, "realm") == 0) {
+ realm = curmd->value;
+ } else if (strcmp(curmd->name, "username") == 0) {
+ username = curmd->value;
+ } else if (strcmp(curmd->name, "password") == 0) {
+ password = curmd->value;
+ }
+ curmd = curmd->next;
+ }
+
+ if (siteurl == NULL) {
+ return fetch_about_srverror(ctx);
+ }
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ title = messages_get("LoginTitle");
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>%s</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"authentication\">\n"
+ "<h1 class=\"ns-border\">%s</h1>\n",
+ title, title);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<form method=\"post\""
+ " enctype=\"multipart/form-data\">");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = get_authentication_description(siteurl,
+ realm,
+ username,
+ password,
+ &description);
+ if (res == NSERROR_OK) {
+ res = fetch_about_ssenddataf(ctx, "<p>%s</p>", description);
+ free(description);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+ }
+
+ res = fetch_about_ssenddataf(ctx, "<table>");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<tr>"
+ "<th><label for=\"name\">%s:</label></th>"
+ "<td><input type=\"text\" id=\"username\" "
+ "name=\"username\" value=\"%s\"></td>"
+ "</tr>",
+ messages_get("Username"), username);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<tr>"
+ "<th><label for=\"password\">%s:</label></th>"
+ "<td><input type=\"password\" id=\"password\" "
+ "name=\"password\" value=\"%s\"></td>"
+ "</tr>",
+ messages_get("Password"), password);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</table>");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<div id=\"buttons\">"
+ "<input type=\"submit\" id=\"login\" name=\"login\" "
+ "value=\"%s\" class=\"default-action\">"
+ "<input type=\"submit\" id=\"cancel\" name=\"cancel\" "
+ "value=\"%s\">"
+ "</div>",
+ messages_get("Login"),
+ messages_get("Cancel"));
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
+ if (res != NSERROR_OK) {
+ url_s = strdup("");
+ }
+ res = fetch_about_ssenddataf(ctx,
+ "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
+ url_s);
+ free(url_s);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<input type=\"hidden\" name=\"realm\" value=\"%s\">",
+ realm);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</form></body>\n</html>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_auth_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ nsurl_unref(siteurl);
+
+ return true;
+
+fetch_about_query_auth_handler_aborted:
+
+ nsurl_unref(siteurl);
+
+ return false;
+}
diff --git a/content/fetchers/about/query_auth.h b/content/fetchers/about/query_auth.h
new file mode 100644
index 000000000..1c7f658e1
--- /dev/null
+++ b/content/fetchers/about/query_auth.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme query auth handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_AUTH_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_AUTH_H
+
+/**
+ * Handler to generate about scheme query auth page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_auth_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/query_fetcherror.c b/content/fetchers/about/query_fetcherror.c
new file mode 100644
index 000000000..010a597e4
--- /dev/null
+++ b/content/fetchers/about/query_fetcherror.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme query privacy page
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "utils/errors.h"
+#include "utils/messages.h"
+#include "content/fetch.h"
+
+#include "private.h"
+#include "query.h"
+#include "query_fetcherror.h"
+
+/**
+ * Handler to generate about scheme fetch error query page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_fetcherror_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ char *url_s;
+ size_t url_l;
+ const char *reason = "";
+ const char *title;
+ struct nsurl *siteurl = NULL;
+ char *description = NULL;
+ const struct fetch_multipart_data *curmd; /* mutipart data iterator */
+
+ /* extract parameters from multipart post data */
+ curmd = fetch_about_get_multipart(ctx);
+ while (curmd != NULL) {
+ if (strcmp(curmd->name, "siteurl") == 0) {
+ res = nsurl_create(curmd->value, &siteurl);
+ if (res != NSERROR_OK) {
+ return fetch_about_srverror(ctx);
+ }
+ } else if (strcmp(curmd->name, "reason") == 0) {
+ reason = curmd->value;
+ }
+ curmd = curmd->next;
+ }
+
+ if (siteurl == NULL) {
+ return fetch_about_srverror(ctx);
+ }
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+
+ title = messages_get("FetchErrorTitle");
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>%s</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"fetcherror\">\n"
+ "<h1 class=\"ns-border ns-odd-fg-bad\">%s</h1>\n",
+ title, title);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<form method=\"post\""
+ " enctype=\"multipart/form-data\">");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+
+ res = get_query_description(siteurl,
+ "FetchErrorDescription",
+ &description);
+ if (res == NSERROR_OK) {
+ res = fetch_about_ssenddataf(ctx, "<div><p>%s</p></div>", description);
+ free(description);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+ }
+ res = fetch_about_ssenddataf(ctx, "<div><p>%s</p></div>", reason);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<div id=\"buttons\">"
+ "<input type=\"submit\" id=\"back\" name=\"back\" "
+ "value=\"%s\" class=\"default-action\">"
+ "<input type=\"submit\" id=\"retry\" name=\"retry\" "
+ "value=\"%s\">"
+ "</div>",
+ messages_get("Backtoprevious"),
+ messages_get("TryAgain"));
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+
+ res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
+ if (res != NSERROR_OK) {
+ url_s = strdup("");
+ }
+ res = fetch_about_ssenddataf(ctx,
+ "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
+ url_s);
+ free(url_s);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</form></body>\n</html>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_fetcherror_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ nsurl_unref(siteurl);
+
+ return true;
+
+fetch_about_query_fetcherror_handler_aborted:
+ nsurl_unref(siteurl);
+
+ return false;
+}
diff --git a/content/fetchers/about/query_fetcherror.h b/content/fetchers/about/query_fetcherror.h
new file mode 100644
index 000000000..85d972f8e
--- /dev/null
+++ b/content/fetchers/about/query_fetcherror.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme query fetch error handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_FETCHERROR_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_FETCHERROR_H
+
+/**
+ * Handler to generate about scheme fetch error query page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_fetcherror_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/query_privacy.c b/content/fetchers/about/query_privacy.c
new file mode 100644
index 000000000..030672bab
--- /dev/null
+++ b/content/fetchers/about/query_privacy.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme query privacy page
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "utils/errors.h"
+#include "utils/messages.h"
+#include "content/fetch.h"
+
+#include "private.h"
+#include "query.h"
+#include "query_privacy.h"
+
+/**
+ * Handler to generate about scheme privacy query page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_privacy_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ char *url_s;
+ size_t url_l;
+ const char *reason = "";
+ const char *title;
+ struct nsurl *siteurl = NULL;
+ char *description = NULL;
+ const char *chainurl = NULL;
+ const struct fetch_multipart_data *curmd; /* mutipart data iterator */
+
+ /* extract parameters from multipart post data */
+ curmd = fetch_about_get_multipart(ctx);
+ while (curmd != NULL) {
+ if (strcmp(curmd->name, "siteurl") == 0) {
+ res = nsurl_create(curmd->value, &siteurl);
+ if (res != NSERROR_OK) {
+ return fetch_about_srverror(ctx);
+ }
+ } else if (strcmp(curmd->name, "reason") == 0) {
+ reason = curmd->value;
+ } else if (strcmp(curmd->name, "chainurl") == 0) {
+ chainurl = curmd->value;
+ }
+ curmd = curmd->next;
+ }
+
+ if (siteurl == NULL) {
+ return fetch_about_srverror(ctx);
+ }
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+
+ title = messages_get("PrivacyTitle");
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>%s</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"privacy\">\n"
+ "<h1 class=\"ns-border ns-odd-fg-bad\">%s</h1>\n",
+ title, title);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<form method=\"post\""
+ " enctype=\"multipart/form-data\">");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+
+ res = get_query_description(siteurl,
+ "PrivacyDescription",
+ &description);
+ if (res == NSERROR_OK) {
+ res = fetch_about_ssenddataf(ctx, "<div><p>%s</p></div>", description);
+ free(description);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+ }
+
+ if (chainurl == NULL) {
+ res = fetch_about_ssenddataf(ctx,
+ "<div><p>%s</p></div>"
+ "<div><p>%s</p></div>",
+ reason,
+ messages_get("ViewCertificatesNotPossible"));
+ } else {
+ res = fetch_about_ssenddataf(ctx,
+ "<div><p>%s</p></div>"
+ "<div><p><a href=\"%s\" target=\"_blank\">%s</a></p></div>",
+ reason,
+ chainurl,
+ messages_get("ViewCertificates"));
+ }
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+ res = fetch_about_ssenddataf(ctx,
+ "<div id=\"buttons\">"
+ "<input type=\"submit\" id=\"back\" name=\"back\" "
+ "value=\"%s\" class=\"default-action\">"
+ "<input type=\"submit\" id=\"proceed\" name=\"proceed\" "
+ "value=\"%s\">"
+ "</div>",
+ messages_get("Backtosafety"),
+ messages_get("Proceed"));
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+
+ res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
+ if (res != NSERROR_OK) {
+ url_s = strdup("");
+ }
+ res = fetch_about_ssenddataf(ctx,
+ "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
+ url_s);
+ free(url_s);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</form></body>\n</html>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_ssl_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ nsurl_unref(siteurl);
+
+ return true;
+
+fetch_about_query_ssl_handler_aborted:
+ nsurl_unref(siteurl);
+
+ return false;
+}
diff --git a/content/fetchers/about/query_privacy.h b/content/fetchers/about/query_privacy.h
new file mode 100644
index 000000000..38ddbe889
--- /dev/null
+++ b/content/fetchers/about/query_privacy.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme query privacy handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_PRIVACY_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_PRIVACY_H
+
+/**
+ * Handler to generate about scheme query privacy page.
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_privacy_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/query_timeout.c b/content/fetchers/about/query_timeout.c
new file mode 100644
index 000000000..5c014bc9b
--- /dev/null
+++ b/content/fetchers/about/query_timeout.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme query timeout page
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "utils/errors.h"
+#include "utils/messages.h"
+#include "content/fetch.h"
+
+#include "private.h"
+#include "query.h"
+#include "query_timeout.h"
+
+/**
+ * Handler to generate about scheme timeout query page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_timeout_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ char *url_s;
+ size_t url_l;
+ const char *reason = "";
+ const char *title;
+ struct nsurl *siteurl = NULL;
+ char *description = NULL;
+ const struct fetch_multipart_data *curmd; /* mutipart data iterator */
+
+ /* extract parameters from multipart post data */
+ curmd = fetch_about_get_multipart(ctx);
+ while (curmd != NULL) {
+ if (strcmp(curmd->name, "siteurl") == 0) {
+ res = nsurl_create(curmd->value, &siteurl);
+ if (res != NSERROR_OK) {
+ return fetch_about_srverror(ctx);
+ }
+ } else if (strcmp(curmd->name, "reason") == 0) {
+ reason = curmd->value;
+ }
+ curmd = curmd->next;
+ }
+
+ if (siteurl == NULL) {
+ return fetch_about_srverror(ctx);
+ }
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+
+ title = messages_get("TimeoutTitle");
+ res = fetch_about_ssenddataf(ctx,
+ "<html>\n<head>\n"
+ "<title>%s</title>\n"
+ "<link rel=\"stylesheet\" type=\"text/css\" "
+ "href=\"resource:internal.css\">\n"
+ "</head>\n"
+ "<body class=\"ns-even-bg ns-even-fg ns-border\" id =\"timeout\">\n"
+ "<h1 class=\"ns-border ns-odd-fg-bad\">%s</h1>\n",
+ title, title);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<form method=\"post\""
+ " enctype=\"multipart/form-data\">");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+
+ res = get_query_description(siteurl,
+ "TimeoutDescription",
+ &description);
+ if (res == NSERROR_OK) {
+ res = fetch_about_ssenddataf(ctx, "<div><p>%s</p></div>", description);
+ free(description);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+ }
+ res = fetch_about_ssenddataf(ctx, "<div><p>%s</p></div>", reason);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "<div id=\"buttons\">"
+ "<input type=\"submit\" id=\"back\" name=\"back\" "
+ "value=\"%s\" class=\"default-action\">"
+ "<input type=\"submit\" id=\"retry\" name=\"retry\" "
+ "value=\"%s\">"
+ "</div>",
+ messages_get("Backtoprevious"),
+ messages_get("TryAgain"));
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+
+ res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
+ if (res != NSERROR_OK) {
+ url_s = strdup("");
+ }
+ res = fetch_about_ssenddataf(ctx,
+ "<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
+ url_s);
+ free(url_s);
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "</form></body>\n</html>\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_query_timeout_handler_aborted;
+ }
+
+ fetch_about_send_finished(ctx);
+
+ nsurl_unref(siteurl);
+
+ return true;
+
+fetch_about_query_timeout_handler_aborted:
+ nsurl_unref(siteurl);
+
+ return false;
+}
diff --git a/content/fetchers/about/query_timeout.h b/content/fetchers/about/query_timeout.h
new file mode 100644
index 000000000..a64757f21
--- /dev/null
+++ b/content/fetchers/about/query_timeout.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * about scheme query timeout handler interface
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_TIMEOUT_H
+#define NETSURF_CONTENT_FETCHERS_ABOUT_QUERY_TIMEOUT_H
+
+/**
+ * Handler to generate about scheme timeout query page
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_query_timeout_handler(struct fetch_about_context *ctx);
+
+#endif
diff --git a/content/fetchers/about/testament.c b/content/fetchers/about/testament.c
new file mode 100644
index 000000000..ae992ec52
--- /dev/null
+++ b/content/fetchers/about/testament.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * content generator for the about scheme testament page
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "utils/errors.h"
+#include "netsurf/inttypes.h"
+#include "testament.h"
+
+#include "private.h"
+#include "atestament.h"
+
+typedef struct {
+ const char *leaf;
+ const char *modtype;
+} modification_t;
+
+/**
+ * Generate the text of an svn testament which represents the current
+ * build-tree status
+ *
+ * \param ctx The fetcher context.
+ * \return true if handled false if aborted.
+ */
+bool fetch_about_testament_handler(struct fetch_about_context *ctx)
+{
+ nserror res;
+ static modification_t modifications[] = WT_MODIFICATIONS;
+ int modidx; /* midification index */
+
+ /* content is going to return ok */
+ fetch_about_set_http_code(ctx, 200);
+
+ /* content type */
+ if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
+ goto fetch_about_testament_handler_aborted;
+
+ res = fetch_about_ssenddataf(ctx,
+ "# Automatically generated by NetSurf build system\n\n");
+ if (res != NSERROR_OK) {
+ goto fetch_about_testament_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+#if defined(WT_BRANCHISTRUNK) || defined(WT_BRANCHISMASTER)
+ "# This is a *DEVELOPMENT* build from the main line.\n\n"
+#elif defined(WT_BRANCHISTAG) && (WT_MODIFIED == 0)
+ "# This is a tagged build of NetSurf\n"
+#ifdef WT_TAGIS
+ "# The tag used was '" WT_TAGIS "'\n\n"
+#else
+ "\n"
+#endif
+#elif defined(WT_NO_SVN) || defined(WT_NO_GIT)
+ "# This NetSurf was built outside of our revision "
+ "control environment.\n"
+ "# This testament is therefore not very useful.\n\n"
+#else
+ "# This NetSurf was built from a branch (" WT_BRANCHPATH ").\n\n"
+#endif
+#if defined(CI_BUILD)
+ "# This build carries the CI build number '" CI_BUILD "'\n\n"
+#endif
+ );
+ if (res != NSERROR_OK) {
+ goto fetch_about_testament_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx,
+ "Built by %s (%s) from %s at revision %s on %s\n\n",
+ GECOS, USERNAME, WT_BRANCHPATH, WT_REVID, WT_COMPILEDATE);
+ if (res != NSERROR_OK) {
+ goto fetch_about_testament_handler_aborted;
+ }
+
+ res = fetch_about_ssenddataf(ctx, "Built on %s in %s\n\n", WT_HOSTNAME, WT_ROOT);
+ if (res != NSERROR_OK) {
+ goto fetch_about_testament_handler_aborted;
+ }
+
+ if (WT_MODIFIED > 0) {
+ res = fetch_about_ssenddataf(ctx,
+ "Working tree has %d modification%s\n\n",
+ WT_MODIFIED, WT_MODIFIED == 1 ? "" : "s");
+ } else {
+ res = fetch_about_ssenddataf(ctx, "Working tree is not modified.\n");
+ }
+ if (res != NSERROR_OK) {
+ goto fetch_about_testament_handler_aborted;
+ }
+
+ for (modidx = 0; modidx < WT_MODIFIED; ++modidx) {
+ res = fetch_about_ssenddataf(ctx,
+ " %s %s\n",
+ modifications[modidx].modtype,
+ modifications[modidx].leaf);
+ if (res != NSERROR_OK) {
+ goto fetch_about_testament_handler_aborted;
+ }
+ }
+
+ fetch_about_send_finished(ctx);
+
+ return true;
+
+fetch_about_testament_handler_aborted:
+ return false;
+}
diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c
index f9cafae69..680e60456 100644
--- a/content/fetchers/curl.c
+++ b/content/fetchers/curl.c
@@ -67,21 +67,38 @@
#define UPDATES_PER_SECOND 2
/**
- * The ciphersuites the browser is prepared to use
+ * The ciphersuites the browser is prepared to use for TLS1.3
+ */
+#define CIPHER_SUITES \
+ "TLS_AES_256_GCM_SHA384:" \
+ "TLS_CHACHA20_POLY1305_SHA256:" \
+ "TLS_AES_128_GCM_SHA256"
+
+/**
+ * The ciphersuites the browser is prepared to use for TLS<1.3
*/
#define CIPHER_LIST \
/* disable everything */ \
"-ALL:" \
- /* enable TLSv1.2 PFS suites */ \
- "EECDH+AES+TLSv1.2:EDH+AES+TLSv1.2:" \
- /* enable PFS AES GCM suites */ \
- "EECDH+AESGCM:EDH+AESGCM:" \
- /* Enable PFS AES CBC suites */ \
- "EECDH+AES:EDH+AES:" \
- /* Enable non-PFS fallback suite */ \
- "AES128-SHA:" \
- /* Remove any PFS suites using weak DSA key exchange */ \
- "-DSS"
+ /* enable TLSv1.2 ECDHE AES GCM suites */ \
+ "EECDH+AESGCM+TLSv1.2:" \
+ /* enable ECDHE CHACHA20/POLY1305 suites */ \
+ "EECDH+CHACHA20:" \
+ /* Sort above by strength */ \
+ "@STRENGTH:" \
+ /* enable ECDHE (auth=RSA, mac=SHA1) AES CBC suites */ \
+ "EECDH+aRSA+AES+SHA1"
+
+/**
+ * The legacy cipher suites the browser is prepared to use for TLS<1.3
+ */
+#define CIPHER_LIST_LEGACY \
+ /* as above */ \
+ CIPHER_LIST":" \
+ /* enable (non-PFS) RSA AES GCM suites */ \
+ "RSA+AESGCM:" \
+ /* enable (non-PFS) RSA (mac=SHA1) AES CBC suites */ \
+ "RSA+AES+SHA1"
/* Open SSL compatability for certificate handling */
#ifdef WITH_OPENSSL
@@ -89,33 +106,11 @@
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
-/* OpenSSL 1.0.x to 1.1.0 certificate reference counting changed
- * LibreSSL declares its OpenSSL version as 2.1 but only supports the old way
- */
-#if (defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x1010000fL))
-static int ns_X509_up_ref(X509 *cert)
-{
- cert->references++;
- return 1;
-}
-
-static void ns_X509_free(X509 *cert)
-{
- cert->references--;
- if (cert->references == 0) {
- X509_free(cert);
- }
-}
-#else
-#define ns_X509_up_ref X509_up_ref
-#define ns_X509_free X509_free
-#endif
-
#else /* WITH_OPENSSL */
typedef char X509;
-static void ns_X509_free(X509 *cert)
+static void X509_free(X509 *cert)
{
free(cert);
}
@@ -216,6 +211,26 @@ struct cert_info {
long err; /**< OpenSSL error code */
};
+#if LIBCURL_VERSION_NUM >= 0x072000 /* 7.32.0 depricated CURLOPT_PROGRESSFUNCTION*/
+#define NSCURLOPT_PROGRESS_FUNCTION CURLOPT_XFERINFOFUNCTION
+#define NSCURLOPT_PROGRESS_DATA CURLOPT_XFERINFODATA
+#define NSCURL_PROGRESS_T curl_off_t
+#else
+#define NSCURLOPT_PROGRESS_FUNCTION CURLOPT_PROGRESSFUNCTION
+#define NSCURLOPT_PROGRESS_DATA CURLOPT_PROGRESSDATA
+#define NSCURL_PROGRESS_T double
+#endif
+
+#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 depricated curl_formadd */
+#define NSCURL_POSTDATA_T curl_mime
+#define NSCURL_POSTDATA_CURLOPT CURLOPT_MIMEPOST
+#define NSCURL_POSTDATA_FREE(x) curl_mime_free(x)
+#else
+#define NSCURL_POSTDATA_T struct curl_httppost
+#define NSCURL_POSTDATA_CURLOPT CURLOPT_HTTPPOST
+#define NSCURL_POSTDATA_FREE(x) curl_formfree(x)
+#endif
+
/** Information for a single fetch. */
struct curl_fetch_info {
struct fetch *fetch_handle; /**< The fetch handle we're parented by. */
@@ -225,7 +240,7 @@ struct curl_fetch_info {
bool abort; /**< Abort requested. */
bool stopped; /**< Download stopped on purpose. */
bool only_2xx; /**< Only HTTP 2xx responses acceptable. */
- bool downgrade_tls; /**< Downgrade to TLS <= 1.0 */
+ bool downgrade_tls; /**< Downgrade to TLS 1.2 */
nsurl *url; /**< URL of this fetch. */
lwc_string *host; /**< The hostname of this fetch. */
struct curl_slist *headers; /**< List of request headers. */
@@ -233,9 +248,11 @@ struct curl_fetch_info {
unsigned long content_length; /**< Response Content-Length, or 0. */
char *cookie_string; /**< Cookie string for this fetch */
char *realm; /**< HTTP Auth Realm */
- char *post_urlenc; /**< Url encoded POST string, or 0. */
+ struct fetch_postdata *postdata; /**< POST data */
+ NSCURL_POSTDATA_T *curl_postdata; /**< POST data in curl representation */
+
long http_code; /**< HTTP result code from cURL. */
- 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_CERT_DEPTH]; /**< HTTPS certificate data */
@@ -342,85 +359,95 @@ static bool fetch_curl_can_fetch(const nsurl *url)
}
+
/**
- * Convert a list of struct ::fetch_multipart_data to a list of
- * struct curl_httppost for libcurl.
+ * allocate postdata
*/
-static struct curl_httppost *
-fetch_curl_post_convert(const struct fetch_multipart_data *control)
+static struct fetch_postdata *
+fetch_curl_alloc_postdata(const char *post_urlenc,
+ const struct fetch_multipart_data *post_multipart)
{
- struct curl_httppost *post = 0, *last = 0;
- CURLFORMcode code;
- nserror ret;
-
- for (; control; control = control->next) {
- if (control->file) {
- char *leafname = NULL;
- ret = guit->file->basename(control->value, &leafname, NULL);
- if (ret != NSERROR_OK) {
- continue;
+ struct fetch_postdata *postdata;
+ postdata = calloc(1, sizeof(struct fetch_postdata));
+ if (postdata != NULL) {
+
+ if (post_urlenc) {
+ postdata->type = FETCH_POSTDATA_URLENC;
+ postdata->data.urlenc = strdup(post_urlenc);
+ if (postdata->data.urlenc == NULL) {
+ free(postdata);
+ postdata = NULL;
}
-
- /* We have to special case filenames of "", so curl
- * a) actually attempts the fetch and
- * b) doesn't attempt to open the file ""
- */
- if (control->value[0] == '\0') {
- /* dummy buffer - needs to be static so
- * pointer's still valid when we go out
- * of scope (not that libcurl should be
- * attempting to access it, of course).
- */
- static char buf;
-
- code = curl_formadd(&post, &last,
- CURLFORM_COPYNAME, control->name,
- CURLFORM_BUFFER, control->value,
- /* needed, as basename("") == "." */
- CURLFORM_FILENAME, "",
- CURLFORM_BUFFERPTR, &buf,
- CURLFORM_BUFFERLENGTH, 0,
- CURLFORM_CONTENTTYPE,
- "application/octet-stream",
- CURLFORM_END);
- if (code != CURL_FORMADD_OK)
- NSLOG(netsurf, INFO,
- "curl_formadd: %d (%s)", code,
- control->name);
- } else {
- char *mimetype = guit->fetch->mimetype(control->value);
- code = curl_formadd(&post, &last,
- CURLFORM_COPYNAME, control->name,
- CURLFORM_FILE, control->rawfile,
- CURLFORM_FILENAME, leafname,
- CURLFORM_CONTENTTYPE,
- (mimetype != 0 ? mimetype : "text/plain"),
- CURLFORM_END);
- if (code != CURL_FORMADD_OK)
- NSLOG(netsurf, INFO,
- "curl_formadd: %d (%s=%s)",
- code,
- control->name,
- control->value);
- free(mimetype);
+ } else if (post_multipart) {
+ postdata->type = FETCH_POSTDATA_MULTIPART;
+ postdata->data.multipart = fetch_multipart_data_clone(post_multipart);
+ if (postdata->data.multipart == NULL) {
+ free(postdata);
+ postdata = NULL;
}
- free(leafname);
- }
- else {
- code = curl_formadd(&post, &last,
- CURLFORM_COPYNAME, control->name,
- CURLFORM_COPYCONTENTS, control->value,
- CURLFORM_END);
- if (code != CURL_FORMADD_OK)
- NSLOG(netsurf, INFO,
- "curl_formadd: %d (%s=%s)", code,
- control->name, control->value);
+ } else {
+ postdata->type = FETCH_POSTDATA_NONE;
}
}
+ return postdata;
+}
- return post;
+/**
+ * free postdata
+ */
+static void fetch_curl_free_postdata(struct fetch_postdata *postdata)
+{
+ if (postdata != NULL) {
+ switch (postdata->type) {
+ case FETCH_POSTDATA_NONE:
+ break;
+ case FETCH_POSTDATA_URLENC:
+ free(postdata->data.urlenc);
+ break;
+ case FETCH_POSTDATA_MULTIPART:
+ fetch_multipart_data_destroy(postdata->data.multipart);
+ break;
+ }
+
+ free(postdata);
+ }
}
+/**
+ *construct a new fetch structure
+ */
+static struct curl_fetch_info *fetch_alloc(void)
+{
+ struct curl_fetch_info *fetch;
+ fetch = malloc(sizeof (*fetch));
+ if (fetch == NULL)
+ return NULL;
+
+ fetch->curl_handle = NULL;
+ fetch->sent_ssl_chain = false;
+ fetch->had_headers = false;
+ fetch->abort = false;
+ fetch->stopped = false;
+ fetch->only_2xx = false;
+ fetch->downgrade_tls = false;
+ fetch->headers = NULL;
+ fetch->url = NULL;
+ fetch->host = NULL;
+ fetch->location = NULL;
+ fetch->content_length = 0;
+ fetch->http_code = 0;
+ fetch->cookie_string = NULL;
+ fetch->realm = NULL;
+ fetch->last_progress_update = 0;
+ fetch->postdata = NULL;
+ fetch->curl_postdata = NULL;
+
+ /* Clear certificate chain data */
+ memset(fetch->cert_data, 0, sizeof(fetch->cert_data));
+ fetch->cert_depth = -1;
+
+ return fetch;
+}
/**
* Start fetching data for the given URL.
@@ -456,46 +483,22 @@ fetch_curl_setup(struct fetch *parent_fetch,
struct curl_slist *slist;
int i;
- fetch = malloc(sizeof (*fetch));
+ fetch = fetch_alloc();
if (fetch == NULL)
- return 0;
-
- fetch->fetch_handle = parent_fetch;
+ return NULL;
NSLOG(netsurf, INFO, "fetch %p, url '%s'", fetch, nsurl_access(url));
- /* construct a new fetch structure */
- fetch->curl_handle = NULL;
- fetch->sent_ssl_chain = false;
- fetch->had_headers = false;
- fetch->abort = false;
- fetch->stopped = false;
fetch->only_2xx = only_2xx;
fetch->downgrade_tls = downgrade_tls;
- fetch->headers = NULL;
+ fetch->fetch_handle = parent_fetch;
fetch->url = nsurl_ref(url);
fetch->host = nsurl_get_component(url, NSURL_HOST);
- fetch->location = NULL;
- fetch->content_length = 0;
- fetch->http_code = 0;
- fetch->cookie_string = NULL;
- fetch->realm = NULL;
- fetch->post_urlenc = NULL;
- fetch->post_multipart = NULL;
- if (post_urlenc) {
- fetch->post_urlenc = strdup(post_urlenc);
- } else if (post_multipart) {
- fetch->post_multipart = fetch_curl_post_convert(post_multipart);
+ if (fetch->host == NULL) {
+ goto failed;
}
- fetch->last_progress_update = 0;
-
- /* Clear certificate chain data */
- memset(fetch->cert_data, 0, sizeof(fetch->cert_data));
- fetch->cert_depth = -1;
-
- if ((fetch->host == NULL) ||
- (post_multipart != NULL && fetch->post_multipart == NULL) ||
- (post_urlenc != NULL && fetch->post_urlenc == NULL)) {
+ fetch->postdata = fetch_curl_alloc_postdata(post_urlenc, post_multipart);
+ if (fetch->postdata == NULL) {
goto failed;
}
@@ -548,9 +551,7 @@ failed:
lwc_string_unref(fetch->host);
nsurl_unref(fetch->url);
- free(fetch->post_urlenc);
- if (fetch->post_multipart)
- curl_formfree(fetch->post_multipart);
+ fetch_curl_free_postdata(fetch->postdata);
curl_slist_free_all(fetch->headers);
free(fetch);
return NULL;
@@ -730,7 +731,7 @@ fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx)
*/
if (!fetch->cert_data[depth].cert) {
fetch->cert_data[depth].cert = X509_STORE_CTX_get_current_cert(x509_ctx);
- ns_X509_up_ref(fetch->cert_data[depth].cert);
+ X509_up_ref(fetch->cert_data[depth].cert);
fetch->cert_data[depth].err = X509_STORE_CTX_get_error(x509_ctx);
}
@@ -805,7 +806,8 @@ fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm)
{
struct curl_fetch_info *f = (struct curl_fetch_info *) parm;
SSL_CTX *sslctx = _sslctx;
- long options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+ long options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
/* set verify callback for each certificate in chain */
SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback);
@@ -816,19 +818,14 @@ fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm)
parm);
if (f->downgrade_tls) {
- /* Disable TLS 1.1/1.2 if the server can't cope with them */
-#ifdef SSL_OP_NO_TLSv1_1
- options |= SSL_OP_NO_TLSv1_1;
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
- options |= SSL_OP_NO_TLSv1_2;
+ /* Disable TLS 1.3 if the server can't cope with it */
+#ifdef SSL_OP_NO_TLSv1_3
+ options |= SSL_OP_NO_TLSv1_3;
#endif
#ifdef SSL_MODE_SEND_FALLBACK_SCSV
/* Ensure server rejects the connection if downgraded too far */
SSL_CTX_set_mode(sslctx, SSL_MODE_SEND_FALLBACK_SCSV);
#endif
- /* Disable TLS1.2 ciphersuites */
- SSL_CTX_set_cipher_list(sslctx, CIPHER_LIST ":-TLSv1.2");
}
SSL_CTX_set_options(sslctx, options);
@@ -865,6 +862,301 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
f->sent_ssl_chain = true;
}
+#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 depricated curl_formadd */
+
+/**
+ * curl mime data context
+ */
+struct curl_mime_ctx {
+ char *buffer;
+ curl_off_t size;
+ curl_off_t position;
+};
+
+static size_t mime_data_read_callback(char *buffer, size_t size, size_t nitems, void *arg)
+{
+ struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg;
+ curl_off_t sz = mctx->size - mctx->position;
+
+ nitems *= size;
+ if(sz > (curl_off_t)nitems) {
+ sz = nitems;
+ }
+ if(sz) {
+ memcpy(buffer, mctx->buffer + mctx->position, sz);
+ }
+ mctx->position += sz;
+ return sz;
+}
+
+static int mime_data_seek_callback(void *arg, curl_off_t offset, int origin)
+{
+ struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg;
+
+ switch(origin) {
+ case SEEK_END:
+ offset += mctx->size;
+ break;
+ case SEEK_CUR:
+ offset += mctx->position;
+ break;
+ }
+
+ if(offset < 0) {
+ return CURL_SEEKFUNC_FAIL;
+ }
+ mctx->position = offset;
+ return CURL_SEEKFUNC_OK;
+}
+
+static void mime_data_free_callback(void *arg)
+{
+ struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg;
+ free(mctx);
+}
+
+/**
+ * Convert a POST data list to a libcurl curl_mime.
+ *
+ * \param chandle curl fetch handle.
+ * \param multipart limked list of struct ::fetch_multipart forming post data.
+ */
+static curl_mime *
+fetch_curl_postdata_convert(CURL *chandle,
+ const struct fetch_multipart_data *multipart)
+{
+ curl_mime *cmime;
+ curl_mimepart *part;
+ CURLcode code = CURLE_OK;
+ size_t value_len;
+
+ cmime = curl_mime_init(chandle);
+ if (cmime == NULL) {
+ NSLOG(netsurf, WARNING, "postdata conversion failed to curl mime context");
+ return NULL;
+ }
+
+ /* iterate post data */
+ for (; multipart != NULL; multipart = multipart->next) {
+ part = curl_mime_addpart(cmime);
+ if (part == NULL) {
+ goto convert_failed;
+ }
+
+ code = curl_mime_name(part, multipart->name);
+ if (code != CURLE_OK) {
+ goto convert_failed;
+ }
+
+ value_len = strlen(multipart->value);
+
+ if (multipart->file && value_len==0) {
+ /* file entries with no filename require special handling */
+ code=curl_mime_data(part, multipart->value, value_len);
+ if (code != CURLE_OK) {
+ goto convert_failed;
+ }
+
+ code = curl_mime_filename(part, "");
+ if (code != CURLE_OK) {
+ goto convert_failed;
+ }
+
+ code = curl_mime_type(part, "application/octet-stream");
+ if (code != CURLE_OK) {
+ goto convert_failed;
+ }
+
+ } else if(multipart->file) {
+ /* file entry */
+ nserror ret;
+ char *leafname = NULL;
+ char *mimetype = NULL;
+
+ code = curl_mime_filedata(part, multipart->rawfile);
+ if (code != CURLE_OK) {
+ goto convert_failed;
+ }
+
+ ret = guit->file->basename(multipart->value, &leafname, NULL);
+ if (ret != NSERROR_OK) {
+ goto convert_failed;
+ }
+ code = curl_mime_filename(part, leafname);
+ free(leafname);
+ if (code != CURLE_OK) {
+ goto convert_failed;
+ }
+
+ mimetype = guit->fetch->mimetype(multipart->value);
+ if (mimetype == NULL) {
+ mimetype=strdup("text/plain");
+ }
+ if (mimetype == NULL) {
+ goto convert_failed;
+ }
+ code = curl_mime_type(part, mimetype);
+ free(mimetype);
+ if (code != CURLE_OK) {
+ goto convert_failed;
+ }
+
+ } else {
+ /* make the curl mime reference the existing multipart
+ * data which requires use of a callback and context.
+ */
+ struct curl_mime_ctx *cb_ctx;
+ cb_ctx = malloc(sizeof(struct curl_mime_ctx));
+ if (cb_ctx == NULL) {
+ goto convert_failed;
+ }
+ cb_ctx->buffer = multipart->value;
+ cb_ctx->size = value_len;
+ cb_ctx->position = 0;
+ code = curl_mime_data_cb(part,
+ value_len,
+ mime_data_read_callback,
+ mime_data_seek_callback,
+ mime_data_free_callback,
+ cb_ctx);
+ if (code != CURLE_OK) {
+ free(cb_ctx);
+ goto convert_failed;
+ }
+ }
+ }
+
+ return cmime;
+
+convert_failed:
+ NSLOG(netsurf, WARNING, "postdata conversion failed with curl code: %d", code);
+ curl_mime_free(cmime);
+ return NULL;
+}
+
+#else /* LIBCURL_VERSION_NUM >= 0x073800 */
+
+/**
+ * Convert a list of struct ::fetch_multipart_data to a list of
+ * struct curl_httppost for libcurl.
+ */
+static struct curl_httppost *
+fetch_curl_postdata_convert(CURL *chandle,
+ const struct fetch_multipart_data *control)
+{
+ struct curl_httppost *post = NULL, *last = NULL;
+ CURLFORMcode code;
+ nserror ret;
+
+ for (; control; control = control->next) {
+ if (control->file) {
+ char *leafname = NULL;
+ ret = guit->file->basename(control->value, &leafname, NULL);
+ if (ret != NSERROR_OK) {
+ continue;
+ }
+
+ /* We have to special case filenames of "", so curl
+ * a) actually attempts the fetch and
+ * b) doesn't attempt to open the file ""
+ */
+ if (control->value[0] == '\0') {
+ /* dummy buffer - needs to be static so
+ * pointer's still valid when we go out
+ * of scope (not that libcurl should be
+ * attempting to access it, of course).
+ */
+ static char buf;
+
+ code = curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, control->name,
+ CURLFORM_BUFFER, control->value,
+ /* needed, as basename("") == "." */
+ CURLFORM_FILENAME, "",
+ CURLFORM_BUFFERPTR, &buf,
+ CURLFORM_BUFFERLENGTH, 0,
+ CURLFORM_CONTENTTYPE,
+ "application/octet-stream",
+ CURLFORM_END);
+ if (code != CURL_FORMADD_OK)
+ NSLOG(netsurf, INFO,
+ "curl_formadd: %d (%s)", code,
+ control->name);
+ } else {
+ char *mimetype = guit->fetch->mimetype(control->value);
+ code = curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, control->name,
+ CURLFORM_FILE, control->rawfile,
+ CURLFORM_FILENAME, leafname,
+ CURLFORM_CONTENTTYPE,
+ (mimetype != 0 ? mimetype : "text/plain"),
+ CURLFORM_END);
+ if (code != CURL_FORMADD_OK)
+ NSLOG(netsurf, INFO,
+ "curl_formadd: %d (%s=%s)",
+ code,
+ control->name,
+ control->value);
+ free(mimetype);
+ }
+ free(leafname);
+ } else {
+ code = curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, control->name,
+ CURLFORM_COPYCONTENTS, control->value,
+ CURLFORM_END);
+ if (code != CURL_FORMADD_OK)
+ NSLOG(netsurf, INFO,
+ "curl_formadd: %d (%s=%s)", code,
+ control->name, control->value);
+ }
+ }
+
+ return post;
+}
+
+#endif /* LIBCURL_VERSION_NUM >= 0x073800 */
+
+/**
+ * Setup multipart post data
+ */
+static CURLcode fetch_curl_set_postdata(struct curl_fetch_info *f)
+{
+ CURLcode code = CURLE_OK;
+
+#undef SETOPT
+#define SETOPT(option, value) { \
+ code = curl_easy_setopt(f->curl_handle, option, value); \
+ if (code != CURLE_OK) \
+ return code; \
+ }
+
+ switch (f->postdata->type) {
+ case FETCH_POSTDATA_NONE:
+ SETOPT(CURLOPT_POSTFIELDS, NULL);
+ SETOPT(NSCURL_POSTDATA_CURLOPT, NULL);
+ SETOPT(CURLOPT_HTTPGET, 1L);
+ break;
+
+ case FETCH_POSTDATA_URLENC:
+ SETOPT(NSCURL_POSTDATA_CURLOPT, NULL);
+ SETOPT(CURLOPT_HTTPGET, 0L);
+ SETOPT(CURLOPT_POSTFIELDS, f->postdata->data.urlenc);
+ break;
+
+ case FETCH_POSTDATA_MULTIPART:
+ SETOPT(CURLOPT_POSTFIELDS, NULL);
+ SETOPT(CURLOPT_HTTPGET, 0L);
+ if (f->curl_postdata == NULL) {
+ f->curl_postdata =
+ fetch_curl_postdata_convert(f->curl_handle,
+ f->postdata->data.multipart);
+ }
+ SETOPT(NSCURL_POSTDATA_CURLOPT, f->curl_postdata);
+ break;
+ }
+ return code;
+}
/**
* Set options specific for a fetch.
@@ -888,21 +1180,11 @@ static CURLcode fetch_curl_set_options(struct curl_fetch_info *f)
SETOPT(CURLOPT_PRIVATE, f);
SETOPT(CURLOPT_WRITEDATA, f);
SETOPT(CURLOPT_WRITEHEADER, f);
- SETOPT(CURLOPT_PROGRESSDATA, f);
- SETOPT(CURLOPT_REFERER, fetch_get_referer_to_send(f->fetch_handle));
+ SETOPT(NSCURLOPT_PROGRESS_DATA, f);
SETOPT(CURLOPT_HTTPHEADER, f->headers);
- if (f->post_urlenc) {
- SETOPT(CURLOPT_HTTPPOST, NULL);
- SETOPT(CURLOPT_HTTPGET, 0L);
- SETOPT(CURLOPT_POSTFIELDS, f->post_urlenc);
- } else if (f->post_multipart) {
- SETOPT(CURLOPT_POSTFIELDS, NULL);
- SETOPT(CURLOPT_HTTPGET, 0L);
- SETOPT(CURLOPT_HTTPPOST, f->post_multipart);
- } else {
- SETOPT(CURLOPT_POSTFIELDS, NULL);
- SETOPT(CURLOPT_HTTPPOST, NULL);
- SETOPT(CURLOPT_HTTPGET, 1L);
+ code = fetch_curl_set_postdata(f);
+ if (code != CURLE_OK) {
+ return code;
}
f->cookie_string = urldb_get_cookie(f->url, true);
@@ -949,6 +1231,12 @@ static CURLcode fetch_curl_set_options(struct curl_fetch_info *f)
SETOPT(CURLOPT_PROXY, NULL);
}
+
+ if (curl_with_openssl) {
+ SETOPT(CURLOPT_SSL_CIPHER_LIST,
+ f->downgrade_tls ? CIPHER_LIST_LEGACY : CIPHER_LIST);
+ }
+
/* Force-enable SSL session ID caching, as some distros are odd. */
SETOPT(CURLOPT_SSL_SESSIONID_CACHE, 1);
@@ -1162,15 +1450,13 @@ static void fetch_curl_free(void *vf)
if (f->headers) {
curl_slist_free_all(f->headers);
}
- free(f->post_urlenc);
- if (f->post_multipart) {
- curl_formfree(f->post_multipart);
- }
+ fetch_curl_free_postdata(f->postdata);
+ NSCURL_POSTDATA_FREE(f->curl_postdata);
/* 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);
+ X509_free(f->cert_data[i].cert);
}
}
@@ -1200,7 +1486,7 @@ static bool fetch_curl_process_headers(struct curl_fetch_info *f)
http_code = f->http_code;
NSLOG(netsurf, INFO, "HTTP status code %li", http_code);
- if (http_code == 304 && !f->post_urlenc && !f->post_multipart) {
+ if ((http_code == 304) && (f->postdata->type==FETCH_POSTDATA_NONE)) {
/* Not Modified && GET request */
msg.type = FETCH_NOTMODIFIED;
fetch_send_callback(&msg, f->fetch_handle);
@@ -1436,10 +1722,10 @@ static void fetch_curl_poll(lwc_string *scheme_ignored)
*/
static int
fetch_curl_progress(void *clientp,
- double dltotal,
- double dlnow,
- double ultotal,
- double ulnow)
+ NSCURL_PROGRESS_T dltotal,
+ NSCURL_PROGRESS_T dlnow,
+ NSCURL_PROGRESS_T ultotal,
+ NSCURL_PROGRESS_T ulnow)
{
static char fetch_progress_buffer[256]; /**< Progress buffer for cURL */
struct curl_fetch_info *f = (struct curl_fetch_info *) clientp;
@@ -1506,6 +1792,24 @@ fetch_curl_debug(CURL *handle,
}
+static curl_socket_t fetch_curl_socket_open(void *clientp,
+ curlsocktype purpose, struct curl_sockaddr *address)
+{
+ (void) clientp;
+ (void) purpose;
+
+ return (curl_socket_t) guit->fetch->socket_open(
+ address->family, address->socktype,
+ address->protocol);
+}
+
+static int fetch_curl_socket_close(void *clientp, curl_socket_t item)
+{
+ (void) clientp;
+
+ return guit->fetch->socket_close((int) item);
+}
+
/**
* Callback function for cURL.
*/
@@ -1712,8 +2016,10 @@ nserror fetch_curl_register(void)
#undef SETOPT
#define SETOPT(option, value) \
mcode = curl_multi_setopt(fetch_curl_multi, option, value); \
- if (mcode != CURLM_OK) \
- goto curl_multi_setopt_failed;
+ if (mcode != CURLM_OK) { \
+ NSLOG(netsurf, ERROR, "attempting curl_multi_setopt(%s, ...)", #option); \
+ goto curl_multi_setopt_failed; \
+ }
SETOPT(CURLMOPT_MAXCONNECTS, maxconnects);
SETOPT(CURLMOPT_MAX_TOTAL_CONNECTIONS, maxconnects);
@@ -1733,8 +2039,10 @@ nserror fetch_curl_register(void)
#undef SETOPT
#define SETOPT(option, value) \
code = curl_easy_setopt(fetch_blank_curl, option, value); \
- if (code != CURLE_OK) \
- goto curl_easy_setopt_failed;
+ if (code != CURLE_OK) { \
+ NSLOG(netsurf, ERROR, "attempting curl_easy_setopt(%s, ...)", #option); \
+ goto curl_easy_setopt_failed; \
+ }
SETOPT(CURLOPT_ERRORBUFFER, fetch_error_buffer);
SETOPT(CURLOPT_DEBUGFUNCTION, fetch_curl_debug);
@@ -1749,7 +2057,7 @@ nserror fetch_curl_register(void)
SETOPT(CURLOPT_WRITEFUNCTION, fetch_curl_data);
SETOPT(CURLOPT_HEADERFUNCTION, fetch_curl_header);
- SETOPT(CURLOPT_PROGRESSFUNCTION, fetch_curl_progress);
+ SETOPT(NSCURLOPT_PROGRESS_FUNCTION, fetch_curl_progress);
SETOPT(CURLOPT_NOPROGRESS, 0);
SETOPT(CURLOPT_USERAGENT, user_agent_string());
SETOPT(CURLOPT_ENCODING, "gzip");
@@ -1757,6 +2065,8 @@ nserror fetch_curl_register(void)
SETOPT(CURLOPT_LOW_SPEED_TIME, 180L);
SETOPT(CURLOPT_NOSIGNAL, 1L);
SETOPT(CURLOPT_CONNECTTIMEOUT, nsoption_uint(curl_fetch_timeout));
+ SETOPT(CURLOPT_OPENSOCKETFUNCTION, fetch_curl_socket_open);
+ SETOPT(CURLOPT_CLOSESOCKETFUNCTION, fetch_curl_socket_close);
if (nsoption_charp(ca_bundle) &&
strcmp(nsoption_charp(ca_bundle), "")) {
@@ -1786,6 +2096,14 @@ nserror fetch_curl_register(void)
/* only set the cipher list with openssl otherwise the
* fetch fails with "Unknown cipher in list"
*/
+#if LIBCURL_VERSION_NUM >= 0x073d00
+ /* Need libcurl 7.61.0 or later built against OpenSSL with
+ * TLS1.3 support */
+ code = curl_easy_setopt(fetch_blank_curl,
+ CURLOPT_TLS13_CIPHERS, CIPHER_SUITES);
+ if (code != CURLE_OK && code != CURLE_NOT_BUILT_IN)
+ goto curl_easy_setopt_failed;
+#endif
SETOPT(CURLOPT_SSL_CIPHER_LIST, CIPHER_LIST);
}
diff --git a/content/fetchers/file/dirlist.c b/content/fetchers/file/dirlist.c
index d49dc7fe7..345dbd8f3 100644
--- a/content/fetchers/file/dirlist.c
+++ b/content/fetchers/file/dirlist.c
@@ -29,6 +29,7 @@
#include "utils/messages.h"
#include "utils/nscolour.h"
+#include "netsurf/inttypes.h"
#include "netsurf/types.h"
#include "netsurf/plot_style.h"
@@ -158,7 +159,7 @@ bool dirlist_generate_title(const char *title, char *buffer, int buffer_length)
"<title>%s</title>\n"
"<style>\n"
"html {\n"
- "\tbackground-color: #%06x;\n"
+ "\tbackground-color: #%06"PRIx32";\n"
"}\n"
"%s"
"</style>\n"