diff --git a/config.def.h b/config.def.h
index e3b9881..15df614 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,10 +1,20 @@
/* modifier 0 means no modifier */
+/*
static char *useragent = "Mozilla/5.0 (X11; U; Unix; en-US) "
"AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 "
"Safari/537.15 Surf/"VERSION;
+*/
+static char *useragent = NULL;
+static char *downloadfolder = "~/Downloads/";
static char *scriptfile = "~/.surf/script.js";
static char *styledir = "~/.surf/styles/";
static char *cachefolder = "~/.surf/cache/";
+static char *dbfolder = "~/.surf/databases/";
+static char *originpromptfile = NULL;
+static char *originpromptdigestsalt = "Surf Origin Prompt Digest Salt 123456789"; /* change me; changing invalidates the origin trust state */
+static char *originprompthmackey = "Surf Origin Prompt HMAC Key 123456789"; /* " */
+static char *origincachefolder = "~/.surf/origins/%s/cache/";
+static char *origindbfolder = "~/.surf/origins/%s/databases/";
static Bool kioskmode = FALSE; /* Ignore shortcuts */
static Bool showindicators = TRUE; /* Show indicators in window title */
@@ -16,6 +26,7 @@ static gfloat zoomlevel = 1.0; /* Default zoom level */
/* Soup default features */
static char *cookiefile = "~/.surf/cookies.txt";
+static char *origincookiefile = "~/.surf/origins/%s/cookies.txt";
static char *cookiepolicies = "Aa@"; /* A: accept all; a: accept nothing,
@: accept no third party */
static char *cafile = "/etc/ssl/certs/ca-certificates.crt";
@@ -34,6 +45,7 @@ static Bool enablestyles = TRUE;
static Bool loadimages = TRUE;
static Bool hidebackground = FALSE;
static Bool allowgeolocation = TRUE;
+static Bool sameoriginpolicy = TRUE;
#define SETPROP(p, q) { \
.v = (char *[]){ "/bin/sh", "-c", \
@@ -46,10 +58,10 @@ static Bool allowgeolocation = TRUE;
/* DOWNLOAD(URI, referer) */
#define DOWNLOAD(d, r) { \
.v = (char *[]){ "/bin/sh", "-c", \
- "st -e /bin/sh -c \"curl -L -J -O --user-agent '$1'" \
+ "st -e /bin/sh -c \"mkdir -p $4 && cd $4 && curl -L -J -O --user-agent '$1'" \
" --referer '$2' -b $3 -c $3 '$0';" \
" sleep 5;\"", \
- d, useragent, r, cookiefile, NULL \
+ d, useragent, r, cookiefile, downloadfolder, NULL \
} \
}
diff --git a/originprompt-nojs.html b/originprompt-nojs.html
new file mode 100644
index 0000000..c697997
--- /dev/null
+++ b/originprompt-nojs.html
@@ -0,0 +1,73 @@
+
+
+
+
+Origin Crossing Gate
+
+
+
+
Origin Crossing Gate
+
+ Proceed with Caution
+
+ From Origin |
+ %s |
+
+
+ To Origin |
+ %s |
+
+
+ To URL |
+ %s |
+
+
+
+
+
+;
diff --git a/originprompt.html b/originprompt.html
new file mode 100644
index 0000000..629f577
--- /dev/null
+++ b/originprompt.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+Origin Crossing Gate
+
+
+
+
Origin Crossing Gate
+
+ Proceed with Caution
+
+ From Origin |
+ %s |
+
+
+ To Origin |
+ %s |
+
+
+ To URL |
+ %s |
+
+
+
+ Trust This Origin Crossing and Suppress This Prompt For
+
+ Duration |
+ 1 Day |
+ 1 Month |
+ 1 Year |
+
+
+
+
+
+;
diff --git a/surf.c b/surf.c
index 48f6b39..bb37ba2 100644
--- a/surf.c
+++ b/surf.c
@@ -35,6 +35,7 @@ char *argv0;
#define COOKIEJAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COOKIEJAR_TYPE, CookieJar))
enum { AtomFind, AtomGo, AtomUri, AtomLast };
+enum NavTransparency { NavExplicit, NavImplicit };
typedef union Arg Arg;
union Arg {
@@ -85,12 +86,15 @@ static GdkNativeWindow embed = 0;
static gboolean showxid = FALSE;
static char winid[64];
static gboolean usingproxy = 0;
-static char togglestat[9];
+static char togglestat[10];
static char pagestat[3];
static GTlsDatabase *tlsdb;
static int policysel = 0;
static char *stylefile = NULL;
+static char *origin_uri = NULL;
+static char *referring_origin = NULL;
static SoupCache *diskcache = NULL;
+static char *originprompt = NULL;
static void addaccelgroup(Client *c);
static void beforerequest(WebKitWebView *w, WebKitWebFrame *f,
@@ -119,6 +123,9 @@ static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f,
static gboolean decidedownload(WebKitWebView *v, WebKitWebFrame *f,
WebKitNetworkRequest *r, gchar *m, WebKitWebPolicyDecision *p,
Client *c);
+static gboolean decidenavigation(WebKitWebView *v, WebKitWebFrame *f,
+ WebKitNetworkRequest *r, WebKitWebNavigationAction *n,
+ WebKitWebPolicyDecision *p, Client *c);
static gboolean decidewindow(WebKitWebView *v, WebKitWebFrame *f,
WebKitNetworkRequest *r, WebKitWebNavigationAction *n,
WebKitWebPolicyDecision *p, Client *c);
@@ -156,10 +163,17 @@ static void linkhover(WebKitWebView *v, const char* t, const char* l,
Client *c);
static void loadstatuschange(WebKitWebView *view, GParamSpec *pspec,
Client *c);
-static void loaduri(Client *c, const Arg *arg);
+static void loadgate(Client *c, const char *cross_from_origin, const char *cross_to_uri);
+static void loaduri(Client *c, const Arg *arg, const enum NavTransparency navtrans);
static void navigate(Client *c, const Arg *arg);
static Client *newclient(void);
-static void newwindow(Client *c, const Arg *arg, gboolean noembed);
+static void newwindow(Client *c, const Arg *arg, gboolean noembed, const enum NavTransparency navtrans);
+static int origincmp(const char *uri1, const char *uri2);
+static int originhas(const char *uri);
+static const char *origingetproto(const char *uri);
+static char *origingethost(const char *uri);
+static char *origingeturi(const char *uri);
+static int originmatch(const char *uri1, const char *uri2);
static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d);
static gboolean contextmenu(WebKitWebView *view, GtkWidget *menu,
WebKitHitTestResult *target, gboolean keyboard, Client *c);
@@ -173,11 +187,16 @@ static void scroll_h(Client *c, const Arg *arg);
static void scroll_v(Client *c, const Arg *arg);
static void scroll(GtkAdjustment *a, const Arg *arg);
static void setatom(Client *c, int a, const char *v);
-static void setup(void);
+static void setup(const char *uri_arg);
static void sigchld(int unused);
static void source(Client *c, const Arg *arg);
static void spawn(Client *c, const Arg *arg);
+static int strrand(char *buf, int buflen);
+static gchar *strentropy();
+static gchar *strlangentropy();
static void stop(Client *c, const Arg *arg);
+static void acceptlanguagescramble();
+static void useragentscramble(WebKitWebView *view);
static void titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c);
static void toggle(Client *c, const Arg *arg);
static void togglecookiepolicy(Client *c, const Arg *arg);
@@ -187,6 +206,7 @@ static void togglestyle(Client *c, const Arg *arg);
static void updatetitle(Client *c);
static void updatewinid(Client *c);
static void usage(void);
+char *qualify_uri(const char *uri);
static void windowobjectcleared(GtkWidget *w, WebKitWebFrame *frame,
JSContextRef js, JSObjectRef win, Client *c);
static void zoom(Client *c, const Arg *arg);
@@ -283,7 +303,7 @@ buttonrelease(WebKitWebView *web, GdkEventButton *e, GList *gl) {
if(e->button == 2 ||
(e->button == 1 && CLEANMASK(e->state) == CLEANMASK(MODKEY))) {
g_object_get(result, "link-uri", &arg.v, NULL);
- newwindow(NULL, &arg, e->state & GDK_CONTROL_MASK);
+ newwindow(NULL, &arg, e->state & GDK_CONTROL_MASK, NavImplicit);
return true;
}
}
@@ -445,7 +465,7 @@ createwindow(WebKitWebView *v, WebKitWebFrame *f, Client *c) {
static gboolean
decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r,
gchar *m, WebKitWebPolicyDecision *p, Client *c) {
- if(!webkit_web_view_can_show_mime_type(v, m)) {
+ if(!webkit_web_view_can_show_mime_type(v, m) && !webkit_web_frame_get_parent(f)) {
webkit_web_policy_decision_download(p);
return TRUE;
}
@@ -453,6 +473,40 @@ decidedownload(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r,
}
static gboolean
+decidenavigation(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
+ WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p,
+ Client *c) {
+ const char *uri = webkit_network_request_get_uri(r);
+ Arg arg;
+
+ if (!useragent) {
+ useragentscramble(view);
+ }
+ acceptlanguagescramble();
+
+ if (!sameoriginpolicy) {
+ /* configured to not bother isolating origins */
+ return FALSE;
+ } else if (webkit_web_frame_get_parent(f)) {
+ /* has a parent, and therefore not an origin */
+ return FALSE;
+ /* branches below operate on the origin, top-most frame */
+ } else if (uri && (uri[0] == '\0' || strcmp(uri, "about:blank") == 0)) {
+ /* nothing is really going to load */
+ return FALSE;
+ } else if (origin_uri == NULL || originmatch(uri, origin_uri)) {
+ /* origin matches */
+ return FALSE;
+ } else {
+ /* top-most frame, and origin differs -- isolate within a new process */
+ webkit_web_policy_decision_ignore(p);
+ arg.v = (void *) uri;
+ newwindow(NULL, &arg, 0, NavImplicit);
+ return TRUE;
+ }
+}
+
+static gboolean
decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p,
Client *c) {
@@ -462,7 +516,7 @@ decidewindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
webkit_web_policy_decision_ignore(p);
arg.v = (void *)webkit_network_request_get_uri(r);
- newwindow(NULL, &arg, 0);
+ newwindow(NULL, &arg, 0, NavImplicit);
return TRUE;
}
return FALSE;
@@ -565,7 +619,7 @@ geturi(Client *c) {
char *uri;
if(!(uri = (char *)webkit_web_view_get_uri(c->view)))
- uri = "about:blank";
+ uri = "";
return uri;
}
@@ -697,6 +751,9 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
switch(webkit_web_view_get_load_status (c->view)) {
case WEBKIT_LOAD_COMMITTED:
uri = geturi(c);
+ if (strcmp(uri, "about:blank") != 0) {
+ origin_uri = uri;
+ }
if(strstr(uri, "https://") == uri) {
frame = webkit_web_view_get_main_frame(c->view);
src = webkit_web_frame_get_data_source(frame);
@@ -725,38 +782,63 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) {
}
}
-static void
-loaduri(Client *c, const Arg *arg) {
- char *u = NULL, *rp;
- const char *uri = (char *)arg->v;
- Arg a = { .b = FALSE };
+/* needs to be g_free()'d by caller */
+char *
+qualify_uri(const char *uri) {
+ char *qualified_uri = NULL, *rp;
struct stat st;
- if(strcmp(uri, "") == 0)
- return;
-
/* In case it's a file path. */
if(stat(uri, &st) == 0) {
rp = realpath(uri, NULL);
- u = g_strdup_printf("file://%s", rp);
+ qualified_uri = g_strdup_printf("file://%s", rp);
free(rp);
} else {
- u = g_strrstr(uri, "://") ? g_strdup(uri)
+ qualified_uri = g_strrstr(uri, "://") ? g_strdup(uri)
: g_strdup_printf("http://%s", uri);
}
- setatom(c, AtomUri, uri);
+ return qualified_uri;
+}
+
+static void
+loadgate(Client *c, const char *cross_from_origin, const char *cross_to_uri) {
+ char *cross_to_origin = origingeturi(cross_to_uri);
+ char *content = g_strdup_printf(originprompt, originpromptdigestsalt, originprompthmackey, cross_from_origin, cross_to_uri, cross_from_origin, cross_to_origin, cross_to_origin, cross_to_uri, cross_to_uri);
+
+ origin_uri = g_strdup(cross_to_uri);
+ webkit_web_view_load_string(c->view, content, "text/html", NULL, cross_to_origin);
+ c->progress = 0;
+ c->title = g_strdup("Origin Crossing Gate");
+ updatetitle(c);
+
+ g_free(cross_to_origin);
+ g_free(content);
+}
+
+static void
+loaduri(Client *c, const Arg *arg, const enum NavTransparency navtrans) {
+ const char *uri = (char *)arg->v;
+ Arg a = { .b = FALSE };
- /* prevents endless loop */
- if(strcmp(u, geturi(c)) == 0) {
- reload(c, &a);
+ if(strcmp(uri, "") == 0)
+ return;
+
+ if (!sameoriginpolicy || !origin_uri || !originhas(origin_uri) || originmatch(uri, origin_uri)) {
+ setatom(c, AtomUri, uri);
+
+ /* prevents endless loop */
+ if(strcmp(uri, geturi(c)) == 0) {
+ reload(c, &a);
+ } else {
+ webkit_web_view_load_uri(c->view, uri);
+ c->progress = 0;
+ c->title = copystr(&c->title, uri);
+ updatetitle(c);
+ }
} else {
- webkit_web_view_load_uri(c->view, u);
- c->progress = 0;
- c->title = copystr(&c->title, u);
- updatetitle(c);
+ newwindow(NULL, arg, 0, navtrans);
}
- g_free(u);
}
static void
@@ -835,6 +917,9 @@ newclient(void) {
"new-window-policy-decision-requested",
G_CALLBACK(decidewindow), c);
g_signal_connect(G_OBJECT(c->view),
+ "navigation-policy-decision-requested",
+ G_CALLBACK(decidenavigation), c);
+ g_signal_connect(G_OBJECT(c->view),
"mime-type-policy-decision-requested",
G_CALLBACK(decidedownload), c);
g_signal_connect(G_OBJECT(c->view),
@@ -900,6 +985,11 @@ newclient(void) {
runscript(frame);
settings = webkit_web_view_get_settings(c->view);
+ g_object_set(G_OBJECT(settings), "html5-local-storage-database-path", dbfolder, NULL);
+ g_object_set(G_OBJECT(settings), "enable-running-of-insecure-content", 0, NULL);
+ g_object_set(G_OBJECT(settings), "enable-offline-web-application-cache", 0, NULL);
+ g_object_set(G_OBJECT(settings), "enable-dns-prefetching", 0, NULL);
+
if(!(ua = getenv("SURF_USERAGENT")))
ua = useragent;
g_object_set(G_OBJECT(settings), "user-agent", ua, NULL);
@@ -924,6 +1014,11 @@ newclient(void) {
g_object_set(G_OBJECT(settings), "resizable-text-areas",
1, NULL);
+ if (!useragent) {
+ useragentscramble(c->view);
+ }
+ acceptlanguagescramble();
+
/*
* While stupid, CSS specifies that a pixel represents 1/96 of an inch.
* This ensures websites are not unusably small with a high DPI screen.
@@ -983,11 +1078,12 @@ newclient(void) {
}
static void
-newwindow(Client *c, const Arg *arg, gboolean noembed) {
+newwindow(Client *c, const Arg *arg, gboolean noembed, const enum NavTransparency navtrans) {
guint i = 0;
- const char *cmd[18], *uri;
+ const char *cmd[22], *uri;
const Arg a = { .v = (void *)cmd };
char tmp[64];
+ char *origin_packed = NULL;
cmd[i++] = argv0;
cmd[i++] = "-a";
@@ -1011,8 +1107,20 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) {
cmd[i++] = "-s";
if(showxid)
cmd[i++] = "-x";
+ if(sameoriginpolicy)
+ cmd[i++] = "-O";
+ cmd[i++] = originpromptfile;
if(enablediskcache)
cmd[i++] = "-D";
+ if(navtrans == NavImplicit) {
+ cmd[i++] = "-R";
+ if (originhas(origin_uri)) {
+ origin_packed = origingeturi(origin_uri);
+ } else {
+ origin_packed = g_strdup("-");
+ }
+ cmd[i++] = origin_packed;
+ }
cmd[i++] = "-c";
cmd[i++] = cookiefile;
cmd[i++] = "--";
@@ -1021,6 +1129,7 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) {
cmd[i++] = uri;
cmd[i++] = NULL;
spawn(NULL, &a);
+ g_free(origin_packed);
}
static gboolean
@@ -1071,11 +1180,112 @@ menuactivate(GtkMenuItem *item, Client *c) {
}
}
+static int
+origincmp(const char *uri1, const char *uri2) {
+ /* Doesn't handle default ports, but otherwise should comply with RFC 6454, The Web Origin Concept. */
+ int c;
+ if (g_str_has_prefix(uri1, "http://") && g_str_has_prefix(uri2, "http://")) {
+ return strncmp(uri1 + strlen("http://"), uri2 + strlen("http://"), strcspn(uri1 + strlen("http://"), "/?#"));
+ } else if (g_str_has_prefix(uri1, "https://") && g_str_has_prefix(uri2, "https://")) {
+ return strncmp(uri1 + strlen("https://"), uri2 + strlen("https://"), strcspn(uri1 + strlen("https://"), "/?#"));
+ } else {
+ c = strcmp(uri1, uri2);
+ if (c == 0) {
+ /* -1 when 0 to force a mismatch in originmatch() */
+ c = -1;
+ }
+ return c;
+ }
+}
+
+static int
+originhas(const char *uri) {
+ char *origin = origingethost(uri);
+ int has = origin != NULL;
+ free(origin);
+ return has;
+}
+
+static const char *
+origingetproto(const char *uri) {
+ if (g_str_has_prefix(uri, "http://")) {
+ return "http";
+ } else if (g_str_has_prefix(uri, "https://")) {
+ return "https";
+ } else {
+ return NULL;
+ }
+}
+
+/* caller must free() the return value, if not NULL */
+static char *
+origingethost(const char *uri) {
+ /* Doesn't handle default ports, but otherwise should comply with RFC 6454, The Web Origin Concept. */
+ char *origin = NULL;
+ size_t spansize;
+ size_t spanstart;
+
+ if ( g_str_has_prefix(uri, "http://")) {
+ spanstart = strlen("http://");
+ } else if (g_str_has_prefix(uri, "https://")) {
+ spanstart = strlen("https://");
+ } else {
+ /*
+ * RFC 6454: this case should return a globally unique origin.
+ *
+ * As long as processes are per-origin
+ * (that is, new origins get a new process),
+ * then relying only on process state provides this uniqueness,
+ * since anything stored would be stored by an inaccessible key.
+ *
+ * So, when the caller gets this error,
+ * it should just bypass storage altogether.
+ */
+ return NULL;
+ }
+
+ spansize = strcspn(uri + spanstart, "/?#");
+ if (spansize > 0 && uri[spanstart] == '.') {
+ /* kill attempt to traverse into parent folder */
+ return NULL;
+ }
+ origin = malloc(sizeof(char) * (spansize + 1));
+ if (origin) {
+ strncpy(origin, uri + spanstart, spansize);
+ origin[spansize] = '\0';
+ /* malloc()'d origin */
+ return origin;
+ } else {
+ /* ENOMEM set by malloc() */
+ return NULL;
+ }
+}
+
+/* caller must g_free() the return value, if not NULL */
+static char *
+origingeturi(const char *uri) {
+ const char *origin_proto = origingetproto(uri);
+ char *origin_host = origingethost(uri);
+ char *origin_packed = NULL;
+ if (origin_host) {
+ origin_packed = g_strdup_printf("%s://%s", origin_proto, origin_host);
+ }
+ g_free(origin_host);
+ return origin_packed;
+}
+
+static int
+originmatch(const char *uri1, const char *uri2) {
+ return origincmp(uri1, uri2) == 0;
+}
+
static void
pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) {
- Arg arg = {.v = text };
+ char *qualified_uri = qualify_uri(text);
+ Arg arg = {.v = qualified_uri };
if(text != NULL)
- loaduri((Client *) d, &arg);
+ loaduri((Client *) d, &arg, NavExplicit);
+ g_free(qualified_uri);
}
static void
@@ -1088,6 +1298,8 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d) {
Client *c = (Client *)d;
XPropertyEvent *ev;
Arg arg;
+ const char *unqualified_uri = NULL;
+ char *qualified_uri = NULL;
if(((XEvent *)e)->type == PropertyNotify) {
ev = &((XEvent *)e)->xproperty;
@@ -1098,10 +1310,17 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d) {
return GDK_FILTER_REMOVE;
} else if(ev->atom == atoms[AtomGo]) {
- arg.v = getatom(c, AtomGo);
- loaduri(c, &arg);
-
- return GDK_FILTER_REMOVE;
+ unqualified_uri = getatom(c, AtomGo);
+ if (unqualified_uri) {
+ qualified_uri = qualify_uri(unqualified_uri);
+ if (qualified_uri) {
+ arg.v = qualified_uri;
+ loaduri(c, &arg, NavExplicit);
+ g_free(qualified_uri);
+
+ return GDK_FILTER_REMOVE;
+ }
+ }
}
}
}
@@ -1168,10 +1387,12 @@ setatom(Client *c, int a, const char *v) {
}
static void
-setup(void) {
+setup(const char *qualified_uri) {
int i;
char *proxy;
char *new_proxy;
+ char *origin;
+ char *originpath;
SoupURI *puri;
SoupSession *s;
GError *error = NULL;
@@ -1187,10 +1408,34 @@ setup(void) {
atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False);
atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False);
+ if (originpromptfile)
+ if (!g_file_get_contents(originpromptfile, &originprompt, NULL, NULL))
+ die("Could not open origin prompt file\n");
+
/* dirs and files */
- cookiefile = buildpath(cookiefile);
+ if (sameoriginpolicy && qualified_uri && originhas(qualified_uri)) {
+ origin = origingethost(qualified_uri);
+
+ originpath = g_strdup_printf(origincookiefile, origin);
+ cookiefile = buildpath(originpath);
+ g_free(originpath);
+
+ originpath = g_strdup_printf(origincachefolder, origin);
+ cachefolder = buildpath(originpath);
+ g_free(originpath);
+
+ originpath = g_strdup_printf(origindbfolder, origin);
+ dbfolder = buildpath(originpath);
+ g_free(originpath);
+
+ free(origin);
+ } else {
+ cookiefile = buildpath(cookiefile);
+ cachefolder = buildpath(cachefolder);
+ dbfolder = buildpath(dbfolder);
+ }
+
scriptfile = buildpath(scriptfile);
- cachefolder = buildpath(cachefolder);
styledir = buildpath(styledir);
if(stylefile == NULL) {
for(i = 0; i < LENGTH(styles); i++) {
@@ -1277,6 +1522,99 @@ spawn(Client *c, const Arg *arg) {
}
}
+static int
+strrand(char *buf, int buflen) {
+ int fd;
+ int received;
+
+ fd = g_open("/dev/urandom", O_RDONLY, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ received = read(fd, buf, buflen);
+ if (received != buflen) {
+ return -1;
+ }
+ return g_close(fd, NULL);
+}
+
+/* return value must be freed with g_free() */
+static gchar *
+strentropy() {
+ gchar *strname = NULL;
+ char byte, randbuf[256], namebuf[256];
+ int rand_i, name_i;
+
+ if (strrand(randbuf, sizeof (randbuf)) == -1) {
+ return NULL;
+ }
+
+ name_i = 0;
+ for (rand_i = 0; rand_i < sizeof (randbuf) && name_i < sizeof (namebuf); rand_i++) {
+ byte = randbuf[rand_i];
+ if (byte >= ' ' - 5 && byte <= ' ') { /* space AND some characters below, to increase the number of spaces */
+ namebuf[name_i++] = ' ';
+ } else if ((byte >= 'A' && byte <= 'Z') || (byte >= 'a' && byte <= 'z') || (byte >= '0' && byte <= '9')) {
+ namebuf[name_i++] = byte;
+ } else {
+ /* some bytes are skipped, to randomly vary the length */
+ }
+ }
+ namebuf[name_i] = '\0';
+ strname = g_strndup(namebuf, sizeof(namebuf) - 1);
+
+ return strname;
+}
+
+/* return value must be freed with g_free() */
+static gchar *
+strlangentropy() {
+ gchar *strname = NULL;
+ char randbuf[4], langbuf[6];
+
+ if (strrand(randbuf, sizeof (randbuf)) == -1) {
+ return NULL;
+ }
+
+#define randbetween(low, high, randbyte) (low + ((unsigned char) randbyte % (high - low)))
+ langbuf[0] = randbetween('a', 'z', randbuf[0]);
+ langbuf[1] = randbetween('a', 'z', randbuf[1]);
+ langbuf[2] = '_';
+ langbuf[3] = randbetween('A', 'Z', randbuf[2]);
+ langbuf[4] = randbetween('A', 'Z', randbuf[3]);
+ langbuf[5] = '\0';
+
+ strname = g_strdup(langbuf);
+
+ return strname;
+}
+
+static void
+acceptlanguagescramble() {
+ SoupSession *s = webkit_get_default_session();
+ char *lang = getenv("LANG");
+ char *randlang1 = strlangentropy();
+ char *randlang2 = strlangentropy();
+ char *acceptlanguage;
+
+ if (strlen(lang) >= 5) {
+ acceptlanguage = g_strdup_printf("%5.5s, %s;q=0.9, %s;q=0.8", lang, randlang1, randlang2);
+ g_object_set(G_OBJECT(s), "accept-language", acceptlanguage, NULL);
+ }
+ g_free(randlang1);
+ g_free(randlang2);
+}
+
+static void
+useragentscramble(WebKitWebView *view) {
+ WebKitWebSettings *settings = webkit_web_view_get_settings(view);
+ gchar *ua = strentropy();
+ if (!ua)
+ ua = " "; /* fallback to blank user-agent -- NULL or "" return a webkit default string that leaks information */
+ g_object_set(G_OBJECT(settings), "user-agent", ua, NULL);
+ g_free(ua);
+}
+
static void
eval(Client *c, const Arg *arg) {
WebKitWebFrame *frame = webkit_web_view_get_main_frame(c->view);
@@ -1409,6 +1747,8 @@ gettogglestat(Client *c){
togglestat[p++] = enablediskcache? 'D': 'd';
+ togglestat[p++] = sameoriginpolicy? 'O' : 'o';
+
g_object_get(G_OBJECT(settings), "auto-load-images", &value, NULL);
togglestat[p++] = value? 'I': 'i';
@@ -1441,6 +1781,14 @@ getpagestat(Client *c) {
static void
updatetitle(Client *c) {
char *t;
+ char *originstat;
+
+ if(originhas(origin_uri)) {
+ originstat = origingethost(origin_uri);
+ } else {
+ originstat = g_strdup("-");
+ }
+
if(showindicators) {
gettogglestat(c);
@@ -1450,11 +1798,14 @@ updatetitle(Client *c) {
t = g_strdup_printf("%s:%s | %s", togglestat,
pagestat, c->linkhover);
} else if(c->progress != 100) {
- t = g_strdup_printf("[%i%%] %s:%s | %s", c->progress,
+ t = g_strdup_printf("[%i%%] %s:%s | %s | %s", c->progress,
togglestat, pagestat,
+ originstat,
(c->title == NULL)? "" : c->title);
} else {
- t = g_strdup_printf("%s:%s | %s", togglestat, pagestat,
+ t = g_strdup_printf( "%s:%s | %s | %s",
+ togglestat, pagestat,
+ originstat,
(c->title == NULL)? "" : c->title);
}
@@ -1464,6 +1815,8 @@ updatetitle(Client *c) {
gtk_window_set_title(GTK_WINDOW(c->win),
(c->title == NULL)? "" : c->title);
}
+
+ g_free(originstat);
}
static void
@@ -1507,6 +1860,7 @@ int
main(int argc, char *argv[]) {
Arg arg;
Client *c;
+ char *qualified_uri = NULL;
memset(&arg, 0, sizeof(arg));
@@ -1569,6 +1923,13 @@ main(int argc, char *argv[]) {
case 'N':
enableinspector = 1;
break;
+ case 'o':
+ sameoriginpolicy = 0;
+ break;
+ case 'O':
+ sameoriginpolicy = 1;
+ originpromptfile = EARGF(usage());
+ break;
case 'p':
enableplugins = 0;
break;
@@ -1578,6 +1939,9 @@ main(int argc, char *argv[]) {
case 'r':
scriptfile = EARGF(usage());
break;
+ case 'R':
+ referring_origin = EARGF(usage());
+ break;
case 's':
enablescripts = 0;
break;
@@ -1602,13 +1966,25 @@ main(int argc, char *argv[]) {
default:
usage();
} ARGEND;
- if(argc > 0)
- arg.v = argv[0];
+ if(argc > 0) {
+ if (argv[0]) {
+ qualified_uri = qualify_uri(argv[0]);
+ }
+ }
- setup();
+ setup(qualified_uri);
c = newclient();
- if(arg.v) {
- loaduri(clients, &arg);
+ if(qualified_uri) {
+ if (originhas(qualified_uri)) {
+ origin_uri = qualified_uri;
+ }
+ if (sameoriginpolicy && referring_origin && (strcmp(referring_origin, "-") == 0 || !originmatch(referring_origin, qualified_uri))) {
+ loadgate(clients, referring_origin, qualified_uri);
+ } else {
+ arg.v = qualified_uri;
+ loaduri(clients, &arg, NavImplicit);
+ }
+ g_free(qualified_uri);
} else {
updatetitle(c);
}
@@ -1616,6 +1992,8 @@ main(int argc, char *argv[]) {
gtk_main();
cleanup();
+ g_free(qualified_uri);
+
return EXIT_SUCCESS;
}