surf.c (56046B)
1 /* See LICENSE file for copyright and license details. 2 * 3 * To understand surf, start reading main(). 4 */ 5 #include <sys/file.h> 6 #include <sys/socket.h> 7 #include <sys/types.h> 8 #include <sys/wait.h> 9 #include <glib.h> 10 #include <inttypes.h> 11 #include <libgen.h> 12 #include <limits.h> 13 #include <pwd.h> 14 #include <regex.h> 15 #include <signal.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #include <gdk/gdk.h> 22 #include <gdk/gdkkeysyms.h> 23 #include <gdk/gdkx.h> 24 #include <glib/gstdio.h> 25 #include <gtk/gtk.h> 26 #include <gtk/gtkx.h> 27 #include <gcr/gcr.h> 28 #include <JavaScriptCore/JavaScript.h> 29 #include <webkit2/webkit2.h> 30 #include <X11/X.h> 31 #include <X11/Xatom.h> 32 #include <glib.h> 33 34 #ifdef __OpenBSD__ 35 #include <err.h> 36 #endif 37 38 #include "arg.h" 39 #include "common.h" 40 41 #define LENGTH(x) (sizeof(x) / sizeof(x[0])) 42 #define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK)) 43 44 enum { AtomFind, AtomGo, AtomUri, AtomUTF8, AtomLast }; 45 46 enum { 47 OnDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT, 48 OnLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK, 49 OnImg = WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE, 50 OnMedia = WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA, 51 OnEdit = WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE, 52 OnBar = WEBKIT_HIT_TEST_RESULT_CONTEXT_SCROLLBAR, 53 OnSel = WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION, 54 OnAny = OnDoc | OnLink | OnImg | OnMedia | OnEdit | OnBar | OnSel, 55 }; 56 57 typedef enum { 58 AccessMicrophone, 59 AccessWebcam, 60 CaretBrowsing, 61 Certificate, 62 CookiePolicies, 63 DiskCache, 64 DefaultCharset, 65 DNSPrefetch, 66 Ephemeral, 67 FileURLsCrossAccess, 68 FontSize, 69 FrameFlattening, 70 Geolocation, 71 HideBackground, 72 Inspector, 73 Java, 74 JavaScript, 75 KioskMode, 76 LoadImages, 77 MediaManualPlay, 78 PreferredLanguages, 79 RunInFullscreen, 80 ScrollBars, 81 ShowIndicators, 82 SiteQuirks, 83 SmoothScrolling, 84 SpellChecking, 85 SpellLanguages, 86 StrictTLS, 87 Style, 88 WebGL, 89 ZoomLevel, 90 ParameterLast 91 } ParamName; 92 93 typedef union { 94 int i; 95 float f; 96 const void *v; 97 } Arg; 98 99 typedef struct { 100 Arg val; 101 int prio; 102 } Parameter; 103 104 typedef struct Client { 105 GtkWidget *win; 106 WebKitWebView *view; 107 WebKitWebInspector *inspector; 108 WebKitFindController *finder; 109 WebKitHitTestResult *mousepos; 110 GTlsCertificate *cert, *failedcert; 111 GTlsCertificateFlags tlserr; 112 Window xid; 113 guint64 pageid; 114 int progress, fullscreen, https, insecure, errorpage; 115 const char *title, *overtitle, *targeturi; 116 const char *needle; 117 struct Client *next; 118 } Client; 119 120 typedef struct { 121 guint mod; 122 guint keyval; 123 void (*func)(Client *c, const Arg *a); 124 const Arg arg; 125 } Key; 126 127 typedef struct { 128 unsigned int target; 129 unsigned int mask; 130 guint button; 131 void (*func)(Client *c, const Arg *a, WebKitHitTestResult *h); 132 const Arg arg; 133 unsigned int stopevent; 134 } Button; 135 136 typedef struct { 137 const char *uri; 138 Parameter config[ParameterLast]; 139 regex_t re; 140 } UriParameters; 141 142 typedef struct { 143 char *regex; 144 char *file; 145 regex_t re; 146 } SiteSpecific; 147 148 /* Surf */ 149 static void die(const char *errstr, ...); 150 static void usage(void); 151 static void setup(void); 152 static void sigchld(int unused); 153 static void sighup(int unused); 154 static char *buildfile(const char *path); 155 static char *buildpath(const char *path); 156 static char *untildepath(const char *path); 157 static const char *getuserhomedir(const char *user); 158 static const char *getcurrentuserhomedir(void); 159 static Client *newclient(Client *c); 160 static void loaduri(Client *c, const Arg *a); 161 static const char *geturi(Client *c); 162 static void setatom(Client *c, int a, const char *v); 163 static const char *getatom(Client *c, int a); 164 static void updatetitle(Client *c); 165 static void gettogglestats(Client *c); 166 static void getpagestats(Client *c); 167 static WebKitCookieAcceptPolicy cookiepolicy_get(void); 168 static char cookiepolicy_set(const WebKitCookieAcceptPolicy p); 169 static void seturiparameters(Client *c, const char *uri, ParamName *params); 170 static void setparameter(Client *c, int refresh, ParamName p, const Arg *a); 171 static const char *getcert(const char *uri); 172 static void setcert(Client *c, const char *file); 173 static const char *getstyle(const char *uri); 174 static void setstyle(Client *c, const char *file); 175 static void runscript(Client *c); 176 static void evalscript(Client *c, const char *jsstr, ...); 177 static void updatewinid(Client *c); 178 static void handleplumb(Client *c, const char *uri); 179 static void newwindow(Client *c, const Arg *a, int noembed); 180 static void spawn(Client *c, const Arg *a); 181 static void msgext(Client *c, char type, const Arg *a); 182 static void destroyclient(Client *c); 183 static void cleanup(void); 184 185 /* GTK/WebKit */ 186 static WebKitWebView *newview(Client *c, WebKitWebView *rv); 187 static void initwebextensions(WebKitWebContext *wc, Client *c); 188 static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a, 189 Client *c); 190 static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c); 191 static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, 192 gpointer d); 193 static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c); 194 static gboolean readsock(GIOChannel *s, GIOCondition ioc, gpointer unused); 195 static void showview(WebKitWebView *v, Client *c); 196 static GtkWidget *createwindow(Client *c); 197 static gboolean loadfailedtls(WebKitWebView *v, gchar *uri, 198 GTlsCertificate *cert, 199 GTlsCertificateFlags err, Client *c); 200 static void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c); 201 static void progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c); 202 static void titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c); 203 static void mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, 204 guint modifiers, Client *c); 205 static gboolean permissionrequested(WebKitWebView *v, 206 WebKitPermissionRequest *r, Client *c); 207 static gboolean decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d, 208 WebKitPolicyDecisionType dt, Client *c); 209 static void decidenavigation(WebKitPolicyDecision *d, Client *c); 210 static void decidenewwindow(WebKitPolicyDecision *d, Client *c); 211 static void decideresource(WebKitPolicyDecision *d, Client *c); 212 static void insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, 213 Client *c); 214 static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d, 215 Client *c); 216 static void responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c); 217 static void download(Client *c, WebKitURIResponse *r); 218 static void webprocessterminated(WebKitWebView *v, 219 WebKitWebProcessTerminationReason r, 220 Client *c); 221 static void closeview(WebKitWebView *v, Client *c); 222 static void destroywin(GtkWidget* w, Client *c); 223 224 /* Hotkeys */ 225 static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d); 226 static void reload(Client *c, const Arg *a); 227 static void print(Client *c, const Arg *a); 228 static void showcert(Client *c, const Arg *a); 229 static void clipboard(Client *c, const Arg *a); 230 static void zoom(Client *c, const Arg *a); 231 static void scrollv(Client *c, const Arg *a); 232 static void scrollh(Client *c, const Arg *a); 233 static void navigate(Client *c, const Arg *a); 234 static void stop(Client *c, const Arg *a); 235 static void toggle(Client *c, const Arg *a); 236 static void togglefullscreen(Client *c, const Arg *a); 237 static void togglecookiepolicy(Client *c, const Arg *a); 238 static void toggleinspector(Client *c, const Arg *a); 239 static void find(Client *c, const Arg *a); 240 241 /* Buttons */ 242 static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h); 243 static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h); 244 static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h); 245 246 static char winid[64]; 247 static char togglestats[11]; 248 static char pagestats[2]; 249 static Atom atoms[AtomLast]; 250 static Window embed; 251 static int showxid; 252 static int cookiepolicy; 253 static Display *dpy; 254 static Client *clients; 255 static GdkDevice *gdkkb; 256 static char *stylefile; 257 static const char *useragent; 258 static Parameter *curconfig; 259 static int modparams[ParameterLast]; 260 static int spair[2]; 261 char *argv0; 262 263 static ParamName loadtransient[] = { 264 Certificate, 265 CookiePolicies, 266 DiskCache, 267 DNSPrefetch, 268 FileURLsCrossAccess, 269 JavaScript, 270 LoadImages, 271 PreferredLanguages, 272 ShowIndicators, 273 StrictTLS, 274 ParameterLast 275 }; 276 277 static ParamName loadcommitted[] = { 278 // AccessMicrophone, 279 // AccessWebcam, 280 CaretBrowsing, 281 DefaultCharset, 282 FontSize, 283 FrameFlattening, 284 Geolocation, 285 HideBackground, 286 Inspector, 287 Java, 288 // KioskMode, 289 MediaManualPlay, 290 RunInFullscreen, 291 ScrollBars, 292 SiteQuirks, 293 SmoothScrolling, 294 SpellChecking, 295 SpellLanguages, 296 Style, 297 ZoomLevel, 298 ParameterLast 299 }; 300 301 static ParamName loadfinished[] = { 302 ParameterLast 303 }; 304 305 /* configuration, allows nested code to access above variables */ 306 #include "config.h" 307 308 void 309 die(const char *errstr, ...) 310 { 311 va_list ap; 312 313 va_start(ap, errstr); 314 vfprintf(stderr, errstr, ap); 315 va_end(ap); 316 exit(1); 317 } 318 319 void 320 usage(void) 321 { 322 die("usage: surf [-bBdDfFgGiIkKmMnNpPsStTvwxX]\n" 323 "[-a cookiepolicies ] [-c cookiefile] [-C stylefile] [-e xid]\n" 324 "[-r scriptfile] [-u useragent] [-z zoomlevel] [uri]\n"); 325 } 326 327 void 328 setup(void) 329 { 330 GIOChannel *gchanin; 331 GdkDisplay *gdpy; 332 int i, j; 333 334 /* clean up any zombies immediately */ 335 sigchld(0); 336 if (signal(SIGHUP, sighup) == SIG_ERR) 337 die("Can't install SIGHUP handler"); 338 339 if (!(dpy = XOpenDisplay(NULL))) 340 die("Can't open default display"); 341 342 /* atoms */ 343 atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False); 344 atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False); 345 atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False); 346 atoms[AtomUTF8] = XInternAtom(dpy, "UTF8_STRING", False); 347 348 gtk_init(NULL, NULL); 349 350 gdpy = gdk_display_get_default(); 351 352 curconfig = defconfig; 353 354 /* dirs and files */ 355 cookiefile = buildfile(cookiefile); 356 scriptfile = buildfile(scriptfile); 357 certdir = buildpath(certdir); 358 if (curconfig[Ephemeral].val.i) 359 cachedir = NULL; 360 else 361 cachedir = buildpath(cachedir); 362 363 gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy)); 364 365 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, spair) < 0) { 366 fputs("Unable to create sockets\n", stderr); 367 spair[0] = spair[1] = -1; 368 } else { 369 gchanin = g_io_channel_unix_new(spair[0]); 370 g_io_channel_set_encoding(gchanin, NULL, NULL); 371 g_io_channel_set_flags(gchanin, g_io_channel_get_flags(gchanin) 372 | G_IO_FLAG_NONBLOCK, NULL); 373 g_io_channel_set_close_on_unref(gchanin, TRUE); 374 g_io_add_watch(gchanin, G_IO_IN, readsock, NULL); 375 } 376 377 378 for (i = 0; i < LENGTH(certs); ++i) { 379 if (!regcomp(&(certs[i].re), certs[i].regex, REG_EXTENDED)) { 380 certs[i].file = g_strconcat(certdir, "/", certs[i].file, 381 NULL); 382 } else { 383 fprintf(stderr, "Could not compile regex: %s\n", 384 certs[i].regex); 385 certs[i].regex = NULL; 386 } 387 } 388 389 if (!stylefile) { 390 styledir = buildpath(styledir); 391 for (i = 0; i < LENGTH(styles); ++i) { 392 if (!regcomp(&(styles[i].re), styles[i].regex, 393 REG_EXTENDED)) { 394 styles[i].file = g_strconcat(styledir, "/", 395 styles[i].file, NULL); 396 } else { 397 fprintf(stderr, "Could not compile regex: %s\n", 398 styles[i].regex); 399 styles[i].regex = NULL; 400 } 401 } 402 g_free(styledir); 403 } else { 404 stylefile = buildfile(stylefile); 405 } 406 407 for (i = 0; i < LENGTH(uriparams); ++i) { 408 if (regcomp(&(uriparams[i].re), uriparams[i].uri, 409 REG_EXTENDED)) { 410 fprintf(stderr, "Could not compile regex: %s\n", 411 uriparams[i].uri); 412 uriparams[i].uri = NULL; 413 continue; 414 } 415 416 /* copy default parameters with higher priority */ 417 for (j = 0; j < ParameterLast; ++j) { 418 if (defconfig[j].prio >= uriparams[i].config[j].prio) 419 uriparams[i].config[j] = defconfig[j]; 420 } 421 } 422 } 423 424 void 425 sigchld(int unused) 426 { 427 if (signal(SIGCHLD, sigchld) == SIG_ERR) 428 die("Can't install SIGCHLD handler"); 429 while (waitpid(-1, NULL, WNOHANG) > 0) 430 ; 431 } 432 433 void 434 sighup(int unused) 435 { 436 Arg a = { .i = 0 }; 437 Client *c; 438 439 for (c = clients; c; c = c->next) 440 reload(c, &a); 441 } 442 443 char * 444 buildfile(const char *path) 445 { 446 char *dname, *bname, *bpath, *fpath; 447 FILE *f; 448 449 dname = g_path_get_dirname(path); 450 bname = g_path_get_basename(path); 451 452 bpath = buildpath(dname); 453 g_free(dname); 454 455 fpath = g_build_filename(bpath, bname, NULL); 456 g_free(bpath); 457 g_free(bname); 458 459 if (!(f = fopen(fpath, "a"))) 460 die("Could not open file: %s\n", fpath); 461 462 g_chmod(fpath, 0600); /* always */ 463 fclose(f); 464 465 return fpath; 466 } 467 468 static const char* 469 getuserhomedir(const char *user) 470 { 471 struct passwd *pw = getpwnam(user); 472 473 if (!pw) 474 die("Can't get user %s login information.\n", user); 475 476 return pw->pw_dir; 477 } 478 479 static const char* 480 getcurrentuserhomedir(void) 481 { 482 const char *homedir; 483 const char *user; 484 struct passwd *pw; 485 486 homedir = getenv("HOME"); 487 if (homedir) 488 return homedir; 489 490 user = getenv("USER"); 491 if (user) 492 return getuserhomedir(user); 493 494 pw = getpwuid(getuid()); 495 if (!pw) 496 die("Can't get current user home directory\n"); 497 498 return pw->pw_dir; 499 } 500 501 char * 502 buildpath(const char *path) 503 { 504 char *apath, *fpath; 505 506 if (path[0] == '~') 507 apath = untildepath(path); 508 else 509 apath = g_strdup(path); 510 511 /* creating directory */ 512 if (g_mkdir_with_parents(apath, 0700) < 0) 513 die("Could not access directory: %s\n", apath); 514 515 fpath = realpath(apath, NULL); 516 g_free(apath); 517 518 return fpath; 519 } 520 521 char * 522 untildepath(const char *path) 523 { 524 char *apath, *name, *p; 525 const char *homedir; 526 527 if (path[1] == '/' || path[1] == '\0') { 528 p = (char *)&path[1]; 529 homedir = getcurrentuserhomedir(); 530 } else { 531 if ((p = strchr(path, '/'))) 532 name = g_strndup(&path[1], p - (path + 1)); 533 else 534 name = g_strdup(&path[1]); 535 536 homedir = getuserhomedir(name); 537 g_free(name); 538 } 539 apath = g_build_filename(homedir, p, NULL); 540 return apath; 541 } 542 543 Client * 544 newclient(Client *rc) 545 { 546 Client *c; 547 548 if (!(c = calloc(1, sizeof(Client)))) 549 die("Cannot malloc!\n"); 550 551 c->next = clients; 552 clients = c; 553 554 c->progress = 100; 555 c->view = newview(c, rc ? rc->view : NULL); 556 557 return c; 558 } 559 560 void 561 loaduri(Client *c, const Arg *a) 562 { 563 struct stat st; 564 char *url, *path, *apath; 565 const char *uri = a->v; 566 567 if (g_strcmp0(uri, "") == 0) 568 return; 569 570 if (g_str_has_prefix(uri, "http://") || 571 g_str_has_prefix(uri, "https://") || 572 g_str_has_prefix(uri, "file://") || 573 g_str_has_prefix(uri, "about:")) { 574 url = g_strdup(uri); 575 } else { 576 if (uri[0] == '~') 577 apath = untildepath(uri); 578 else 579 apath = (char *)uri; 580 if (!stat(apath, &st) && (path = realpath(apath, NULL))) { 581 url = g_strdup_printf("file://%s", path); 582 free(path); 583 } else { 584 url = g_strdup_printf("http://%s", uri); 585 } 586 if (apath != uri) 587 free(apath); 588 } 589 590 setatom(c, AtomUri, url); 591 592 if (strcmp(url, geturi(c)) == 0) { 593 reload(c, a); 594 } else { 595 webkit_web_view_load_uri(c->view, url); 596 updatetitle(c); 597 } 598 599 g_free(url); 600 } 601 602 const char * 603 geturi(Client *c) 604 { 605 const char *uri; 606 607 if (!(uri = webkit_web_view_get_uri(c->view))) 608 uri = "about:blank"; 609 return uri; 610 } 611 612 void 613 setatom(Client *c, int a, const char *v) 614 { 615 XChangeProperty(dpy, c->xid, 616 atoms[a], atoms[AtomUTF8], 8, PropModeReplace, 617 (unsigned char *)v, strlen(v) + 1); 618 XSync(dpy, False); 619 } 620 621 const char * 622 getatom(Client *c, int a) 623 { 624 static char buf[BUFSIZ]; 625 Atom adummy; 626 int idummy; 627 unsigned long ldummy; 628 unsigned char *p = NULL; 629 630 XSync(dpy, False); 631 XGetWindowProperty(dpy, c->xid, 632 atoms[a], 0L, BUFSIZ, False, atoms[AtomUTF8], 633 &adummy, &idummy, &ldummy, &ldummy, &p); 634 if (p) 635 strncpy(buf, (char *)p, LENGTH(buf) - 1); 636 else 637 buf[0] = '\0'; 638 XFree(p); 639 640 return buf; 641 } 642 643 void 644 updatetitle(Client *c) 645 { 646 char *title; 647 const char *name = c->overtitle ? c->overtitle : 648 c->title ? c->title : ""; 649 650 if (curconfig[ShowIndicators].val.i) { 651 gettogglestats(c); 652 getpagestats(c); 653 654 if (c->progress != 100) 655 title = g_strdup_printf("[%i%%] %s:%s | %s", 656 c->progress, togglestats, pagestats, name); 657 else 658 title = g_strdup_printf("%s:%s | %s", 659 togglestats, pagestats, name); 660 661 gtk_window_set_title(GTK_WINDOW(c->win), title); 662 g_free(title); 663 } else { 664 gtk_window_set_title(GTK_WINDOW(c->win), name); 665 } 666 } 667 668 void 669 gettogglestats(Client *c) 670 { 671 togglestats[0] = cookiepolicy_set(cookiepolicy_get()); 672 togglestats[1] = curconfig[CaretBrowsing].val.i ? 'C' : 'c'; 673 togglestats[2] = curconfig[Geolocation].val.i ? 'G' : 'g'; 674 togglestats[3] = curconfig[DiskCache].val.i ? 'D' : 'd'; 675 togglestats[4] = curconfig[LoadImages].val.i ? 'I' : 'i'; 676 togglestats[5] = curconfig[JavaScript].val.i ? 'S' : 's'; 677 togglestats[6] = curconfig[Style].val.i ? 'M' : 'm'; 678 togglestats[7] = curconfig[FrameFlattening].val.i ? 'F' : 'f'; 679 togglestats[8] = curconfig[Certificate].val.i ? 'X' : 'x'; 680 togglestats[9] = curconfig[StrictTLS].val.i ? 'T' : 't'; 681 } 682 683 void 684 getpagestats(Client *c) 685 { 686 if (c->https) 687 pagestats[0] = (c->tlserr || c->insecure) ? 'U' : 'T'; 688 else 689 pagestats[0] = '-'; 690 pagestats[1] = '\0'; 691 } 692 693 WebKitCookieAcceptPolicy 694 cookiepolicy_get(void) 695 { 696 switch (((char *)curconfig[CookiePolicies].val.v)[cookiepolicy]) { 697 case 'a': 698 return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER; 699 case '@': 700 return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY; 701 default: /* fallthrough */ 702 case 'A': 703 return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS; 704 } 705 } 706 707 char 708 cookiepolicy_set(const WebKitCookieAcceptPolicy p) 709 { 710 switch (p) { 711 case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER: 712 return 'a'; 713 case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY: 714 return '@'; 715 default: /* fallthrough */ 716 case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS: 717 return 'A'; 718 } 719 } 720 721 void 722 seturiparameters(Client *c, const char *uri, ParamName *params) 723 { 724 Parameter *config, *uriconfig = NULL; 725 int i, p; 726 727 for (i = 0; i < LENGTH(uriparams); ++i) { 728 if (uriparams[i].uri && 729 !regexec(&(uriparams[i].re), uri, 0, NULL, 0)) { 730 uriconfig = uriparams[i].config; 731 break; 732 } 733 } 734 735 curconfig = uriconfig ? uriconfig : defconfig; 736 737 for (i = 0; (p = params[i]) != ParameterLast; ++i) { 738 switch(p) { 739 default: /* FALLTHROUGH */ 740 if (!(defconfig[p].prio < curconfig[p].prio || 741 defconfig[p].prio < modparams[p])) 742 continue; 743 case Certificate: 744 case CookiePolicies: 745 case Style: 746 setparameter(c, 0, p, &curconfig[p].val); 747 } 748 } 749 } 750 751 void 752 setparameter(Client *c, int refresh, ParamName p, const Arg *a) 753 { 754 GdkRGBA bgcolor = { 0 }; 755 WebKitSettings *s = webkit_web_view_get_settings(c->view); 756 757 modparams[p] = curconfig[p].prio; 758 759 switch (p) { 760 case AccessMicrophone: 761 return; /* do nothing */ 762 case AccessWebcam: 763 return; /* do nothing */ 764 case CaretBrowsing: 765 webkit_settings_set_enable_caret_browsing(s, a->i); 766 refresh = 0; 767 break; 768 case Certificate: 769 if (a->i) 770 setcert(c, geturi(c)); 771 return; /* do not update */ 772 case CookiePolicies: 773 webkit_cookie_manager_set_accept_policy( 774 webkit_web_context_get_cookie_manager( 775 webkit_web_view_get_context(c->view)), 776 cookiepolicy_get()); 777 refresh = 0; 778 break; 779 case DiskCache: 780 webkit_web_context_set_cache_model( 781 webkit_web_view_get_context(c->view), a->i ? 782 WEBKIT_CACHE_MODEL_WEB_BROWSER : 783 WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); 784 return; /* do not update */ 785 case DefaultCharset: 786 webkit_settings_set_default_charset(s, a->v); 787 return; /* do not update */ 788 case DNSPrefetch: 789 webkit_settings_set_enable_dns_prefetching(s, a->i); 790 return; /* do not update */ 791 case FileURLsCrossAccess: 792 webkit_settings_set_allow_file_access_from_file_urls(s, a->i); 793 webkit_settings_set_allow_universal_access_from_file_urls(s, a->i); 794 return; /* do not update */ 795 case FontSize: 796 webkit_settings_set_default_font_size(s, a->i); 797 return; /* do not update */ 798 case FrameFlattening: 799 webkit_settings_set_enable_frame_flattening(s, a->i); 800 break; 801 case Geolocation: 802 refresh = 0; 803 break; 804 case HideBackground: 805 if (a->i) 806 webkit_web_view_set_background_color(c->view, &bgcolor); 807 return; /* do not update */ 808 case Inspector: 809 webkit_settings_set_enable_developer_extras(s, a->i); 810 return; /* do not update */ 811 case Java: 812 webkit_settings_set_enable_java(s, a->i); 813 return; /* do not update */ 814 case JavaScript: 815 webkit_settings_set_enable_javascript(s, a->i); 816 break; 817 case KioskMode: 818 return; /* do nothing */ 819 case LoadImages: 820 webkit_settings_set_auto_load_images(s, a->i); 821 break; 822 case MediaManualPlay: 823 webkit_settings_set_media_playback_requires_user_gesture(s, a->i); 824 break; 825 case PreferredLanguages: 826 return; /* do nothing */ 827 case RunInFullscreen: 828 return; /* do nothing */ 829 case ScrollBars: 830 /* Disabled until we write some WebKitWebExtension for 831 * manipulating the DOM directly. 832 enablescrollbars = !enablescrollbars; 833 evalscript(c, "document.documentElement.style.overflow = '%s'", 834 enablescrollbars ? "auto" : "hidden"); 835 */ 836 return; /* do not update */ 837 case ShowIndicators: 838 break; 839 case SmoothScrolling: 840 webkit_settings_set_enable_smooth_scrolling(s, a->i); 841 return; /* do not update */ 842 case SiteQuirks: 843 webkit_settings_set_enable_site_specific_quirks(s, a->i); 844 break; 845 case SpellChecking: 846 webkit_web_context_set_spell_checking_enabled( 847 webkit_web_view_get_context(c->view), a->i); 848 return; /* do not update */ 849 case SpellLanguages: 850 return; /* do nothing */ 851 case StrictTLS: 852 webkit_web_context_set_tls_errors_policy( 853 webkit_web_view_get_context(c->view), a->i ? 854 WEBKIT_TLS_ERRORS_POLICY_FAIL : 855 WEBKIT_TLS_ERRORS_POLICY_IGNORE); 856 break; 857 case Style: 858 webkit_user_content_manager_remove_all_style_sheets( 859 webkit_web_view_get_user_content_manager(c->view)); 860 if (a->i) 861 setstyle(c, getstyle(geturi(c))); 862 refresh = 0; 863 break; 864 case WebGL: 865 webkit_settings_set_enable_webgl(s, a->i); 866 break; 867 case ZoomLevel: 868 webkit_web_view_set_zoom_level(c->view, a->f); 869 return; /* do not update */ 870 default: 871 return; /* do nothing */ 872 } 873 874 updatetitle(c); 875 if (refresh) 876 reload(c, a); 877 } 878 879 const char * 880 getcert(const char *uri) 881 { 882 int i; 883 884 for (i = 0; i < LENGTH(certs); ++i) { 885 if (certs[i].regex && 886 !regexec(&(certs[i].re), uri, 0, NULL, 0)) 887 return certs[i].file; 888 } 889 890 return NULL; 891 } 892 893 void 894 setcert(Client *c, const char *uri) 895 { 896 const char *file = getcert(uri); 897 char *host; 898 GTlsCertificate *cert; 899 900 if (!file) 901 return; 902 903 if (!(cert = g_tls_certificate_new_from_file(file, NULL))) { 904 fprintf(stderr, "Could not read certificate file: %s\n", file); 905 return; 906 } 907 908 if ((uri = strstr(uri, "https://"))) { 909 uri += sizeof("https://") - 1; 910 host = g_strndup(uri, strchr(uri, '/') - uri); 911 webkit_web_context_allow_tls_certificate_for_host( 912 webkit_web_view_get_context(c->view), cert, host); 913 g_free(host); 914 } 915 916 g_object_unref(cert); 917 918 } 919 920 const char * 921 getstyle(const char *uri) 922 { 923 int i; 924 925 if (stylefile) 926 return stylefile; 927 928 for (i = 0; i < LENGTH(styles); ++i) { 929 if (styles[i].regex && 930 !regexec(&(styles[i].re), uri, 0, NULL, 0)) 931 return styles[i].file; 932 } 933 934 return ""; 935 } 936 937 void 938 setstyle(Client *c, const char *file) 939 { 940 gchar *style; 941 942 if (!g_file_get_contents(file, &style, NULL, NULL)) { 943 fprintf(stderr, "Could not read style file: %s\n", file); 944 return; 945 } 946 947 webkit_user_content_manager_add_style_sheet( 948 webkit_web_view_get_user_content_manager(c->view), 949 webkit_user_style_sheet_new(style, 950 WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, 951 WEBKIT_USER_STYLE_LEVEL_USER, 952 NULL, NULL)); 953 954 g_free(style); 955 } 956 957 void 958 runscript(Client *c) 959 { 960 gchar *script; 961 gsize l; 962 963 if (g_file_get_contents(scriptfile, &script, &l, NULL) && l) 964 evalscript(c, "%s", script); 965 g_free(script); 966 } 967 968 void 969 evalscript(Client *c, const char *jsstr, ...) 970 { 971 va_list ap; 972 gchar *script; 973 974 va_start(ap, jsstr); 975 script = g_strdup_vprintf(jsstr, ap); 976 va_end(ap); 977 978 webkit_web_view_run_javascript(c->view, script, NULL, NULL, NULL); 979 g_free(script); 980 } 981 982 void 983 updatewinid(Client *c) 984 { 985 snprintf(winid, LENGTH(winid), "%lu", c->xid); 986 } 987 988 void 989 handleplumb(Client *c, const char *uri) 990 { 991 Arg a = (Arg)PLUMB(uri); 992 spawn(c, &a); 993 } 994 995 void 996 newwindow(Client *c, const Arg *a, int noembed) 997 { 998 int i = 0; 999 char tmp[64]; 1000 const char *cmd[29], *uri; 1001 const Arg arg = { .v = cmd }; 1002 1003 cmd[i++] = argv0; 1004 cmd[i++] = "-a"; 1005 cmd[i++] = curconfig[CookiePolicies].val.v; 1006 cmd[i++] = curconfig[ScrollBars].val.i ? "-B" : "-b"; 1007 if (cookiefile && g_strcmp0(cookiefile, "")) { 1008 cmd[i++] = "-c"; 1009 cmd[i++] = cookiefile; 1010 } 1011 if (stylefile && g_strcmp0(stylefile, "")) { 1012 cmd[i++] = "-C"; 1013 cmd[i++] = stylefile; 1014 } 1015 cmd[i++] = curconfig[DiskCache].val.i ? "-D" : "-d"; 1016 if (embed && !noembed) { 1017 cmd[i++] = "-e"; 1018 snprintf(tmp, LENGTH(tmp), "%lu", embed); 1019 cmd[i++] = tmp; 1020 } 1021 cmd[i++] = curconfig[RunInFullscreen].val.i ? "-F" : "-f" ; 1022 cmd[i++] = curconfig[Geolocation].val.i ? "-G" : "-g" ; 1023 cmd[i++] = curconfig[LoadImages].val.i ? "-I" : "-i" ; 1024 cmd[i++] = curconfig[KioskMode].val.i ? "-K" : "-k" ; 1025 cmd[i++] = curconfig[Style].val.i ? "-M" : "-m" ; 1026 cmd[i++] = curconfig[Inspector].val.i ? "-N" : "-n" ; 1027 if (scriptfile && g_strcmp0(scriptfile, "")) { 1028 cmd[i++] = "-r"; 1029 cmd[i++] = scriptfile; 1030 } 1031 cmd[i++] = curconfig[JavaScript].val.i ? "-S" : "-s"; 1032 cmd[i++] = curconfig[StrictTLS].val.i ? "-T" : "-t"; 1033 if (fulluseragent && g_strcmp0(fulluseragent, "")) { 1034 cmd[i++] = "-u"; 1035 cmd[i++] = fulluseragent; 1036 } 1037 if (showxid) 1038 cmd[i++] = "-w"; 1039 cmd[i++] = curconfig[Certificate].val.i ? "-X" : "-x" ; 1040 /* do not keep zoom level */ 1041 cmd[i++] = "--"; 1042 if ((uri = a->v)) 1043 cmd[i++] = uri; 1044 cmd[i] = NULL; 1045 1046 spawn(c, &arg); 1047 } 1048 1049 void 1050 spawn(Client *c, const Arg *a) 1051 { 1052 if (fork() == 0) { 1053 if (dpy) 1054 close(ConnectionNumber(dpy)); 1055 close(spair[0]); 1056 close(spair[1]); 1057 setsid(); 1058 execvp(((char **)a->v)[0], (char **)a->v); 1059 fprintf(stderr, "%s: execvp %s", argv0, ((char **)a->v)[0]); 1060 perror(" failed"); 1061 exit(1); 1062 } 1063 } 1064 1065 void 1066 destroyclient(Client *c) 1067 { 1068 Client *p; 1069 1070 webkit_web_view_stop_loading(c->view); 1071 /* Not needed, has already been called 1072 gtk_widget_destroy(c->win); 1073 */ 1074 1075 for (p = clients; p && p->next != c; p = p->next) 1076 ; 1077 if (p) 1078 p->next = c->next; 1079 else 1080 clients = c->next; 1081 free(c); 1082 } 1083 1084 void 1085 cleanup(void) 1086 { 1087 while (clients) 1088 destroyclient(clients); 1089 1090 close(spair[0]); 1091 close(spair[1]); 1092 g_free(cookiefile); 1093 g_free(scriptfile); 1094 g_free(stylefile); 1095 g_free(cachedir); 1096 XCloseDisplay(dpy); 1097 } 1098 1099 WebKitWebView * 1100 newview(Client *c, WebKitWebView *rv) 1101 { 1102 WebKitWebView *v; 1103 WebKitSettings *settings; 1104 WebKitWebContext *context; 1105 WebKitCookieManager *cookiemanager; 1106 WebKitUserContentManager *contentmanager; 1107 1108 /* Webview */ 1109 if (rv) { 1110 v = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(rv)); 1111 } else { 1112 settings = webkit_settings_new_with_settings( 1113 "allow-file-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i, 1114 "allow-universal-access-from-file-urls", curconfig[FileURLsCrossAccess].val.i, 1115 "auto-load-images", curconfig[LoadImages].val.i, 1116 "default-charset", curconfig[DefaultCharset].val.v, 1117 "default-font-size", curconfig[FontSize].val.i, 1118 "enable-caret-browsing", curconfig[CaretBrowsing].val.i, 1119 "enable-developer-extras", curconfig[Inspector].val.i, 1120 "enable-dns-prefetching", curconfig[DNSPrefetch].val.i, 1121 "enable-frame-flattening", curconfig[FrameFlattening].val.i, 1122 "enable-html5-database", curconfig[DiskCache].val.i, 1123 "enable-html5-local-storage", curconfig[DiskCache].val.i, 1124 "enable-java", curconfig[Java].val.i, 1125 "enable-javascript", curconfig[JavaScript].val.i, 1126 "enable-site-specific-quirks", curconfig[SiteQuirks].val.i, 1127 "enable-smooth-scrolling", curconfig[SmoothScrolling].val.i, 1128 "enable-webgl", curconfig[WebGL].val.i, 1129 "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.i, 1130 NULL); 1131 /* For more interesting settings, have a look at 1132 * http://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html */ 1133 1134 if (strcmp(fulluseragent, "")) { 1135 webkit_settings_set_user_agent(settings, fulluseragent); 1136 } else if (surfuseragent) { 1137 webkit_settings_set_user_agent_with_application_details( 1138 settings, "Surf", VERSION); 1139 } 1140 useragent = webkit_settings_get_user_agent(settings); 1141 1142 contentmanager = webkit_user_content_manager_new(); 1143 1144 if (curconfig[Ephemeral].val.i) { 1145 context = webkit_web_context_new_ephemeral(); 1146 } else { 1147 context = webkit_web_context_new_with_website_data_manager( 1148 webkit_website_data_manager_new( 1149 "base-cache-directory", cachedir, 1150 "base-data-directory", cachedir, 1151 NULL)); 1152 } 1153 1154 1155 cookiemanager = webkit_web_context_get_cookie_manager(context); 1156 1157 /* rendering process model, can be a shared unique one 1158 * or one for each view */ 1159 webkit_web_context_set_process_model(context, 1160 WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); 1161 /* TLS */ 1162 webkit_web_context_set_tls_errors_policy(context, 1163 curconfig[StrictTLS].val.i ? WEBKIT_TLS_ERRORS_POLICY_FAIL : 1164 WEBKIT_TLS_ERRORS_POLICY_IGNORE); 1165 /* disk cache */ 1166 webkit_web_context_set_cache_model(context, 1167 curconfig[DiskCache].val.i ? WEBKIT_CACHE_MODEL_WEB_BROWSER : 1168 WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER); 1169 1170 /* Currently only works with text file to be compatible with curl */ 1171 if (!curconfig[Ephemeral].val.i) 1172 webkit_cookie_manager_set_persistent_storage(cookiemanager, 1173 cookiefile, WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT); 1174 /* cookie policy */ 1175 webkit_cookie_manager_set_accept_policy(cookiemanager, 1176 cookiepolicy_get()); 1177 /* languages */ 1178 webkit_web_context_set_preferred_languages(context, 1179 curconfig[PreferredLanguages].val.v); 1180 webkit_web_context_set_spell_checking_languages(context, 1181 curconfig[SpellLanguages].val.v); 1182 webkit_web_context_set_spell_checking_enabled(context, 1183 curconfig[SpellChecking].val.i); 1184 1185 g_signal_connect(G_OBJECT(context), "download-started", 1186 G_CALLBACK(downloadstarted), c); 1187 g_signal_connect(G_OBJECT(context), "initialize-web-extensions", 1188 G_CALLBACK(initwebextensions), c); 1189 1190 v = g_object_new(WEBKIT_TYPE_WEB_VIEW, 1191 "settings", settings, 1192 "user-content-manager", contentmanager, 1193 "web-context", context, 1194 NULL); 1195 } 1196 1197 g_signal_connect(G_OBJECT(v), "notify::estimated-load-progress", 1198 G_CALLBACK(progresschanged), c); 1199 g_signal_connect(G_OBJECT(v), "notify::title", 1200 G_CALLBACK(titlechanged), c); 1201 g_signal_connect(G_OBJECT(v), "button-release-event", 1202 G_CALLBACK(buttonreleased), c); 1203 g_signal_connect(G_OBJECT(v), "close", 1204 G_CALLBACK(closeview), c); 1205 g_signal_connect(G_OBJECT(v), "create", 1206 G_CALLBACK(createview), c); 1207 g_signal_connect(G_OBJECT(v), "decide-policy", 1208 G_CALLBACK(decidepolicy), c); 1209 g_signal_connect(G_OBJECT(v), "insecure-content-detected", 1210 G_CALLBACK(insecurecontent), c); 1211 g_signal_connect(G_OBJECT(v), "load-failed-with-tls-errors", 1212 G_CALLBACK(loadfailedtls), c); 1213 g_signal_connect(G_OBJECT(v), "load-changed", 1214 G_CALLBACK(loadchanged), c); 1215 g_signal_connect(G_OBJECT(v), "mouse-target-changed", 1216 G_CALLBACK(mousetargetchanged), c); 1217 g_signal_connect(G_OBJECT(v), "permission-request", 1218 G_CALLBACK(permissionrequested), c); 1219 g_signal_connect(G_OBJECT(v), "ready-to-show", 1220 G_CALLBACK(showview), c); 1221 g_signal_connect(G_OBJECT(v), "web-process-terminated", 1222 G_CALLBACK(webprocessterminated), c); 1223 1224 return v; 1225 } 1226 1227 static gboolean 1228 readsock(GIOChannel *s, GIOCondition ioc, gpointer unused) 1229 { 1230 static char msg[MSGBUFSZ]; 1231 GError *gerr = NULL; 1232 gsize msgsz; 1233 1234 if (g_io_channel_read_chars(s, msg, sizeof(msg), &msgsz, &gerr) != 1235 G_IO_STATUS_NORMAL) { 1236 if (gerr) { 1237 fprintf(stderr, "surf: error reading socket: %s\n", 1238 gerr->message); 1239 g_error_free(gerr); 1240 } 1241 return TRUE; 1242 } 1243 if (msgsz < 2) { 1244 fprintf(stderr, "surf: message too short: %d\n", msgsz); 1245 return TRUE; 1246 } 1247 1248 return TRUE; 1249 } 1250 1251 void 1252 initwebextensions(WebKitWebContext *wc, Client *c) 1253 { 1254 GVariant *gv; 1255 1256 if (spair[1] < 0) 1257 return; 1258 1259 gv = g_variant_new("i", spair[1]); 1260 1261 webkit_web_context_set_web_extensions_initialization_user_data(wc, gv); 1262 webkit_web_context_set_web_extensions_directory(wc, WEBEXTDIR); 1263 } 1264 1265 GtkWidget * 1266 createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c) 1267 { 1268 Client *n; 1269 1270 switch (webkit_navigation_action_get_navigation_type(a)) { 1271 case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ 1272 /* 1273 * popup windows of type “other” are almost always triggered 1274 * by user gesture, so inverse the logic here 1275 */ 1276 /* instead of this, compare destination uri to mouse-over uri for validating window */ 1277 if (webkit_navigation_action_is_user_gesture(a)) 1278 return NULL; 1279 case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ 1280 case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ 1281 case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ 1282 case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ 1283 case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: 1284 n = newclient(c); 1285 break; 1286 default: 1287 return NULL; 1288 } 1289 1290 return GTK_WIDGET(n->view); 1291 } 1292 1293 gboolean 1294 buttonreleased(GtkWidget *w, GdkEvent *e, Client *c) 1295 { 1296 WebKitHitTestResultContext element; 1297 int i; 1298 1299 element = webkit_hit_test_result_get_context(c->mousepos); 1300 1301 for (i = 0; i < LENGTH(buttons); ++i) { 1302 if (element & buttons[i].target && 1303 e->button.button == buttons[i].button && 1304 CLEANMASK(e->button.state) == CLEANMASK(buttons[i].mask) && 1305 buttons[i].func) { 1306 buttons[i].func(c, &buttons[i].arg, c->mousepos); 1307 return buttons[i].stopevent; 1308 } 1309 } 1310 1311 return FALSE; 1312 } 1313 1314 GdkFilterReturn 1315 processx(GdkXEvent *e, GdkEvent *event, gpointer d) 1316 { 1317 Client *c = (Client *)d; 1318 XPropertyEvent *ev; 1319 Arg a; 1320 1321 if (((XEvent *)e)->type == PropertyNotify) { 1322 ev = &((XEvent *)e)->xproperty; 1323 if (ev->state == PropertyNewValue) { 1324 if (ev->atom == atoms[AtomFind]) { 1325 find(c, NULL); 1326 1327 return GDK_FILTER_REMOVE; 1328 } else if (ev->atom == atoms[AtomGo]) { 1329 a.v = getatom(c, AtomGo); 1330 loaduri(c, &a); 1331 1332 return GDK_FILTER_REMOVE; 1333 } 1334 } 1335 } 1336 return GDK_FILTER_CONTINUE; 1337 } 1338 1339 gboolean 1340 winevent(GtkWidget *w, GdkEvent *e, Client *c) 1341 { 1342 int i; 1343 1344 switch (e->type) { 1345 case GDK_ENTER_NOTIFY: 1346 c->overtitle = c->targeturi; 1347 updatetitle(c); 1348 break; 1349 case GDK_KEY_PRESS: 1350 if (!curconfig[KioskMode].val.i) { 1351 for (i = 0; i < LENGTH(keys); ++i) { 1352 if (gdk_keyval_to_lower(e->key.keyval) == 1353 keys[i].keyval && 1354 CLEANMASK(e->key.state) == keys[i].mod && 1355 keys[i].func) { 1356 updatewinid(c); 1357 keys[i].func(c, &(keys[i].arg)); 1358 return TRUE; 1359 } 1360 } 1361 } 1362 case GDK_LEAVE_NOTIFY: 1363 c->overtitle = NULL; 1364 updatetitle(c); 1365 break; 1366 case GDK_WINDOW_STATE: 1367 if (e->window_state.changed_mask == 1368 GDK_WINDOW_STATE_FULLSCREEN) 1369 c->fullscreen = e->window_state.new_window_state & 1370 GDK_WINDOW_STATE_FULLSCREEN; 1371 break; 1372 default: 1373 break; 1374 } 1375 1376 return FALSE; 1377 } 1378 1379 void 1380 showview(WebKitWebView *v, Client *c) 1381 { 1382 GdkRGBA bgcolor = { 0 }; 1383 GdkWindow *gwin; 1384 1385 c->finder = webkit_web_view_get_find_controller(c->view); 1386 c->inspector = webkit_web_view_get_inspector(c->view); 1387 1388 c->pageid = webkit_web_view_get_page_id(c->view); 1389 c->win = createwindow(c); 1390 1391 gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view)); 1392 gtk_widget_show_all(c->win); 1393 gtk_widget_grab_focus(GTK_WIDGET(c->view)); 1394 1395 gwin = gtk_widget_get_window(GTK_WIDGET(c->win)); 1396 c->xid = gdk_x11_window_get_xid(gwin); 1397 updatewinid(c); 1398 if (showxid) { 1399 gdk_display_sync(gtk_widget_get_display(c->win)); 1400 puts(winid); 1401 fflush(stdout); 1402 } 1403 1404 if (curconfig[HideBackground].val.i) 1405 webkit_web_view_set_background_color(c->view, &bgcolor); 1406 1407 if (!curconfig[KioskMode].val.i) { 1408 gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK); 1409 gdk_window_add_filter(gwin, processx, c); 1410 } 1411 1412 if (curconfig[RunInFullscreen].val.i) 1413 togglefullscreen(c, NULL); 1414 1415 if (curconfig[ZoomLevel].val.f != 1.0) 1416 webkit_web_view_set_zoom_level(c->view, 1417 curconfig[ZoomLevel].val.f); 1418 1419 setatom(c, AtomFind, ""); 1420 setatom(c, AtomUri, "about:blank"); 1421 } 1422 1423 GtkWidget * 1424 createwindow(Client *c) 1425 { 1426 char *wmstr; 1427 GtkWidget *w; 1428 1429 if (embed) { 1430 w = gtk_plug_new(embed); 1431 } else { 1432 w = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1433 1434 wmstr = g_path_get_basename(argv0); 1435 gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf"); 1436 g_free(wmstr); 1437 1438 wmstr = g_strdup_printf("%s[%"PRIu64"]", "Surf", c->pageid); 1439 gtk_window_set_role(GTK_WINDOW(w), wmstr); 1440 g_free(wmstr); 1441 1442 gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]); 1443 } 1444 1445 g_signal_connect(G_OBJECT(w), "destroy", 1446 G_CALLBACK(destroywin), c); 1447 g_signal_connect(G_OBJECT(w), "enter-notify-event", 1448 G_CALLBACK(winevent), c); 1449 g_signal_connect(G_OBJECT(w), "key-press-event", 1450 G_CALLBACK(winevent), c); 1451 g_signal_connect(G_OBJECT(w), "leave-notify-event", 1452 G_CALLBACK(winevent), c); 1453 g_signal_connect(G_OBJECT(w), "window-state-event", 1454 G_CALLBACK(winevent), c); 1455 1456 return w; 1457 } 1458 1459 gboolean 1460 loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert, 1461 GTlsCertificateFlags err, Client *c) 1462 { 1463 GString *errmsg = g_string_new(NULL); 1464 gchar *html, *pem; 1465 1466 c->failedcert = g_object_ref(cert); 1467 c->tlserr = err; 1468 c->errorpage = 1; 1469 1470 if (err & G_TLS_CERTIFICATE_UNKNOWN_CA) 1471 g_string_append(errmsg, 1472 "The signing certificate authority is not known.<br>"); 1473 if (err & G_TLS_CERTIFICATE_BAD_IDENTITY) 1474 g_string_append(errmsg, 1475 "The certificate does not match the expected identity " 1476 "of the site that it was retrieved from.<br>"); 1477 if (err & G_TLS_CERTIFICATE_NOT_ACTIVATED) 1478 g_string_append(errmsg, 1479 "The certificate's activation time " 1480 "is still in the future.<br>"); 1481 if (err & G_TLS_CERTIFICATE_EXPIRED) 1482 g_string_append(errmsg, "The certificate has expired.<br>"); 1483 if (err & G_TLS_CERTIFICATE_REVOKED) 1484 g_string_append(errmsg, 1485 "The certificate has been revoked according to " 1486 "the GTlsConnection's certificate revocation list.<br>"); 1487 if (err & G_TLS_CERTIFICATE_INSECURE) 1488 g_string_append(errmsg, 1489 "The certificate's algorithm is considered insecure.<br>"); 1490 if (err & G_TLS_CERTIFICATE_GENERIC_ERROR) 1491 g_string_append(errmsg, 1492 "Some error occurred validating the certificate.<br>"); 1493 1494 g_object_get(cert, "certificate-pem", &pem, NULL); 1495 html = g_strdup_printf("<p>Could not validate TLS for “%s”<br>%s</p>" 1496 "<p>You can inspect the following certificate " 1497 "with Ctrl-t (default keybinding).</p>" 1498 "<p><pre>%s</pre></p>", uri, errmsg->str, pem); 1499 g_free(pem); 1500 g_string_free(errmsg, TRUE); 1501 1502 webkit_web_view_load_alternate_html(c->view, html, uri, NULL); 1503 g_free(html); 1504 1505 return TRUE; 1506 } 1507 1508 void 1509 loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c) 1510 { 1511 const char *uri = geturi(c); 1512 1513 switch (e) { 1514 case WEBKIT_LOAD_STARTED: 1515 setatom(c, AtomUri, uri); 1516 c->title = uri; 1517 c->https = c->insecure = 0; 1518 seturiparameters(c, uri, loadtransient); 1519 if (c->errorpage) 1520 c->errorpage = 0; 1521 else 1522 g_clear_object(&c->failedcert); 1523 break; 1524 case WEBKIT_LOAD_REDIRECTED: 1525 setatom(c, AtomUri, uri); 1526 c->title = uri; 1527 seturiparameters(c, uri, loadtransient); 1528 break; 1529 case WEBKIT_LOAD_COMMITTED: 1530 setatom(c, AtomUri, uri); 1531 c->title = uri; 1532 seturiparameters(c, uri, loadcommitted); 1533 c->https = webkit_web_view_get_tls_info(c->view, &c->cert, 1534 &c->tlserr); 1535 break; 1536 case WEBKIT_LOAD_FINISHED: 1537 seturiparameters(c, uri, loadfinished); 1538 /* Disabled until we write some WebKitWebExtension for 1539 * manipulating the DOM directly. 1540 evalscript(c, "document.documentElement.style.overflow = '%s'", 1541 enablescrollbars ? "auto" : "hidden"); 1542 */ 1543 runscript(c); 1544 break; 1545 } 1546 updatetitle(c); 1547 } 1548 1549 void 1550 progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c) 1551 { 1552 c->progress = webkit_web_view_get_estimated_load_progress(c->view) * 1553 100; 1554 updatetitle(c); 1555 } 1556 1557 void 1558 titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c) 1559 { 1560 c->title = webkit_web_view_get_title(c->view); 1561 updatetitle(c); 1562 } 1563 1564 void 1565 mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers, 1566 Client *c) 1567 { 1568 WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h); 1569 1570 /* Keep the hit test to know where is the pointer on the next click */ 1571 c->mousepos = h; 1572 1573 if (hc & OnLink) 1574 c->targeturi = webkit_hit_test_result_get_link_uri(h); 1575 else if (hc & OnImg) 1576 c->targeturi = webkit_hit_test_result_get_image_uri(h); 1577 else if (hc & OnMedia) 1578 c->targeturi = webkit_hit_test_result_get_media_uri(h); 1579 else 1580 c->targeturi = NULL; 1581 1582 c->overtitle = c->targeturi; 1583 updatetitle(c); 1584 } 1585 1586 gboolean 1587 permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c) 1588 { 1589 ParamName param = ParameterLast; 1590 1591 if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) { 1592 param = Geolocation; 1593 } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(r)) { 1594 if (webkit_user_media_permission_is_for_audio_device( 1595 WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r))) 1596 param = AccessMicrophone; 1597 else if (webkit_user_media_permission_is_for_video_device( 1598 WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r))) 1599 param = AccessWebcam; 1600 } else { 1601 return FALSE; 1602 } 1603 1604 if (curconfig[param].val.i) 1605 webkit_permission_request_allow(r); 1606 else 1607 webkit_permission_request_deny(r); 1608 1609 return TRUE; 1610 } 1611 1612 gboolean 1613 decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d, 1614 WebKitPolicyDecisionType dt, Client *c) 1615 { 1616 switch (dt) { 1617 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: 1618 decidenavigation(d, c); 1619 break; 1620 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION: 1621 decidenewwindow(d, c); 1622 break; 1623 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: 1624 decideresource(d, c); 1625 break; 1626 default: 1627 webkit_policy_decision_ignore(d); 1628 break; 1629 } 1630 return TRUE; 1631 } 1632 1633 void 1634 decidenavigation(WebKitPolicyDecision *d, Client *c) 1635 { 1636 WebKitNavigationAction *a = 1637 webkit_navigation_policy_decision_get_navigation_action( 1638 WEBKIT_NAVIGATION_POLICY_DECISION(d)); 1639 1640 switch (webkit_navigation_action_get_navigation_type(a)) { 1641 case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ 1642 case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ 1643 case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ 1644 case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ 1645 case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: /* fallthrough */ 1646 case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ 1647 default: 1648 /* Do not navigate to links with a "_blank" target (popup) */ 1649 if (webkit_navigation_policy_decision_get_frame_name( 1650 WEBKIT_NAVIGATION_POLICY_DECISION(d))) { 1651 webkit_policy_decision_ignore(d); 1652 } else { 1653 /* Filter out navigation to different domain ? */ 1654 /* get action→urirequest, copy and load in new window+view 1655 * on Ctrl+Click ? */ 1656 webkit_policy_decision_use(d); 1657 } 1658 break; 1659 } 1660 } 1661 1662 void 1663 decidenewwindow(WebKitPolicyDecision *d, Client *c) 1664 { 1665 Arg arg; 1666 WebKitNavigationAction *a = 1667 webkit_navigation_policy_decision_get_navigation_action( 1668 WEBKIT_NAVIGATION_POLICY_DECISION(d)); 1669 1670 1671 switch (webkit_navigation_action_get_navigation_type(a)) { 1672 case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */ 1673 case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */ 1674 case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */ 1675 case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */ 1676 case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: 1677 /* Filter domains here */ 1678 /* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event. 1679 * test for link clicked but no button ? */ 1680 arg.v = webkit_uri_request_get_uri( 1681 webkit_navigation_action_get_request(a)); 1682 newwindow(c, &arg, 0); 1683 break; 1684 case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ 1685 default: 1686 break; 1687 } 1688 1689 webkit_policy_decision_ignore(d); 1690 } 1691 1692 void 1693 decideresource(WebKitPolicyDecision *d, Client *c) 1694 { 1695 int i, isascii = 1; 1696 WebKitResponsePolicyDecision *r = WEBKIT_RESPONSE_POLICY_DECISION(d); 1697 WebKitURIResponse *res = 1698 webkit_response_policy_decision_get_response(r); 1699 const gchar *uri = webkit_uri_response_get_uri(res); 1700 1701 if (g_str_has_suffix(uri, "/favicon.ico")) { 1702 webkit_policy_decision_ignore(d); 1703 return; 1704 } 1705 1706 if (!g_str_has_prefix(uri, "http://") 1707 && !g_str_has_prefix(uri, "https://") 1708 && !g_str_has_prefix(uri, "about:") 1709 && !g_str_has_prefix(uri, "file://") 1710 && !g_str_has_prefix(uri, "data:") 1711 && !g_str_has_prefix(uri, "blob:") 1712 && strlen(uri) > 0) { 1713 for (i = 0; i < strlen(uri); i++) { 1714 if (!g_ascii_isprint(uri[i])) { 1715 isascii = 0; 1716 break; 1717 } 1718 } 1719 if (isascii) { 1720 handleplumb(c, uri); 1721 webkit_policy_decision_ignore(d); 1722 return; 1723 } 1724 } 1725 1726 if (webkit_response_policy_decision_is_mime_type_supported(r)) { 1727 webkit_policy_decision_use(d); 1728 } else { 1729 webkit_policy_decision_ignore(d); 1730 download(c, res); 1731 } 1732 } 1733 1734 void 1735 insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, Client *c) 1736 { 1737 c->insecure = 1; 1738 } 1739 1740 void 1741 downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c) 1742 { 1743 g_signal_connect(G_OBJECT(d), "notify::response", 1744 G_CALLBACK(responsereceived), c); 1745 } 1746 1747 void 1748 responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c) 1749 { 1750 download(c, webkit_download_get_response(d)); 1751 webkit_download_cancel(d); 1752 } 1753 1754 void 1755 download(Client *c, WebKitURIResponse *r) 1756 { 1757 Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c)); 1758 spawn(c, &a); 1759 } 1760 1761 void 1762 webprocessterminated(WebKitWebView *v, WebKitWebProcessTerminationReason r, 1763 Client *c) 1764 { 1765 fprintf(stderr, "web process terminated: %s\n", 1766 r == WEBKIT_WEB_PROCESS_CRASHED ? "crashed" : "no memory"); 1767 closeview(v, c); 1768 } 1769 1770 void 1771 closeview(WebKitWebView *v, Client *c) 1772 { 1773 gtk_widget_destroy(c->win); 1774 } 1775 1776 void 1777 destroywin(GtkWidget* w, Client *c) 1778 { 1779 destroyclient(c); 1780 if (!clients) 1781 gtk_main_quit(); 1782 } 1783 1784 void 1785 pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) 1786 { 1787 Arg a = {.v = text }; 1788 if (text) 1789 loaduri((Client *) d, &a); 1790 } 1791 1792 void 1793 reload(Client *c, const Arg *a) 1794 { 1795 if (a->i) 1796 webkit_web_view_reload_bypass_cache(c->view); 1797 else 1798 webkit_web_view_reload(c->view); 1799 } 1800 1801 void 1802 print(Client *c, const Arg *a) 1803 { 1804 webkit_print_operation_run_dialog(webkit_print_operation_new(c->view), 1805 GTK_WINDOW(c->win)); 1806 } 1807 1808 void 1809 showcert(Client *c, const Arg *a) 1810 { 1811 GTlsCertificate *cert = c->failedcert ? c->failedcert : c->cert; 1812 GcrCertificate *gcrt; 1813 GByteArray *crt; 1814 GtkWidget *win; 1815 GcrCertificateWidget *wcert; 1816 1817 if (!cert) 1818 return; 1819 1820 g_object_get(cert, "certificate", &crt, NULL); 1821 gcrt = gcr_simple_certificate_new(crt->data, crt->len); 1822 g_byte_array_unref(crt); 1823 1824 win = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1825 wcert = gcr_certificate_widget_new(gcrt); 1826 g_object_unref(gcrt); 1827 1828 gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(wcert)); 1829 gtk_widget_show_all(win); 1830 } 1831 1832 void 1833 clipboard(Client *c, const Arg *a) 1834 { 1835 if (a->i) { /* load clipboard uri */ 1836 gtk_clipboard_request_text(gtk_clipboard_get( 1837 GDK_SELECTION_PRIMARY), 1838 pasteuri, c); 1839 } else { /* copy uri */ 1840 gtk_clipboard_set_text(gtk_clipboard_get( 1841 GDK_SELECTION_PRIMARY), c->targeturi 1842 ? c->targeturi : geturi(c), -1); 1843 } 1844 } 1845 1846 void 1847 zoom(Client *c, const Arg *a) 1848 { 1849 if (a->i > 0) 1850 webkit_web_view_set_zoom_level(c->view, 1851 curconfig[ZoomLevel].val.f + 0.1); 1852 else if (a->i < 0) 1853 webkit_web_view_set_zoom_level(c->view, 1854 curconfig[ZoomLevel].val.f - 0.1); 1855 else 1856 webkit_web_view_set_zoom_level(c->view, 1.0); 1857 1858 curconfig[ZoomLevel].val.f = webkit_web_view_get_zoom_level(c->view); 1859 } 1860 1861 static void 1862 msgext(Client *c, char type, const Arg *a) 1863 { 1864 static char msg[MSGBUFSZ]; 1865 int ret; 1866 1867 if (spair[0] < 0) 1868 return; 1869 1870 if ((ret = snprintf(msg, sizeof(msg), "%c%c%c", c->pageid, type, a->i)) 1871 >= sizeof(msg)) { 1872 fprintf(stderr, "surf: message too long: %d\n", ret); 1873 return; 1874 } 1875 1876 if (send(spair[0], msg, ret, 0) != ret) 1877 fprintf(stderr, "surf: error sending: %u%c%d (%d)\n", 1878 c->pageid, type, a->i, ret); 1879 } 1880 1881 void 1882 scrollv(Client *c, const Arg *a) 1883 { 1884 msgext(c, 'v', a); 1885 } 1886 1887 void 1888 scrollh(Client *c, const Arg *a) 1889 { 1890 msgext(c, 'h', a); 1891 } 1892 1893 void 1894 navigate(Client *c, const Arg *a) 1895 { 1896 if (a->i < 0) 1897 webkit_web_view_go_back(c->view); 1898 else if (a->i > 0) 1899 webkit_web_view_go_forward(c->view); 1900 } 1901 1902 void 1903 stop(Client *c, const Arg *a) 1904 { 1905 webkit_web_view_stop_loading(c->view); 1906 } 1907 1908 void 1909 toggle(Client *c, const Arg *a) 1910 { 1911 curconfig[a->i].val.i ^= 1; 1912 setparameter(c, 1, (ParamName)a->i, &curconfig[a->i].val); 1913 } 1914 1915 void 1916 togglefullscreen(Client *c, const Arg *a) 1917 { 1918 /* toggling value is handled in winevent() */ 1919 if (c->fullscreen) 1920 gtk_window_unfullscreen(GTK_WINDOW(c->win)); 1921 else 1922 gtk_window_fullscreen(GTK_WINDOW(c->win)); 1923 } 1924 1925 void 1926 togglecookiepolicy(Client *c, const Arg *a) 1927 { 1928 ++cookiepolicy; 1929 cookiepolicy %= strlen(curconfig[CookiePolicies].val.v); 1930 1931 setparameter(c, 0, CookiePolicies, NULL); 1932 } 1933 1934 void 1935 toggleinspector(Client *c, const Arg *a) 1936 { 1937 if (webkit_web_inspector_is_attached(c->inspector)) 1938 webkit_web_inspector_close(c->inspector); 1939 else if (curconfig[Inspector].val.i) 1940 webkit_web_inspector_show(c->inspector); 1941 } 1942 1943 void 1944 find(Client *c, const Arg *a) 1945 { 1946 const char *s, *f; 1947 1948 if (a && a->i) { 1949 if (a->i > 0) 1950 webkit_find_controller_search_next(c->finder); 1951 else 1952 webkit_find_controller_search_previous(c->finder); 1953 } else { 1954 s = getatom(c, AtomFind); 1955 f = webkit_find_controller_get_search_text(c->finder); 1956 1957 if (g_strcmp0(f, s) == 0) /* reset search */ 1958 webkit_find_controller_search(c->finder, "", findopts, 1959 G_MAXUINT); 1960 1961 webkit_find_controller_search(c->finder, s, findopts, 1962 G_MAXUINT); 1963 1964 if (strcmp(s, "") == 0) 1965 webkit_find_controller_search_finish(c->finder); 1966 } 1967 } 1968 1969 void 1970 clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h) 1971 { 1972 navigate(c, a); 1973 } 1974 1975 void 1976 clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h) 1977 { 1978 Arg arg; 1979 1980 arg.v = webkit_hit_test_result_get_link_uri(h); 1981 newwindow(c, &arg, a->i); 1982 } 1983 1984 void 1985 clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h) 1986 { 1987 Arg arg; 1988 1989 arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h)); 1990 spawn(c, &arg); 1991 } 1992 1993 int 1994 main(int argc, char *argv[]) 1995 { 1996 Arg arg; 1997 Client *c; 1998 1999 #ifdef __disabled_OpenBSD__ 2000 char path[128]; 2001 const char* home = getcurrentuserhomedir(); 2002 2003 /* in new X session: surf doesn't start until another X program has run */ 2004 if (snprintf(path, sizeof(path), "%s/.cache", home) < 0) 2005 err(1, "snprintf"); 2006 if (unveil(path, "rwc") == -1) 2007 err(1, "unveil"); 2008 2009 if (snprintf(path, sizeof(path), "%s/.config", home) < 0) 2010 err(1, "snprintf"); 2011 if (unveil(path, "r") == -1) 2012 err(1, "unveil"); 2013 2014 if (snprintf(path, sizeof(path), "%s/.config/surf", home) < 0) 2015 err(1, "snprintf"); 2016 if (unveil(path, "rwxc") == -1) 2017 err(1, "unveil"); 2018 2019 if (snprintf(path, sizeof(path), "%s/.icons", home) < 0) 2020 err(1, "snprintf"); 2021 if (unveil(path, "r") == -1) 2022 err(1, "unveil"); 2023 2024 if (snprintf(path, sizeof(path), "%s/.local", home) < 0) 2025 err(1, "snprintf"); 2026 if (unveil(path, "rwc") == -1) 2027 err(1, "unveil"); 2028 2029 if (snprintf(path, sizeof(path), "%s/.Xauthority", home) < 0) 2030 err(1, "snprintf"); 2031 if (unveil(path, "r") == -1) 2032 err(1, "unveil"); 2033 2034 if (snprintf(path, sizeof(path), "%s/.Xdefaults", home) < 0) 2035 err(1, "snprintf"); 2036 if (unveil(path, "r") == -1) 2037 err(1, "unveil"); 2038 2039 if (snprintf(path, sizeof(path), "%s/tmp", home) < 0) 2040 err(1, "snprintf"); 2041 if (unveil(path, "rwc") == -1) 2042 err(1, "unveil"); 2043 2044 if (unveil("/bin", "rx") == -1) 2045 err(1, "unveil"); 2046 2047 if (unveil("/dev/urandom", "r") == -1) 2048 err(1, "unveil"); 2049 2050 if (unveil("/etc/fonts", "r") == -1) 2051 err(1, "unveil"); 2052 2053 if (unveil("/etc/gtk-3.0", "r") == -1) 2054 err(1, "unveil"); 2055 2056 if (unveil("/etc/xdg", "r") == -1) 2057 err(1, "unveil"); 2058 2059 if (unveil("/etc/aspell.conf", "r") == -1) 2060 err(1, "unveil"); 2061 2062 if (unveil("/etc/localtime", "r") == -1) 2063 err(1, "unveil"); 2064 2065 if (unveil("/etc/machine-id", "r") == -1) 2066 err(1, "unveil"); 2067 2068 if (unveil("/tmp", "rwc") == -1) 2069 err(1, "unveil /tmp"); 2070 2071 if (unveil("/proc", "rw") == -1) 2072 err(1, "unveil"); 2073 2074 if (unveil("/usr/libexec", "r") == -1) 2075 err(1, "unveil"); 2076 2077 if (unveil("/usr/local/bin", "rx") == -1) 2078 err(1, "unveil"); 2079 2080 if (unveil("/usr/local/lib", "rx") == -1) 2081 err(1, "unveil"); 2082 2083 if (unveil("/usr/local/libexec/webkit2gtk-4.0", "x") == -1) 2084 err(1, "unveil /usr/local/libexec/webkit2gtk-4.0"); 2085 2086 if (unveil("/usr/local/lib/gdk-pixbuf-2.0", "x") == -1) 2087 err(1, "unveil /usr/local/libexec/gdk-pixbuf-2.0"); 2088 2089 if (unveil("/usr/local/share", "r") == -1) 2090 err(1, "unveil"); 2091 2092 if (unveil("/usr/local/share/locale", "r") == -1) 2093 err(1, "unveil"); 2094 2095 if (unveil("/usr/share/locale", "r") == -1) 2096 err(1, "unveil"); 2097 2098 if (unveil("/usr/X11R6/lib", "rx") == -1) 2099 err(1, "unveil"); 2100 2101 if (unveil("/var", "rw") == -1) 2102 err(1, "unveil"); 2103 2104 if (pledge("stdio rpath wpath cpath dpath tmppath fattr chown flock unix " 2105 "sendfd recvfd tty proc exec prot_exec ps", NULL) == -1) 2106 err(1, "pledge"); 2107 #endif 2108 2109 memset(&arg, 0, sizeof(arg)); 2110 2111 /* command line args */ 2112 ARGBEGIN { 2113 case 'a': 2114 defconfig[CookiePolicies].val.v = EARGF(usage()); 2115 defconfig[CookiePolicies].prio = 2; 2116 break; 2117 case 'b': 2118 defconfig[ScrollBars].val.i = 0; 2119 defconfig[ScrollBars].prio = 2; 2120 break; 2121 case 'B': 2122 defconfig[ScrollBars].val.i = 1; 2123 defconfig[ScrollBars].prio = 2; 2124 break; 2125 case 'c': 2126 cookiefile = EARGF(usage()); 2127 break; 2128 case 'C': 2129 stylefile = EARGF(usage()); 2130 break; 2131 case 'd': 2132 defconfig[DiskCache].val.i = 0; 2133 defconfig[DiskCache].prio = 2; 2134 break; 2135 case 'D': 2136 defconfig[DiskCache].val.i = 1; 2137 defconfig[DiskCache].prio = 2; 2138 break; 2139 case 'e': 2140 embed = strtol(EARGF(usage()), NULL, 0); 2141 break; 2142 case 'f': 2143 defconfig[RunInFullscreen].val.i = 0; 2144 defconfig[RunInFullscreen].prio = 2; 2145 break; 2146 case 'F': 2147 defconfig[RunInFullscreen].val.i = 1; 2148 defconfig[RunInFullscreen].prio = 2; 2149 break; 2150 case 'g': 2151 defconfig[Geolocation].val.i = 0; 2152 defconfig[Geolocation].prio = 2; 2153 break; 2154 case 'G': 2155 defconfig[Geolocation].val.i = 1; 2156 defconfig[Geolocation].prio = 2; 2157 break; 2158 case 'i': 2159 defconfig[LoadImages].val.i = 0; 2160 defconfig[LoadImages].prio = 2; 2161 break; 2162 case 'I': 2163 defconfig[LoadImages].val.i = 1; 2164 defconfig[LoadImages].prio = 2; 2165 break; 2166 case 'k': 2167 defconfig[KioskMode].val.i = 0; 2168 defconfig[KioskMode].prio = 2; 2169 break; 2170 case 'K': 2171 defconfig[KioskMode].val.i = 1; 2172 defconfig[KioskMode].prio = 2; 2173 break; 2174 case 'm': 2175 defconfig[Style].val.i = 0; 2176 defconfig[Style].prio = 2; 2177 break; 2178 case 'M': 2179 defconfig[Style].val.i = 1; 2180 defconfig[Style].prio = 2; 2181 break; 2182 case 'n': 2183 defconfig[Inspector].val.i = 0; 2184 defconfig[Inspector].prio = 2; 2185 break; 2186 case 'N': 2187 defconfig[Inspector].val.i = 1; 2188 defconfig[Inspector].prio = 2; 2189 break; 2190 case 'r': 2191 scriptfile = EARGF(usage()); 2192 break; 2193 case 's': 2194 defconfig[JavaScript].val.i = 0; 2195 defconfig[JavaScript].prio = 2; 2196 break; 2197 case 'S': 2198 defconfig[JavaScript].val.i = 1; 2199 defconfig[JavaScript].prio = 2; 2200 break; 2201 case 't': 2202 defconfig[StrictTLS].val.i = 0; 2203 defconfig[StrictTLS].prio = 2; 2204 break; 2205 case 'T': 2206 defconfig[StrictTLS].val.i = 1; 2207 defconfig[StrictTLS].prio = 2; 2208 break; 2209 case 'u': 2210 fulluseragent = EARGF(usage()); 2211 break; 2212 case 'v': 2213 die("surf-"VERSION", see LICENSE for © details\n"); 2214 case 'w': 2215 showxid = 1; 2216 break; 2217 case 'x': 2218 defconfig[Certificate].val.i = 0; 2219 defconfig[Certificate].prio = 2; 2220 break; 2221 case 'X': 2222 defconfig[Certificate].val.i = 1; 2223 defconfig[Certificate].prio = 2; 2224 break; 2225 case 'z': 2226 defconfig[ZoomLevel].val.f = strtof(EARGF(usage()), NULL); 2227 defconfig[ZoomLevel].prio = 2; 2228 break; 2229 default: 2230 usage(); 2231 } ARGEND; 2232 if (argc > 0) 2233 arg.v = argv[0]; 2234 else 2235 arg.v = "about:blank"; 2236 2237 setup(); 2238 c = newclient(NULL); 2239 showview(NULL, c); 2240 2241 loaduri(c, &arg); 2242 updatetitle(c); 2243 2244 gtk_main(); 2245 cleanup(); 2246 2247 return 0; 2248 }