diff --git a/config.def.h b/config.def.h index e3b9881..591a668 100644 --- a/config.def.h +++ b/config.def.h @@ -1,10 +1,11 @@ /* 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 *scriptfile = "~/.surf/script.js"; static char *styledir = "~/.surf/styles/"; static char *cachefolder = "~/.surf/cache/"; +static char *dbfolder = "~/.surf/databases/"; +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 +17,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,22 +36,29 @@ static Bool enablestyles = TRUE; static Bool loadimages = TRUE; static Bool hidebackground = FALSE; static Bool allowgeolocation = TRUE; +static Bool sameoriginpolicy = TRUE; +static char *originprompt = "Crossing from %s to"; -#define SETPROP(p, q) { \ +#define SETPROP(p, q, prompt) { \ .v = (char *[]){ "/bin/sh", "-c", \ - "prop=\"`xprop -id $2 $0 | cut -d '\"' -f 2 | xargs -0 printf %b | dmenu`\" &&" \ + "prop=\"`xprop -id $2 $0 | cut -d '\"' -f 2 | xargs -0 printf %b | dmenu -p \"$3\"`\" &&" \ "xprop -id $2 -f $1 8s -set $1 \"$prop\"", \ - p, q, winid, NULL \ + p, q, winid, prompt, NULL \ } \ } /* DOWNLOAD(URI, referer) */ #define DOWNLOAD(d, r) { \ .v = (char *[]){ "/bin/sh", "-c", \ - "st -e /bin/sh -c \"curl -L -J -O --user-agent '$1'" \ - " --referer '$2' -b $3 -c $3 '$0';" \ - " sleep 5;\"", \ - d, useragent, r, cookiefile, NULL \ + "D=\"`printf \"${PWD}\n${HOME}\n${HOME}/Downloads\n\" | dmenu -p 'Save into' -l 3`\" &&" \ + "mkdir -p \"${D}\" && cd \"${D}\" &&" \ + "N=\"`basename \"$0\"`\" &&" \ + "F=\"`echo \"${N}\" | dmenu -p 'Save as'`\" &&" \ + "st -e /bin/sh -c \"" \ + "curl -L -J -o '$F' --user-agent '$1' --referer '$2' -b $3 -c $3 '$0';" \ + "echo 'Press Enter to close window...'; read y" \ + "\"", \ + d, (useragent ? useragent : ""), r, cookiefile, NULL \ } \ } @@ -110,9 +119,9 @@ static Key keys[] = { { MODKEY, GDK_o, source, { 0 } }, { MODKEY|GDK_SHIFT_MASK,GDK_o, inspector, { 0 } }, - { MODKEY, GDK_g, spawn, SETPROP("_SURF_URI", "_SURF_GO") }, - { MODKEY, GDK_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND") }, - { MODKEY, GDK_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND") }, + { MODKEY, GDK_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", "Go to") }, + { MODKEY, GDK_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", "Find") }, + { MODKEY, GDK_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", "/") }, { MODKEY, GDK_n, find, { .b = TRUE } }, { MODKEY|GDK_SHIFT_MASK,GDK_n, find, { .b = FALSE } }, diff --git a/surf.c b/surf.c index 48f6b39..ab10990 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,16 @@ 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 gboolean hasloaded = false; +static gboolean hasvisual = false; static void addaccelgroup(Client *c); static void beforerequest(WebKitWebView *w, WebKitWebFrame *f, @@ -119,6 +124,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 +164,16 @@ 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 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,43 @@ 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 (!hasloaded) { + /* we *are* the new window */ + return FALSE; + } else if (!origin_uri || 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 +519,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 +622,7 @@ geturi(Client *c) { char *uri; if(!(uri = (char *)webkit_web_view_get_uri(c->view))) - uri = "about:blank"; + uri = ""; return uri; } @@ -695,8 +752,14 @@ loadstatuschange(WebKitWebView *view, GParamSpec *pspec, Client *c) { char *uri; switch(webkit_web_view_get_load_status (c->view)) { + case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT: + hasvisual = true; + break; 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 +788,49 @@ 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 +loaduri(Client *c, const Arg *arg, const enum NavTransparency navtrans) { + const char *uri = (char *)arg->v; + Arg a = { .b = FALSE }; + + 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(u, geturi(c)) == 0) { - reload(c, &a); + /* prevents endless loop */ + if(strcmp(uri, geturi(c)) == 0) { + reload(c, &a); + } else { + webkit_web_view_load_uri(c->view, uri); + c->progress = 0; + hasloaded = true; + 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 @@ -780,6 +854,7 @@ newclient(void) { c->title = NULL; c->progress = 100; + hasloaded = false; /* Window */ if(embed) { @@ -835,6 +910,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 +978,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 +1007,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 +1071,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 +1100,19 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) { cmd[i++] = "-s"; if(showxid) cmd[i++] = "-x"; + if(sameoriginpolicy) + cmd[i++] = "-O"; 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 +1121,13 @@ newwindow(Client *c, const Arg *arg, gboolean noembed) { cmd[i++] = uri; cmd[i++] = NULL; spawn(NULL, &a); + g_free(origin_packed); + if (!hasvisual) { + if(dpy) + close(ConnectionNumber(dpy)); + + exit(0); + } } static gboolean @@ -1071,11 +1178,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 +1296,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 +1308,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; + } + } } } } @@ -1165,13 +1382,16 @@ setatom(Client *c, int a, const char *v) { XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), atoms[a], XA_STRING, 8, PropModeReplace, (unsigned char *)v, strlen(v) + 1); + XSync(dpy, False); } 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; @@ -1188,9 +1408,29 @@ setup(void) { atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False); /* 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 +1517,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 +1742,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 +1776,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 +1793,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 +1810,8 @@ updatetitle(Client *c) { gtk_window_set_title(GTK_WINDOW(c->win), (c->title == NULL)? "" : c->title); } + + g_free(originstat); } static void @@ -1507,6 +1855,8 @@ int main(int argc, char *argv[]) { Arg arg; Client *c; + char *qualified_uri = NULL; + char *prompt = NULL; memset(&arg, 0, sizeof(arg)); @@ -1569,6 +1919,12 @@ main(int argc, char *argv[]) { case 'N': enableinspector = 1; break; + case 'o': + sameoriginpolicy = 0; + break; + case 'O': + sameoriginpolicy = 1; + break; case 'p': enableplugins = 0; break; @@ -1578,6 +1934,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 +1961,29 @@ 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); + updatewinid(c); + if(qualified_uri) { + if (originhas(qualified_uri)) { + origin_uri = qualified_uri; + } + if (sameoriginpolicy && referring_origin && (strcmp(referring_origin, "-") == 0 || !originmatch(referring_origin, qualified_uri))) { + setatom(c, AtomUri, qualified_uri); + prompt = g_strdup_printf(originprompt, referring_origin); + arg = (Arg)SETPROP("_SURF_URI", "_SURF_GO", prompt); + spawn(c, &arg); + g_free(prompt); + } else { + arg.v = qualified_uri; + loaduri(clients, &arg, NavImplicit); + } } else { updatetitle(c); } @@ -1616,6 +1991,8 @@ main(int argc, char *argv[]) { gtk_main(); cleanup(); + g_free(qualified_uri); + return EXIT_SUCCESS; }