Do not build SiteSpecific file when regex is invalid
[surf.git] / surf.c
diff --git a/surf.c b/surf.c
index fb3ef81..fcaa506 100644 (file)
--- a/surf.c
+++ b/surf.c
@@ -60,6 +60,7 @@ enum {
 typedef enum {
        AcceleratedCanvas,
        CaretBrowsing,
+       Certificate,
        CookiePolicies,
        DiskCache,
        DNSPrefetch,
@@ -80,7 +81,7 @@ typedef enum {
        SiteQuirks,
        SpellChecking,
        SpellLanguages,
-       StrictSSL,
+       StrictTLS,
        Style,
        ZoomLevel,
        ParameterLast,
@@ -104,9 +105,9 @@ typedef struct Client {
        WebKitWebInspector *inspector;
        WebKitFindController *finder;
        WebKitHitTestResult *mousepos;
-       GTlsCertificateFlags tlsflags;
+       GTlsCertificateFlags tlserr;
        Window xid;
-       int progress, fullscreen;
+       int progress, fullscreen, https, insecure;
        const char *title, *overtitle, *targeturi;
        const char *needle;
        struct Client *next;
@@ -136,9 +137,9 @@ typedef struct {
 
 typedef struct {
        char *regex;
-       char *style;
+       char *file;
        regex_t re;
-} SiteStyle;
+} SiteSpecific;
 
 /* Surf */
 static void usage(void);
@@ -162,8 +163,10 @@ static WebKitCookieAcceptPolicy cookiepolicy_get(void);
 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 *stylefile);
+static void setstyle(Client *c, const char *file);
 static void runscript(Client *c);
 static void evalscript(Client *c, const char *jsstr, ...);
 static void updatewinid(Client *c);
@@ -196,6 +199,8 @@ static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
 static void decidenavigation(WebKitPolicyDecision *d, Client *c);
 static void decidenewwindow(WebKitPolicyDecision *d, Client *c);
 static void decideresource(WebKitPolicyDecision *d, Client *c);
+static void insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e,
+                            Client *c);
 static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d,
                             Client *c);
 static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c);
@@ -224,7 +229,7 @@ static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h);
 static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h);
 
 static char winid[64];
-static char togglestats[10];
+static char togglestats[11];
 static char pagestats[2];
 static Atom atoms[AtomLast];
 static Window embed;
@@ -244,8 +249,8 @@ char *argv0;
 void
 usage(void)
 {
-       die("usage: %s [-bBdDfFgGiIkKmMnNpPsSvx] [-a cookiepolicies ] "
-           "[-c cookiefile] [-e xid] [-r scriptfile] [-t stylefile] "
+       die("usage: %s [-bBdDfFgGiIkKmMnNpPsStTvx] [-a cookiepolicies ] "
+           "[-c cookiefile] [-C stylefile] [-e xid] [-r scriptfile] "
            "[-u useragent] [-z zoomlevel] [uri]\n", basename(argv0));
 }
 
@@ -271,37 +276,51 @@ setup(void)
        if (signal(SIGHUP, sighup) == SIG_ERR)
                die("Can't install SIGHUP handler");
 
-       gtk_init(NULL, NULL);
-
-       gdpy = gdk_display_get_default();
-       dpy = GDK_DISPLAY_XDISPLAY(gdpy);
-
-       curconfig = defconfig;
+       if (!(dpy = XOpenDisplay(NULL)))
+               die("Can't open default display");
 
        /* atoms */
        atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False);
        atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
        atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
 
+       gtk_init(NULL, NULL);
+
+       gdpy = gdk_display_get_default();
+
+       curconfig = defconfig;
+
        /* dirs and files */
        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].style = g_strconcat(styledir, "/",
-                                                     styles[i].style, NULL);
                }
                g_free(styledir);
        } else {
@@ -319,8 +338,7 @@ setup(void)
                                        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;
                }
@@ -450,7 +468,6 @@ newclient(Client *rc)
        clients = c;
 
        c->progress = 100;
-       c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
        c->view = newview(c, rc ? rc->view : NULL);
 
        return c;
@@ -566,14 +583,17 @@ gettogglestats(Client *c)
        togglestats[6] = curconfig[Plugins].val.b ?         'V' : 'v';
        togglestats[7] = curconfig[Style].val.b ?           'M' : 'm';
        togglestats[8] = curconfig[FrameFlattening].val.b ? 'F' : 'f';
-       togglestats[9] = '\0';
+       togglestats[9] = curconfig[StrictTLS].val.b ?       'T' : 't';
+       togglestats[10] = '\0';
 }
 
 void
 getpagestats(Client *c)
 {
-       pagestats[0] = c->tlsflags > G_TLS_CERTIFICATE_VALIDATE_ALL ? '-' :
-                      c->tlsflags > 0 ? 'U' : 'T';
+       if (c->https)
+               pagestats[0] = (c->tlserr || c->insecure) ?  'U' : 'T';
+       else
+               pagestats[0] = '-';
        pagestats[1] = '\0';
 }
 
@@ -636,6 +656,10 @@ setparameter(Client *c, int refresh, ParamName p, const Arg *a)
                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(
@@ -705,12 +729,12 @@ setparameter(Client *c, int refresh, ParamName p, const Arg *a)
                return; /* do not update */
        case SpellLanguages:
                return; /* do nothing */
-       case StrictSSL:
+       case StrictTLS:
                webkit_web_context_set_tls_errors_policy(
                    webkit_web_view_get_context(c->view), a->b ?
                    WEBKIT_TLS_ERRORS_POLICY_FAIL :
                    WEBKIT_TLS_ERRORS_POLICY_IGNORE);
-               return; /* do not update */
+               break;
        case Style:
                if (a->b)
                        setstyle(c, getstyle(geturi(c)));
@@ -731,6 +755,44 @@ setparameter(Client *c, int refresh, ParamName p, const Arg *a)
                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);
+
+       free(host);
+}
+
 const char *
 getstyle(const char *uri)
 {
@@ -742,19 +804,19 @@ getstyle(const char *uri)
        for (i = 0; i < LENGTH(styles); ++i) {
                if (styles[i].regex &&
                    !regexec(&(styles[i].re), uri, 0, NULL, 0))
-                       return styles[i].style;
+                       return styles[i].file;
        }
 
        return "";
 }
 
 void
-setstyle(Client *c, const char *stylefile)
+setstyle(Client *c, const char *file)
 {
        gchar *style;
 
-       if (!g_file_get_contents(stylefile, &style, NULL, NULL)) {
-               fprintf(stderr, "Could not read style file: %s\n", stylefile);
+       if (!g_file_get_contents(file, &style, NULL, NULL)) {
+               fprintf(stderr, "Could not read style file: %s\n", file);
                return;
        }
 
@@ -811,7 +873,7 @@ newwindow(Client *c, const Arg *a, int noembed)
 {
        int i = 0;
        char tmp[64];
-       const char *cmd[26], *uri;
+       const char *cmd[28], *uri;
        const Arg arg = { .v = cmd };
 
        cmd[i++] = argv0;
@@ -822,6 +884,10 @@ newwindow(Client *c, const Arg *a, int noembed)
                cmd[i++] = "-c";
                cmd[i++] = cookiefile;
        }
+       if (stylefile && g_strcmp0(stylefile, "")) {
+               cmd[i++] = "-C";
+               cmd[i++] = stylefile;
+       }
        cmd[i++] = curconfig[DiskCache].val.b ? "-D" : "-d";
        if (embed && !noembed) {
                cmd[i++] = "-e";
@@ -840,10 +906,7 @@ newwindow(Client *c, const Arg *a, int noembed)
                cmd[i++] = scriptfile;
        }
        cmd[i++] = curconfig[JavaScript].val.b ? "-S" : "-s";
-       if (stylefile && g_strcmp0(stylefile, "")) {
-               cmd[i++] = "-t";
-               cmd[i++] = stylefile;
-       }
+       cmd[i++] = curconfig[StrictTLS].val.b ? "-T" : "-t";
        if (fulluseragent && g_strcmp0(fulluseragent, "")) {
                cmd[i++] = "-u";
                cmd[i++] = fulluseragent;
@@ -901,6 +964,7 @@ cleanup(void)
        g_free(scriptfile);
        g_free(stylefile);
        g_free(cachedir);
+       XCloseDisplay(dpy);
 }
 
 WebKitWebView *
@@ -918,7 +982,7 @@ newview(Client *c, WebKitWebView *rv)
        } else {
                settings = webkit_settings_new_with_settings(
                   "auto-load-images", curconfig[LoadImages].val.b,
-                  "default-font-size", curconfig[FontSize].val.f,
+                  "default-font-size", curconfig[FontSize].val.i,
                   "enable-caret-browsing", curconfig[CaretBrowsing].val.b,
                   "enable-developer-extras", curconfig[Inspector].val.b,
                   "enable-dns-prefetching", curconfig[DNSPrefetch].val.b,
@@ -954,9 +1018,9 @@ newview(Client *c, WebKitWebView *rv)
                 * or one for each view */
                webkit_web_context_set_process_model(context,
                    WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
-               /* ssl */
+               /* TLS */
                webkit_web_context_set_tls_errors_policy(context,
-                   curconfig[StrictSSL].val.b ? WEBKIT_TLS_ERRORS_POLICY_FAIL :
+                   curconfig[StrictTLS].val.b ? WEBKIT_TLS_ERRORS_POLICY_FAIL :
                    WEBKIT_TLS_ERRORS_POLICY_IGNORE);
                /* disk cache */
                webkit_web_context_set_cache_model(context,
@@ -1003,6 +1067,8 @@ newview(Client *c, WebKitWebView *rv)
                         G_CALLBACK(createview), c);
        g_signal_connect(G_OBJECT(v), "decide-policy",
                         G_CALLBACK(decidepolicy), c);
+       g_signal_connect(G_OBJECT(v), "insecure-content-detected",
+                        G_CALLBACK(insecurecontent), c);
        g_signal_connect(G_OBJECT(v), "load-changed",
                         G_CALLBACK(loadchanged), c);
        g_signal_connect(G_OBJECT(v), "mouse-target-changed",
@@ -1197,7 +1263,7 @@ createwindow(Client *c)
                gtk_window_set_role(GTK_WINDOW(w), wmstr);
                g_free(wmstr);
 
-               gtk_window_set_default_size(GTK_WINDOW(w), 800, 600);
+               gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]);
        }
 
        g_signal_connect(G_OBJECT(w), "destroy",
@@ -1224,7 +1290,7 @@ loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
                curconfig = defconfig;
                setatom(c, AtomUri, title);
                c->title = title;
-               c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
+               c->https = c->insecure = 0;
                seturiparameters(c, geturi(c));
                break;
        case WEBKIT_LOAD_REDIRECTED:
@@ -1233,10 +1299,8 @@ loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
                seturiparameters(c, geturi(c));
                break;
        case WEBKIT_LOAD_COMMITTED:
-               if (!webkit_web_view_get_tls_info(c->view, NULL,
-                   &(c->tlsflags)))
-                       c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
-
+               c->https = webkit_web_view_get_tls_info(c->view, NULL,
+                                                       &c->tlserr);
                break;
        case WEBKIT_LOAD_FINISHED:
                /* Disabled until we write some WebKitWebExtension for
@@ -1423,6 +1487,12 @@ decideresource(WebKitPolicyDecision *d, Client *c)
        }
 }
 
+void
+insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, Client *c)
+{
+       c->insecure = 1;
+}
+
 void
 downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c)
 {
@@ -1667,6 +1737,9 @@ main(int argc, char *argv[])
        case 'c':
                cookiefile = EARGF(usage());
                break;
+       case 'C':
+               stylefile = EARGF(usage());
+               break;
        case 'd':
                defconfig CSETB(DiskCache, 0);
                break;
@@ -1728,13 +1801,16 @@ main(int argc, char *argv[])
                defconfig CSETB(JavaScript, 1);
                break;
        case 't':
-               stylefile = EARGF(usage());
+               defconfig CSETB(StrictTLS, 0);
+               break;
+       case 'T':
+               defconfig CSETB(StrictTLS, 1);
                break;
        case 'u':
                fulluseragent = EARGF(usage());
                break;
        case 'v':
-               die("surf-"VERSION", ©2009-2015 surf engineers, "
+               die("surf-"VERSION", ©2009-2017 surf engineers, "
                    "see LICENSE for details\n");
        case 'x':
                showxid = 1;