#include <sys/file.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <glib.h>
#include <libgen.h>
#include <limits.h>
#include <pwd.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <gtk/gtkx.h>
+#include <gcr/gcr.h>
#include <JavaScriptCore/JavaScript.h>
#include <webkit2/webkit2.h>
#include <X11/X.h>
typedef enum {
AcceleratedCanvas,
CaretBrowsing,
+ Certificate,
CookiePolicies,
DiskCache,
DNSPrefetch,
WebKitWebInspector *inspector;
WebKitFindController *finder;
WebKitHitTestResult *mousepos;
+ GTlsCertificate *cert, *failedcert;
GTlsCertificateFlags tlserr;
Window xid;
- int progress, fullscreen, https, insecure;
+ int progress, fullscreen, https, insecure, errorpage;
const char *title, *overtitle, *targeturi;
const char *needle;
struct Client *next;
static char cookiepolicy_set(const WebKitCookieAcceptPolicy p);
static void seturiparameters(Client *c, const char *uri);
static void setparameter(Client *c, int refresh, ParamName p, const Arg *a);
+static const char *getcert(const char *uri);
+static void setcert(Client *c, const char *file);
static const char *getstyle(const char *uri);
static void setstyle(Client *c, const char *file);
static void runscript(Client *c);
static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
static void showview(WebKitWebView *v, Client *c);
static GtkWidget *createwindow(Client *c);
+static gboolean loadfailedtls(WebKitWebView *v, gchar *uri,
+ GTlsCertificate *cert,
+ GTlsCertificateFlags err, Client *c);
static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c);
static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c);
static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c);
static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
static void reload(Client *c, const Arg *a);
static void print(Client *c, const Arg *a);
+static void showcert(Client *c, const Arg *a);
static void clipboard(Client *c, const Arg *a);
static void zoom(Client *c, const Arg *a);
static void scroll(Client *c, const Arg *a);
cookiefile = buildfile(cookiefile);
scriptfile = buildfile(scriptfile);
cachedir = buildpath(cachedir);
+ certdir = buildpath(certdir);
gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy));
+ for (i = 0; i < LENGTH(certs); ++i) {
+ if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) {
+ certs[i].file = g_strconcat(certdir, "/", certs[i].file,
+ NULL);
+ } else {
+ fprintf(stderr, "Could not compile regex: %s\n",
+ certs[i].regex);
+ certs[i].regex = NULL;
+ }
+ }
+
if (!stylefile) {
styledir = buildpath(styledir);
for (i = 0; i < LENGTH(styles); ++i) {
- if (regcomp(&(styles[i].re), styles[i].regex,
+ if (!regcomp(&(styles[i].re), styles[i].regex,
REG_EXTENDED)) {
- fprintf(stderr,
- "Could not compile regex: %s\n",
+ styles[i].file = g_strconcat(styledir, "/",
+ styles[i].file, NULL);
+ } else {
+ fprintf(stderr, "Could not compile regex: %s\n",
styles[i].regex);
styles[i].regex = NULL;
}
- styles[i].file = g_strconcat(styledir, "/",
- styles[i].file, NULL);
}
g_free(styledir);
} else {
uriparams[i].config[j] = defconfig[j];
}
} else {
- fprintf(stderr,
- "Could not compile regex: %s\n",
+ fprintf(stderr, "Could not compile regex: %s\n",
uriparams[i].uri);
uriparams[i].uri = NULL;
}
webkit_settings_set_enable_caret_browsing(s, a->b);
refresh = 0;
break;
+ case Certificate:
+ if (a->b)
+ setcert(c, geturi(c));
+ return; /* do not update */
case CookiePolicies:
webkit_cookie_manager_set_accept_policy(
webkit_web_context_get_cookie_manager(
reload(c, a);
}
+const char *
+getcert(const char *uri)
+{
+ int i;
+
+ for (i = 0; i < LENGTH(certs); ++i) {
+ if (certs[i].regex &&
+ !regexec(&(certs[i].re), uri, 0, NULL, 0))
+ return certs[i].file;
+ }
+
+ return NULL;
+}
+
+void
+setcert(Client *c, const char *uri)
+{
+ const char *file = getcert(uri);
+ char *host;
+ GTlsCertificate *cert;
+
+ if (!file)
+ return;
+
+ if (!(cert = g_tls_certificate_new_from_file(file, NULL))) {
+ fprintf(stderr, "Could not read certificate file: %s\n", file);
+ return;
+ }
+
+ uri = strstr(uri, "://") + sizeof("://") - 1;
+ host = strndup(uri, strstr(uri, "/") - uri);
+
+ webkit_web_context_allow_tls_certificate_for_host(
+ webkit_web_view_get_context(c->view), cert, host);
+ g_object_unref(cert);
+
+ free(host);
+}
+
const char *
getstyle(const char *uri)
{
cmd[i++] = fulluseragent;
}
if (showxid)
- cmd[i++] = "-x";
+ cmd[i++] = "-w";
/* do not keep zoom level */
cmd[i++] = "--";
if ((uri = a->v))
G_CALLBACK(decidepolicy), c);
g_signal_connect(G_OBJECT(v), "insecure-content-detected",
G_CALLBACK(insecurecontent), c);
+ g_signal_connect(G_OBJECT(v), "load-failed-with-tls-errors",
+ G_CALLBACK(loadfailedtls), c);
g_signal_connect(G_OBJECT(v), "load-changed",
G_CALLBACK(loadchanged), c);
g_signal_connect(G_OBJECT(v), "mouse-target-changed",
return w;
}
+gboolean
+loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert,
+ GTlsCertificateFlags err, Client *c)
+{
+ GString *errmsg = g_string_new(NULL);
+ gchar *html, *pem;
+
+ c->failedcert = g_object_ref(cert);
+ c->tlserr = err;
+ c->errorpage = 1;
+
+ if (err & G_TLS_CERTIFICATE_UNKNOWN_CA)
+ g_string_append(errmsg,
+ "The signing certificate authority is not known.<br>");
+ if (err & G_TLS_CERTIFICATE_BAD_IDENTITY)
+ g_string_append(errmsg,
+ "The certificate does not match the expected identity "
+ "of the site that it was retrieved from.<br>");
+ if (err & G_TLS_CERTIFICATE_NOT_ACTIVATED)
+ g_string_append(errmsg,
+ "The certificate's activation time "
+ "is still in the future.<br>");
+ if (err & G_TLS_CERTIFICATE_EXPIRED)
+ g_string_append(errmsg, "The certificate has expired.<br>");
+ if (err & G_TLS_CERTIFICATE_REVOKED)
+ g_string_append(errmsg,
+ "The certificate has been revoked according to "
+ "the GTlsConnection's certificate revocation list.<br>");
+ if (err & G_TLS_CERTIFICATE_INSECURE)
+ g_string_append(errmsg,
+ "The certificate's algorithm is considered insecure.<br>");
+ if (err & G_TLS_CERTIFICATE_GENERIC_ERROR)
+ g_string_append(errmsg,
+ "Some error occurred validating the certificate.<br>");
+
+ g_object_get(cert, "certificate-pem", &pem, NULL);
+ html = g_strdup_printf("<p>Could not validate TLS for “%s”<br>%s</p>"
+ "<p>You can inspect the following certificate "
+ "with Ctrl+Shift+x (default keybinding).</p>"
+ "<p><pre>%s</pre></p>", uri, errmsg->str, pem);
+ g_free(pem);
+ g_string_free(errmsg, TRUE);
+
+ webkit_web_view_load_alternate_html(c->view, html, uri, NULL);
+ g_free(html);
+
+ return TRUE;
+}
+
void
loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
{
c->title = title;
c->https = c->insecure = 0;
seturiparameters(c, geturi(c));
+ if (c->errorpage)
+ c->errorpage = 0;
+ else
+ g_clear_object(&c->failedcert);
break;
case WEBKIT_LOAD_REDIRECTED:
setatom(c, AtomUri, title);
seturiparameters(c, geturi(c));
break;
case WEBKIT_LOAD_COMMITTED:
- c->https = webkit_web_view_get_tls_info(c->view, NULL,
+ c->https = webkit_web_view_get_tls_info(c->view, &c->cert,
&c->tlserr);
break;
case WEBKIT_LOAD_FINISHED:
GTK_WINDOW(c->win));
}
+void
+showcert(Client *c, const Arg *a)
+{
+ GTlsCertificate *cert = c->failedcert ? c->failedcert : c->cert;
+ GcrCertificate *gcrt;
+ GByteArray *crt;
+ GtkWidget *win;
+ GcrCertificateWidget *wcert;
+
+ if (!cert)
+ return;
+
+ g_object_get(cert, "certificate", &crt, NULL);
+ gcrt = gcr_simple_certificate_new(crt->data, crt->len);
+ g_byte_array_unref(crt);
+
+ win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ wcert = gcr_certificate_widget_new(gcrt);
+ g_object_unref(gcrt);
+
+ gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(wcert));
+ gtk_widget_show_all(win);
+}
+
void
clipboard(Client *c, const Arg *a)
{
case 'v':
die("surf-"VERSION", ©2009-2017 surf engineers, "
"see LICENSE for details\n");
- case 'x':
+ case 'w':
showxid = 1;
break;
case 'z':