3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * multi letter commands
25 * pre and post counts for commands
26 * autocompletion on various inputs
27 * create privacy browsing
28 * - encrypted local data
44 #include <sys/types.h>
46 #if defined(__linux__)
47 #include "linux/util.h"
48 #include "linux/tree.h"
49 #elif defined(__FreeBSD__)
51 #include "freebsd/util.h"
57 #include <sys/queue.h>
59 #include <sys/socket.h>
63 #include <gdk/gdkkeysyms.h>
64 #include <webkit/webkit.h>
65 #include <libsoup/soup.h>
66 #include <gnutls/gnutls.h>
67 #include <JavaScriptCore/JavaScript.h>
68 #include <gnutls/x509.h>
70 #include "javascript.h"
73 javascript.h borrowed from vimprobable2 under the following license:
75 Copyright (c) 2009 Leon Winter
76 Copyright (c) 2009 Hannes Schueller
77 Copyright (c) 2009 Matto Fransen
79 Permission is hereby granted, free of charge, to any person obtaining a copy
80 of this software and associated documentation files (the "Software"), to deal
81 in the Software without restriction, including without limitation the rights
82 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
83 copies of the Software, and to permit persons to whom the Software is
84 furnished to do so, subject to the following conditions:
86 The above copyright notice and this permission notice shall be included in
87 all copies or substantial portions of the Software.
89 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
90 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
91 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
92 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
93 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
94 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
98 static char *version
= "$xxxterm$";
100 /* hooked functions */
101 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
102 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
107 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
108 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
109 #define XT_D_MOVE 0x0001
110 #define XT_D_KEY 0x0002
111 #define XT_D_TAB 0x0004
112 #define XT_D_URL 0x0008
113 #define XT_D_CMD 0x0010
114 #define XT_D_NAV 0x0020
115 #define XT_D_DOWNLOAD 0x0040
116 #define XT_D_CONFIG 0x0080
117 #define XT_D_JS 0x0100
118 #define XT_D_FAVORITE 0x0200
119 #define XT_D_PRINTING 0x0400
120 #define XT_D_COOKIE 0x0800
121 #define XT_D_KEYBINDING 0x1000
122 u_int32_t swm_debug
= 0
138 #define DPRINTF(x...)
139 #define DNPRINTF(n,x...)
142 #define LENGTH(x) (sizeof x / sizeof x[0])
143 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
144 ~(GDK_BUTTON1_MASK) & \
145 ~(GDK_BUTTON2_MASK) & \
146 ~(GDK_BUTTON3_MASK) & \
147 ~(GDK_BUTTON4_MASK) & \
159 TAILQ_ENTRY(tab
) entry
;
161 GtkWidget
*tab_content
;
164 GtkWidget
*uri_entry
;
165 GtkWidget
*search_entry
;
167 GtkWidget
*browser_win
;
168 GtkWidget
*statusbar
;
175 GtkWidget
*js_toggle
;
176 GtkEntryCompletion
*completion
;
180 WebKitWebHistoryItem
*item
;
181 WebKitWebBackForwardList
*bfl
;
184 WebKitNetworkRequest
*icon_request
;
185 WebKitDownload
*icon_download
;
186 GdkPixbuf
*icon_pixbuf
;
187 gchar
*icon_dest_uri
;
189 /* adjustments for browser */
192 GtkAdjustment
*adjust_h
;
193 GtkAdjustment
*adjust_v
;
199 int xtp_meaning
; /* identifies dls/favorites */
204 #define XT_HINT_NONE (0)
205 #define XT_HINT_NUMERICAL (1)
206 #define XT_HINT_ALPHANUM (2)
210 /* custom stylesheet */
219 WebKitWebSettings
*settings
;
223 TAILQ_HEAD(tab_list
, tab
);
226 RB_ENTRY(history
) entry
;
230 RB_HEAD(history_list
, history
);
233 RB_ENTRY(download
) entry
;
235 WebKitDownload
*download
;
238 RB_HEAD(download_list
, download
);
241 RB_ENTRY(domain
) entry
;
243 int handy
; /* app use */
245 RB_HEAD(domain_list
, domain
);
248 TAILQ_ENTRY(undo
) entry
;
251 int back
; /* Keeps track of how many back
252 * history items there are. */
254 TAILQ_HEAD(undo_tailq
, undo
);
256 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
257 int next_download_id
= 1;
266 #define XT_NAME ("XXXTerm")
267 #define XT_DIR (".xxxterm")
268 #define XT_CACHE_DIR ("cache")
269 #define XT_CERT_DIR ("certs/")
270 #define XT_SESSIONS_DIR ("sessions/")
271 #define XT_CONF_FILE ("xxxterm.conf")
272 #define XT_FAVS_FILE ("favorites")
273 #define XT_SAVED_TABS_FILE ("main_session")
274 #define XT_RESTART_TABS_FILE ("restart_tabs")
275 #define XT_SOCKET_FILE ("socket")
276 #define XT_HISTORY_FILE ("history")
277 #define XT_REJECT_FILE ("rejected.txt")
278 #define XT_COOKIE_FILE ("cookies.txt")
279 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
280 #define XT_CB_HANDLED (TRUE)
281 #define XT_CB_PASSTHROUGH (FALSE)
282 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
283 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
284 #define XT_DLMAN_REFRESH "10"
285 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
286 "td {overflow: hidden;" \
287 " padding: 2px 2px 2px 2px;" \
288 " border: 1px solid black}\n" \
289 "tr:hover {background: #ffff99 ;}\n" \
290 "th {background-color: #cccccc;" \
291 " border: 1px solid black}" \
292 "table {border-spacing: 0; " \
294 " border: 1px black solid;}\n" \
296 " border: 1px solid black;" \
302 " background: green;}" \
304 " font-size: small;" \
305 " text-align: center;}" \
307 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
308 #define XT_MAX_UNDO_CLOSE_TAB (32)
309 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
310 #define XT_PRINT_EXTRA_MARGIN 10
313 #define XT_COLOR_RED "#cc0000"
314 #define XT_COLOR_YELLOW "#ffff66"
315 #define XT_COLOR_BLUE "lightblue"
316 #define XT_COLOR_GREEN "#99ff66"
317 #define XT_COLOR_WHITE "white"
318 #define XT_COLOR_BLACK "black"
321 * xxxterm "protocol" (xtp)
322 * We use this for managing stuff like downloads and favorites. They
323 * make magical HTML pages in memory which have xxxt:// links in order
324 * to communicate with xxxterm's internals. These links take the format:
325 * xxxt://class/session_key/action/arg
327 * Don't begin xtp class/actions as 0. atoi returns that on error.
329 * Typically we have not put addition of items in this framework, as
330 * adding items is either done via an ex-command or via a keybinding instead.
333 #define XT_XTP_STR "xxxt://"
335 /* XTP classes (xxxt://<class>) */
336 #define XT_XTP_INVALID 0 /* invalid */
337 #define XT_XTP_DL 1 /* downloads */
338 #define XT_XTP_HL 2 /* history */
339 #define XT_XTP_CL 3 /* cookies */
340 #define XT_XTP_FL 4 /* favorites */
342 /* XTP download actions */
343 #define XT_XTP_DL_LIST 1
344 #define XT_XTP_DL_CANCEL 2
345 #define XT_XTP_DL_REMOVE 3
347 /* XTP history actions */
348 #define XT_XTP_HL_LIST 1
349 #define XT_XTP_HL_REMOVE 2
351 /* XTP cookie actions */
352 #define XT_XTP_CL_LIST 1
353 #define XT_XTP_CL_REMOVE 2
355 /* XTP cookie actions */
356 #define XT_XTP_FL_LIST 1
357 #define XT_XTP_FL_REMOVE 2
359 /* xtp tab meanings - identifies which tabs have xtp pages in */
360 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
361 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
362 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
363 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
364 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
367 #define XT_MOVE_INVALID (0)
368 #define XT_MOVE_DOWN (1)
369 #define XT_MOVE_UP (2)
370 #define XT_MOVE_BOTTOM (3)
371 #define XT_MOVE_TOP (4)
372 #define XT_MOVE_PAGEDOWN (5)
373 #define XT_MOVE_PAGEUP (6)
374 #define XT_MOVE_HALFDOWN (7)
375 #define XT_MOVE_HALFUP (8)
376 #define XT_MOVE_LEFT (9)
377 #define XT_MOVE_FARLEFT (10)
378 #define XT_MOVE_RIGHT (11)
379 #define XT_MOVE_FARRIGHT (12)
381 #define XT_TAB_LAST (-4)
382 #define XT_TAB_FIRST (-3)
383 #define XT_TAB_PREV (-2)
384 #define XT_TAB_NEXT (-1)
385 #define XT_TAB_INVALID (0)
386 #define XT_TAB_NEW (1)
387 #define XT_TAB_DELETE (2)
388 #define XT_TAB_DELQUIT (3)
389 #define XT_TAB_OPEN (4)
390 #define XT_TAB_UNDO_CLOSE (5)
391 #define XT_TAB_SHOW (6)
392 #define XT_TAB_HIDE (7)
394 #define XT_NAV_INVALID (0)
395 #define XT_NAV_BACK (1)
396 #define XT_NAV_FORWARD (2)
397 #define XT_NAV_RELOAD (3)
398 #define XT_NAV_RELOAD_CACHE (4)
400 #define XT_FOCUS_INVALID (0)
401 #define XT_FOCUS_URI (1)
402 #define XT_FOCUS_SEARCH (2)
404 #define XT_SEARCH_INVALID (0)
405 #define XT_SEARCH_NEXT (1)
406 #define XT_SEARCH_PREV (2)
408 #define XT_PASTE_CURRENT_TAB (0)
409 #define XT_PASTE_NEW_TAB (1)
411 #define XT_FONT_SET (0)
413 #define XT_URL_SHOW (1)
414 #define XT_URL_HIDE (2)
416 #define XT_STATUSBAR_SHOW (1)
417 #define XT_STATUSBAR_HIDE (2)
419 #define XT_WL_TOGGLE (1<<0)
420 #define XT_WL_ENABLE (1<<1)
421 #define XT_WL_DISABLE (1<<2)
422 #define XT_WL_FQDN (1<<3) /* default */
423 #define XT_WL_TOPLEVEL (1<<4)
424 #define XT_WL_PERSISTENT (1<<5)
425 #define XT_WL_SESSION (1<<6)
427 #define XT_SHOW (1<<7)
428 #define XT_DELETE (1<<8)
429 #define XT_SAVE (1<<9)
430 #define XT_OPEN (1<<10)
432 #define XT_CMD_OPEN (0)
433 #define XT_CMD_OPEN_CURRENT (1)
434 #define XT_CMD_TABNEW (2)
435 #define XT_CMD_TABNEW_CURRENT (3)
437 #define XT_STATUS_NOTHING (0)
438 #define XT_STATUS_LINK (1)
439 #define XT_STATUS_URI (2)
440 #define XT_STATUS_LOADING (3)
442 #define XT_SES_DONOTHING (0)
443 #define XT_SES_CLOSETABS (1)
445 #define XT_BM_NORMAL (0)
446 #define XT_BM_WHITELIST (1)
447 #define XT_BM_KIOSK (2)
449 #define XT_PREFIX (1<<0)
450 #define XT_USERARG (1<<1)
451 #define XT_URLARG (1<<2)
459 TAILQ_ENTRY(mime_type
) entry
;
461 TAILQ_HEAD(mime_type_list
, mime_type
);
467 TAILQ_ENTRY(alias
) entry
;
469 TAILQ_HEAD(alias_list
, alias
);
471 /* settings that require restart */
472 int tabless
= 0; /* allow only 1 tab */
473 int enable_socket
= 0;
474 int single_instance
= 0; /* only allow one xxxterm to run */
475 int fancy_bar
= 1; /* fancy toolbar */
476 int browser_mode
= XT_BM_NORMAL
;
478 /* runtime settings */
479 int show_tabs
= 1; /* show tabs on notebook */
480 int show_url
= 1; /* show url toolbar on notebook */
481 int show_statusbar
= 0; /* vimperator style status bar */
482 int ctrl_click_focus
= 0; /* ctrl click gets focus */
483 int cookies_enabled
= 1; /* enable cookies */
484 int read_only_cookies
= 0; /* enable to not write cookies */
485 int enable_scripts
= 1;
486 int enable_plugins
= 0;
487 int default_font_size
= 12;
488 gfloat default_zoom_level
= 1.0;
489 int window_height
= 768;
490 int window_width
= 1024;
491 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
492 unsigned refresh_interval
= 10; /* download refresh interval */
493 int enable_cookie_whitelist
= 0;
494 int enable_js_whitelist
= 0;
495 time_t session_timeout
= 3600; /* cookie session timeout */
496 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
497 char *ssl_ca_file
= NULL
;
498 char *resource_dir
= NULL
;
499 gboolean ssl_strict_certs
= FALSE
;
500 int append_next
= 1; /* append tab after current tab */
502 char *search_string
= NULL
;
503 char *http_proxy
= NULL
;
504 char download_dir
[PATH_MAX
];
505 char runtime_settings
[PATH_MAX
]; /* override of settings */
506 int allow_volatile_cookies
= 0;
507 int save_global_history
= 0; /* save global history to disk */
508 char *user_agent
= NULL
;
509 int save_rejected_cookies
= 0;
510 time_t session_autosave
= 0;
511 int guess_search
= 0;
512 int dns_prefetch
= FALSE
;
513 gint max_connections
= 25;
514 gint max_host_connections
= 5;
518 int set_download_dir(struct settings
*, char *);
519 int set_work_dir(struct settings
*, char *);
520 int set_runtime_dir(struct settings
*, char *);
521 int set_browser_mode(struct settings
*, char *);
522 int set_cookie_policy(struct settings
*, char *);
523 int add_alias(struct settings
*, char *);
524 int add_mime_type(struct settings
*, char *);
525 int add_cookie_wl(struct settings
*, char *);
526 int add_js_wl(struct settings
*, char *);
527 int add_kb(struct settings
*, char *);
528 void button_set_stockid(GtkWidget
*, char *);
529 GtkWidget
* create_button(char *, char *, int);
531 char *get_browser_mode(struct settings
*);
532 char *get_cookie_policy(struct settings
*);
534 char *get_download_dir(struct settings
*);
535 char *get_work_dir(struct settings
*);
536 char *get_runtime_dir(struct settings
*);
538 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
539 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
540 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
541 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
542 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
545 int (*set
)(struct settings
*, char *);
546 char *(*get
)(struct settings
*);
547 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
550 struct special s_browser_mode
= {
556 struct special s_cookie
= {
562 struct special s_alias
= {
568 struct special s_mime
= {
574 struct special s_js
= {
580 struct special s_kb
= {
586 struct special s_cookie_wl
= {
592 struct special s_download_dir
= {
598 struct special s_work_dir
= {
607 #define XT_S_INVALID (0)
610 #define XT_S_FLOAT (3)
612 #define XT_SF_RESTART (1<<0)
613 #define XT_SF_RUNTIME (1<<1)
619 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
620 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
621 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
622 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
623 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
624 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
625 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
626 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
627 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
628 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
629 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
630 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
631 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
632 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
633 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
634 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
635 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
636 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
637 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
638 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
639 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
640 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
641 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
642 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
643 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
644 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
645 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
646 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
647 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
648 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
649 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
650 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
651 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
652 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
653 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
654 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
655 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
656 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
657 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
659 /* runtime settings */
660 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
661 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
662 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
663 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
664 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
667 int about(struct tab
*, struct karg
*);
668 int blank(struct tab
*, struct karg
*);
669 int cookie_show_wl(struct tab
*, struct karg
*);
670 int js_show_wl(struct tab
*, struct karg
*);
671 int help(struct tab
*, struct karg
*);
672 int set(struct tab
*, struct karg
*);
673 int stats(struct tab
*, struct karg
*);
674 int marco(struct tab
*, struct karg
*);
675 const char * marco_message(int *);
676 int xtp_page_cl(struct tab
*, struct karg
*);
677 int xtp_page_dl(struct tab
*, struct karg
*);
678 int xtp_page_fl(struct tab
*, struct karg
*);
679 int xtp_page_hl(struct tab
*, struct karg
*);
681 #define XT_URI_ABOUT ("about:")
682 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
683 #define XT_URI_ABOUT_ABOUT ("about")
684 #define XT_URI_ABOUT_BLANK ("blank")
685 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
686 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
687 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
688 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
689 #define XT_URI_ABOUT_FAVORITES ("favorites")
690 #define XT_URI_ABOUT_HELP ("help")
691 #define XT_URI_ABOUT_HISTORY ("history")
692 #define XT_URI_ABOUT_JSWL ("jswl")
693 #define XT_URI_ABOUT_SET ("set")
694 #define XT_URI_ABOUT_STATS ("stats")
695 #define XT_URI_ABOUT_MARCO ("marco")
699 int (*func
)(struct tab
*, struct karg
*);
701 { XT_URI_ABOUT_ABOUT
, about
},
702 { XT_URI_ABOUT_BLANK
, blank
},
703 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
704 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
705 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
706 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
707 { XT_URI_ABOUT_HELP
, help
},
708 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
709 { XT_URI_ABOUT_JSWL
, js_show_wl
},
710 { XT_URI_ABOUT_SET
, set
},
711 { XT_URI_ABOUT_STATS
, stats
},
712 { XT_URI_ABOUT_MARCO
, marco
},
716 extern char *__progname
;
719 GtkWidget
*main_window
;
720 GtkNotebook
*notebook
;
721 GtkWidget
*arrow
, *abtn
;
722 struct tab_list tabs
;
723 struct history_list hl
;
724 struct download_list downloads
;
725 struct domain_list c_wl
;
726 struct domain_list js_wl
;
727 struct undo_tailq undos
;
728 struct keybinding_list kbl
;
730 int updating_dl_tabs
= 0;
731 int updating_hl_tabs
= 0;
732 int updating_cl_tabs
= 0;
733 int updating_fl_tabs
= 0;
735 uint64_t blocked_cookies
= 0;
736 char named_session
[PATH_MAX
];
737 void update_favicon(struct tab
*);
738 int icon_size_map(int);
740 GtkListStore
*completion_model
;
741 void completion_add(struct tab
*);
742 void completion_add_uri(const gchar
*);
747 int saved_errno
, status
;
752 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
756 if (errno
!= ECHILD
) {
758 clog_warn("sigchild: waitpid:");
764 if (WIFEXITED(status
)) {
765 if (WEXITSTATUS(status
) != 0) {
767 clog_warnx("sigchild: child exit status: %d",
768 WEXITSTATUS(status));
773 clog_warnx("sigchild: child is terminated abnormally");
782 is_g_object_setting(GObject
*o
, char *str
)
784 guint n_props
= 0, i
;
785 GParamSpec
**proplist
;
787 if (! G_IS_OBJECT(o
))
790 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
793 for (i
=0; i
< n_props
; i
++) {
794 if (! strcmp(proplist
[i
]->name
, str
))
801 * Display a web page from a HTML string in memory, rather than from a URL
804 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
810 /* we set this to indicate we want to manually do navaction */
812 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
814 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "");
815 #if GTK_CHECK_VERSION(2, 20, 0)
816 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
817 gtk_widget_hide(t
->spinner
);
821 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, title
);
822 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
825 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
826 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
827 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
828 GTK_ENTRY_ICON_PRIMARY
, pb
);
829 gdk_pixbuf_unref(pb
);
834 set_status(struct tab
*t
, gchar
*s
, int status
)
842 case XT_STATUS_LOADING
:
843 type
= g_strdup_printf("Loading: %s", s
);
847 type
= g_strdup_printf("Link: %s", s
);
849 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
853 type
= g_strdup_printf("%s", s
);
855 t
->status
= g_strdup(type
);
859 t
->status
= g_strdup(s
);
861 case XT_STATUS_NOTHING
:
866 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
872 hide_oops(struct tab
*t
)
874 gtk_widget_hide(t
->oops
);
878 hide_cmd(struct tab
*t
)
880 gtk_widget_hide(t
->cmd
);
884 show_cmd(struct tab
*t
)
886 gtk_widget_hide(t
->oops
);
887 gtk_widget_show(t
->cmd
);
891 show_oops(struct tab
*t
, const char *fmt
, ...)
900 if (vasprintf(&msg
, fmt
, ap
) == -1)
901 errx(1, "show_oops failed");
904 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
905 gtk_widget_hide(t
->cmd
);
906 gtk_widget_show(t
->oops
);
909 /* XXX collapse with show_oops */
911 show_oops_s(const char *fmt
, ...)
915 struct tab
*ti
, *t
= NULL
;
920 TAILQ_FOREACH(ti
, &tabs
, entry
)
921 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
929 if (vasprintf(&msg
, fmt
, ap
) == -1)
930 errx(1, "show_oops_s failed");
933 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
934 gtk_widget_hide(t
->cmd
);
935 gtk_widget_show(t
->oops
);
939 get_as_string(struct settings
*s
)
950 warnx("get_as_string skip %s\n", s
->name
);
951 } else if (s
->type
== XT_S_INT
)
952 r
= g_strdup_printf("%d", *s
->ival
);
953 else if (s
->type
== XT_S_STR
)
954 r
= g_strdup(*s
->sval
);
955 else if (s
->type
== XT_S_FLOAT
)
956 r
= g_strdup_printf("%f", *s
->fval
);
958 r
= g_strdup_printf("INVALID TYPE");
964 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
969 for (i
= 0; i
< LENGTH(rs
); i
++) {
970 if (rs
[i
].s
&& rs
[i
].s
->walk
)
971 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
973 s
= get_as_string(&rs
[i
]);
974 cb(&rs
[i
], s
, cb_args
);
981 set_browser_mode(struct settings
*s
, char *val
)
983 if (!strcmp(val
, "whitelist")) {
984 browser_mode
= XT_BM_WHITELIST
;
985 allow_volatile_cookies
= 0;
986 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
988 enable_cookie_whitelist
= 1;
989 read_only_cookies
= 0;
990 save_rejected_cookies
= 0;
991 session_timeout
= 3600;
993 enable_js_whitelist
= 1;
994 } else if (!strcmp(val
, "normal")) {
995 browser_mode
= XT_BM_NORMAL
;
996 allow_volatile_cookies
= 0;
997 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
999 enable_cookie_whitelist
= 0;
1000 read_only_cookies
= 0;
1001 save_rejected_cookies
= 0;
1002 session_timeout
= 3600;
1004 enable_js_whitelist
= 0;
1005 } else if (!strcmp(val
, "kiosk")) {
1006 browser_mode
= XT_BM_KIOSK
;
1007 allow_volatile_cookies
= 0;
1008 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1009 cookies_enabled
= 1;
1010 enable_cookie_whitelist
= 0;
1011 read_only_cookies
= 0;
1012 save_rejected_cookies
= 0;
1013 session_timeout
= 3600;
1015 enable_js_whitelist
= 0;
1025 get_browser_mode(struct settings
*s
)
1029 if (browser_mode
== XT_BM_WHITELIST
)
1030 r
= g_strdup("whitelist");
1031 else if (browser_mode
== XT_BM_NORMAL
)
1032 r
= g_strdup("normal");
1033 else if (browser_mode
== XT_BM_KIOSK
)
1034 r
= g_strdup("kiosk");
1042 set_cookie_policy(struct settings
*s
, char *val
)
1044 if (!strcmp(val
, "no3rdparty"))
1045 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1046 else if (!strcmp(val
, "accept"))
1047 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1048 else if (!strcmp(val
, "reject"))
1049 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1057 get_cookie_policy(struct settings
*s
)
1061 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1062 r
= g_strdup("no3rdparty");
1063 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1064 r
= g_strdup("accept");
1065 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1066 r
= g_strdup("reject");
1074 get_download_dir(struct settings
*s
)
1076 if (download_dir
[0] == '\0')
1078 return (g_strdup(download_dir
));
1082 set_download_dir(struct settings
*s
, char *val
)
1085 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1086 pwd
->pw_dir
, &val
[1]);
1088 strlcpy(download_dir
, val
, sizeof download_dir
);
1095 * We use these to prevent people putting xxxt:// URLs on
1096 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1098 #define XT_XTP_SES_KEY_SZ 8
1099 #define XT_XTP_SES_KEY_HEX_FMT \
1100 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1101 char *dl_session_key
; /* downloads */
1102 char *hl_session_key
; /* history list */
1103 char *cl_session_key
; /* cookie list */
1104 char *fl_session_key
; /* favorites list */
1106 char work_dir
[PATH_MAX
];
1107 char certs_dir
[PATH_MAX
];
1108 char cache_dir
[PATH_MAX
];
1109 char sessions_dir
[PATH_MAX
];
1110 char cookie_file
[PATH_MAX
];
1111 SoupURI
*proxy_uri
= NULL
;
1112 SoupSession
*session
;
1113 SoupCookieJar
*s_cookiejar
;
1114 SoupCookieJar
*p_cookiejar
;
1115 char rc_fname
[PATH_MAX
];
1117 struct mime_type_list mtl
;
1118 struct alias_list aliases
;
1121 struct tab
*create_new_tab(char *, struct undo
*, int);
1122 void delete_tab(struct tab
*);
1123 void adjustfont_webkit(struct tab
*, int);
1124 int run_script(struct tab
*, char *);
1125 int download_rb_cmp(struct download
*, struct download
*);
1126 gboolean
cmd_execute(struct tab
*t
, char *str
);
1129 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1131 return (strcmp(h1
->uri
, h2
->uri
));
1133 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1136 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1138 return (strcmp(d1
->d
, d2
->d
));
1140 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1143 get_work_dir(struct settings
*s
)
1145 if (work_dir
[0] == '\0')
1147 return (g_strdup(work_dir
));
1151 set_work_dir(struct settings
*s
, char *val
)
1154 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1155 pwd
->pw_dir
, &val
[1]);
1157 strlcpy(work_dir
, val
, sizeof work_dir
);
1163 * generate a session key to secure xtp commands.
1164 * pass in a ptr to the key in question and it will
1165 * be modified in place.
1168 generate_xtp_session_key(char **key
)
1170 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1176 /* make a new one */
1177 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1178 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1179 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1180 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1182 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1186 * validate a xtp session key.
1190 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1192 if (strcmp(trusted
, untrusted
) != 0) {
1193 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1202 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1204 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1206 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1208 struct valid_url_types
{
1219 valid_url_type(char *url
)
1223 for (i
= 0; i
< LENGTH(vut
); i
++)
1224 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1231 print_cookie(char *msg
, SoupCookie
*c
)
1237 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1238 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1239 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1240 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1241 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1242 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1243 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1244 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1245 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1246 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1250 walk_alias(struct settings
*s
,
1251 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1256 if (s
== NULL
|| cb
== NULL
) {
1257 show_oops_s("walk_alias invalid parameters");
1261 TAILQ_FOREACH(a
, &aliases
, entry
) {
1262 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1263 cb(s
, str
, cb_args
);
1269 match_alias(char *url_in
)
1273 char *url_out
= NULL
, *search
, *enc_arg
;
1275 search
= g_strdup(url_in
);
1277 if (strsep(&arg
, " \t") == NULL
) {
1278 show_oops_s("match_alias: NULL URL");
1282 TAILQ_FOREACH(a
, &aliases
, entry
) {
1283 if (!strcmp(search
, a
->a_name
))
1288 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1291 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1292 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1295 url_out
= g_strdup(a
->a_uri
);
1303 guess_url_type(char *url_in
)
1306 char *url_out
= NULL
, *enc_search
= NULL
;
1308 url_out
= match_alias(url_in
);
1309 if (url_out
!= NULL
)
1314 * If there is no dot nor slash in the string and it isn't a
1315 * path to a local file and doesn't resolves to an IP, assume
1316 * that the user wants to search for the string.
1319 if (strchr(url_in
, '.') == NULL
&&
1320 strchr(url_in
, '/') == NULL
&&
1321 stat(url_in
, &sb
) != 0 &&
1322 gethostbyname(url_in
) == NULL
) {
1324 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1325 url_out
= g_strdup_printf(search_string
, enc_search
);
1331 /* XXX not sure about this heuristic */
1332 if (stat(url_in
, &sb
) == 0)
1333 url_out
= g_strdup_printf("file://%s", url_in
);
1335 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1337 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1343 load_uri(struct tab
*t
, gchar
*uri
)
1346 gchar
*newuri
= NULL
;
1352 /* Strip leading spaces. */
1353 while(*uri
&& isspace(*uri
))
1356 if (strlen(uri
) == 0) {
1361 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1362 for (i
= 0; i
< LENGTH(about_list
); i
++)
1363 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1364 bzero(&args
, sizeof args
);
1365 about_list
[i
].func(t
, &args
);
1366 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1370 show_oops(t
, "invalid about page");
1374 if (valid_url_type(uri
)) {
1375 newuri
= guess_url_type(uri
);
1379 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1380 webkit_web_view_load_uri(t
->wv
, uri
);
1387 get_uri(WebKitWebView
*wv
)
1389 WebKitWebFrame
*frame
;
1392 frame
= webkit_web_view_get_main_frame(wv
);
1393 uri
= webkit_web_frame_get_uri(frame
);
1395 if (uri
&& strlen(uri
) > 0)
1402 add_alias(struct settings
*s
, char *line
)
1405 struct alias
*a
= NULL
;
1407 if (s
== NULL
|| line
== NULL
) {
1408 show_oops_s("add_alias invalid parameters");
1413 a
= g_malloc(sizeof(*a
));
1415 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1416 show_oops_s("add_alias: incomplete alias definition");
1419 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1420 show_oops_s("add_alias: invalid alias definition");
1424 a
->a_name
= g_strdup(alias
);
1425 a
->a_uri
= g_strdup(l
);
1427 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1429 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1439 add_mime_type(struct settings
*s
, char *line
)
1443 struct mime_type
*m
= NULL
;
1444 int downloadfirst
= 0;
1446 /* XXX this could be smarter */
1448 if (line
== NULL
&& strlen(line
) == 0) {
1449 show_oops_s("add_mime_type invalid parameters");
1458 m
= g_malloc(sizeof(*m
));
1460 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1461 show_oops_s("add_mime_type: invalid mime_type");
1464 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1465 mime_type
[strlen(mime_type
) - 1] = '\0';
1470 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1471 show_oops_s("add_mime_type: invalid mime_type");
1475 m
->mt_type
= g_strdup(mime_type
);
1476 m
->mt_action
= g_strdup(l
);
1477 m
->mt_download
= downloadfirst
;
1479 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1480 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1482 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1492 find_mime_type(char *mime_type
)
1494 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1496 TAILQ_FOREACH(m
, &mtl
, entry
) {
1497 if (m
->mt_default
&&
1498 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1501 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1514 walk_mime_type(struct settings
*s
,
1515 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1517 struct mime_type
*m
;
1520 if (s
== NULL
|| cb
== NULL
)
1521 show_oops_s("walk_mime_type invalid parameters");
1523 TAILQ_FOREACH(m
, &mtl
, entry
) {
1524 str
= g_strdup_printf("%s%s --> %s",
1526 m
->mt_default
? "*" : "",
1528 cb(s
, str
, cb_args
);
1534 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1539 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1542 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1544 /* treat *.moo.com the same as .moo.com */
1545 if (str
[0] == '*' && str
[1] == '.')
1547 else if (str
[0] == '.')
1552 d
= g_malloc(sizeof *d
);
1554 d
->d
= g_strdup_printf(".%s", str
);
1556 d
->d
= g_strdup(str
);
1559 if (RB_INSERT(domain_list
, wl
, d
))
1562 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1573 add_cookie_wl(struct settings
*s
, char *entry
)
1575 wl_add(entry
, &c_wl
, 1);
1580 walk_cookie_wl(struct settings
*s
,
1581 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1585 if (s
== NULL
|| cb
== NULL
) {
1586 show_oops_s("walk_cookie_wl invalid parameters");
1590 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1591 cb(s
, d
->d
, cb_args
);
1595 walk_js_wl(struct settings
*s
,
1596 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1600 if (s
== NULL
|| cb
== NULL
) {
1601 show_oops_s("walk_js_wl invalid parameters");
1605 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1606 cb(s
, d
->d
, cb_args
);
1610 add_js_wl(struct settings
*s
, char *entry
)
1612 wl_add(entry
, &js_wl
, 1 /* persistent */);
1617 wl_find(const gchar
*search
, struct domain_list
*wl
)
1620 struct domain
*d
= NULL
, dfind
;
1623 if (search
== NULL
|| wl
== NULL
)
1625 if (strlen(search
) < 2)
1628 if (search
[0] != '.')
1629 s
= g_strdup_printf(".%s", search
);
1631 s
= g_strdup(search
);
1633 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1636 d
= RB_FIND(domain_list
, wl
, &dfind
);
1650 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1656 if (s
== NULL
|| wl
== NULL
)
1659 if (!strncmp(s
, "http://", strlen("http://")))
1660 s
= &s
[strlen("http://")];
1661 else if (!strncmp(s
, "https://", strlen("https://")))
1662 s
= &s
[strlen("https://")];
1667 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1668 /* chop string at first slash */
1669 if (s
[i
] == '/' || s
[i
] == '\0') {
1672 r
= wl_find(ss
, wl
);
1681 get_toplevel_domain(char *domain
)
1688 if (strlen(domain
) < 2)
1691 s
= &domain
[strlen(domain
) - 1];
1692 while (s
!= domain
) {
1708 settings_add(char *var
, char *val
)
1715 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1716 if (strcmp(var
, rs
[i
].name
))
1720 if (rs
[i
].s
->set(&rs
[i
], val
))
1721 errx(1, "invalid value for %s: %s", var
, val
);
1725 switch (rs
[i
].type
) {
1734 errx(1, "invalid sval for %s",
1748 errx(1, "invalid type for %s", var
);
1757 config_parse(char *filename
, int runtime
)
1760 char *line
, *cp
, *var
, *val
;
1761 size_t len
, lineno
= 0;
1763 char file
[PATH_MAX
];
1766 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1768 if (filename
== NULL
)
1771 if (runtime
&& runtime_settings
[0] != '\0') {
1772 snprintf(file
, sizeof file
, "%s/%s",
1773 work_dir
, runtime_settings
);
1774 if (stat(file
, &sb
)) {
1775 warnx("runtime file doesn't exist, creating it");
1776 if ((f
= fopen(file
, "w")) == NULL
)
1778 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1782 strlcpy(file
, filename
, sizeof file
);
1784 if ((config
= fopen(file
, "r")) == NULL
) {
1785 warn("config_parse: cannot open %s", filename
);
1790 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1791 if (feof(config
) || ferror(config
))
1795 cp
+= (long)strspn(cp
, WS
);
1796 if (cp
[0] == '\0') {
1802 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1803 errx(1, "invalid config file entry: %s", line
);
1805 cp
+= (long)strspn(cp
, WS
);
1807 if ((val
= strsep(&cp
, "\0")) == NULL
)
1810 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1811 handled
= settings_add(var
, val
);
1813 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1822 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1828 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1832 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1835 JSStringGetUTF8CString(jsref
, s
, l
);
1836 JSStringRelease(jsref
);
1842 disable_hints(struct tab
*t
)
1844 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1845 bzero(t
->hint_num
, sizeof t
->hint_num
);
1846 run_script(t
, "vimprobable_clear()");
1848 t
->hint_mode
= XT_HINT_NONE
;
1852 enable_hints(struct tab
*t
)
1854 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1855 run_script(t
, "vimprobable_show_hints()");
1857 t
->hint_mode
= XT_HINT_NONE
;
1860 #define XT_JS_OPEN ("open;")
1861 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1862 #define XT_JS_FIRE ("fire;")
1863 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1864 #define XT_JS_FOUND ("found;")
1865 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1868 run_script(struct tab
*t
, char *s
)
1870 JSGlobalContextRef ctx
;
1871 WebKitWebFrame
*frame
;
1873 JSValueRef val
, exception
;
1876 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1877 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1879 frame
= webkit_web_view_get_main_frame(t
->wv
);
1880 ctx
= webkit_web_frame_get_global_context(frame
);
1882 str
= JSStringCreateWithUTF8CString(s
);
1883 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1884 NULL
, 0, &exception
);
1885 JSStringRelease(str
);
1887 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1889 es
= js_ref_to_string(ctx
, exception
);
1890 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1894 es
= js_ref_to_string(ctx
, val
);
1895 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1897 /* handle return value right here */
1898 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1900 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1903 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1904 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1905 &es
[XT_JS_FIRE_LEN
]);
1910 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1911 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1922 hint(struct tab
*t
, struct karg
*args
)
1925 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1927 if (t
->hints_on
== 0)
1936 apply_style(struct tab
*t
)
1938 g_object_set(G_OBJECT(t
->settings
),
1939 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
1943 userstyle(struct tab
*t
, struct karg
*args
)
1945 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
1949 g_object_set(G_OBJECT(t
->settings
),
1950 "user-stylesheet-uri", NULL
, (char *)NULL
);
1959 * Doesn't work fully, due to the following bug:
1960 * https://bugs.webkit.org/show_bug.cgi?id=51747
1963 restore_global_history(void)
1965 char file
[PATH_MAX
];
1971 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1973 if ((f
= fopen(file
, "r")) == NULL
) {
1974 warnx("%s: fopen", __func__
);
1979 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1980 if (feof(f
) || ferror(f
))
1983 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1984 if (feof(f
) || ferror(f
)) {
1986 warnx("%s: broken history file\n", __func__
);
1990 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1991 webkit_web_history_item_new_with_data(uri
, title
);
1992 h
= g_malloc(sizeof(struct history
));
1993 h
->uri
= g_strdup(uri
);
1994 h
->title
= g_strdup(title
);
1995 RB_INSERT(history_list
, &hl
, h
);
1996 completion_add_uri(h
->uri
);
1998 warnx("%s: failed to restore history\n", __func__
);
2014 save_global_history_to_disk(struct tab
*t
)
2016 char file
[PATH_MAX
];
2020 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2022 if ((f
= fopen(file
, "w")) == NULL
) {
2023 show_oops(t
, "%s: global history file: %s",
2024 __func__
, strerror(errno
));
2028 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2029 if (h
->uri
&& h
->title
)
2030 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2039 quit(struct tab
*t
, struct karg
*args
)
2041 if (save_global_history
)
2042 save_global_history_to_disk(t
);
2050 open_tabs(struct tab
*t
, struct karg
*a
)
2052 char file
[PATH_MAX
];
2056 struct tab
*ti
, *tt
;
2061 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2062 if ((f
= fopen(file
, "r")) == NULL
)
2065 ti
= TAILQ_LAST(&tabs
, tab_list
);
2068 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2069 if (feof(f
) || ferror(f
))
2072 /* retrieve session name */
2073 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2074 strlcpy(named_session
,
2075 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2076 sizeof named_session
);
2080 if (uri
&& strlen(uri
))
2081 create_new_tab(uri
, NULL
, 1);
2087 /* close open tabs */
2088 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2090 tt
= TAILQ_FIRST(&tabs
);
2109 restore_saved_tabs(void)
2111 char file
[PATH_MAX
];
2112 int unlink_file
= 0;
2117 snprintf(file
, sizeof file
, "%s/%s",
2118 sessions_dir
, XT_RESTART_TABS_FILE
);
2119 if (stat(file
, &sb
) == -1)
2120 a
.s
= XT_SAVED_TABS_FILE
;
2123 a
.s
= XT_RESTART_TABS_FILE
;
2126 a
.i
= XT_SES_DONOTHING
;
2127 rv
= open_tabs(NULL
, &a
);
2136 save_tabs(struct tab
*t
, struct karg
*a
)
2138 char file
[PATH_MAX
];
2143 const gchar
**arr
= NULL
;
2148 snprintf(file
, sizeof file
, "%s/%s",
2149 sessions_dir
, named_session
);
2151 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2153 if ((f
= fopen(file
, "w")) == NULL
) {
2154 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2158 /* save session name */
2159 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2161 /* save tabs, in the order they are arranged in the notebook */
2162 TAILQ_FOREACH(ti
, &tabs
, entry
)
2165 arr
= g_malloc0(len
* sizeof(gchar
*));
2167 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2168 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2169 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2172 for (i
= 0; i
< len
; i
++)
2174 fprintf(f
, "%s\n", arr
[i
]);
2183 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2195 yank_uri(struct tab
*t
, struct karg
*args
)
2198 GtkClipboard
*clipboard
;
2200 if ((uri
= get_uri(t
->wv
)) == NULL
)
2203 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2204 gtk_clipboard_set_text(clipboard
, uri
, -1);
2210 paste_uri(struct tab
*t
, struct karg
*args
)
2212 GtkClipboard
*clipboard
;
2213 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2215 gchar
*p
= NULL
, *uri
;
2217 /* try primary clipboard first */
2218 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2219 p
= gtk_clipboard_wait_for_text(clipboard
);
2221 /* if it failed get whatever text is in cut_buffer0 */
2223 if (gdk_property_get(gdk_get_default_root_window(),
2225 gdk_atom_intern("STRING", FALSE
),
2227 65536 /* picked out of my butt */,
2233 /* yes sir, we need to NUL the string */
2239 while(*uri
&& isspace(*uri
))
2241 if (strlen(uri
) == 0) {
2242 show_oops(t
, "empty paste buffer");
2245 if (valid_url_type(uri
)) {
2246 /* we can be clever and paste this in search box */
2247 show_oops(t
, "not a valid URL");
2251 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2253 else if (args
->i
== XT_PASTE_NEW_TAB
)
2254 create_new_tab(uri
, NULL
, 1);
2265 find_domain(const gchar
*s
, int add_dot
)
2268 char *r
= NULL
, *ss
= NULL
;
2273 if (!strncmp(s
, "http://", strlen("http://")))
2274 s
= &s
[strlen("http://")];
2275 else if (!strncmp(s
, "https://", strlen("https://")))
2276 s
= &s
[strlen("https://")];
2282 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2283 /* chop string at first slash */
2284 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2287 r
= g_strdup_printf(".%s", ss
);
2298 toggle_cwl(struct tab
*t
, struct karg
*args
)
2302 char *dom
= NULL
, *dom_toggle
= NULL
;
2308 uri
= get_uri(t
->wv
);
2309 dom
= find_domain(uri
, 1);
2310 d
= wl_find(dom
, &c_wl
);
2317 if (args
->i
& XT_WL_TOGGLE
)
2319 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2321 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2324 if (args
->i
& XT_WL_TOPLEVEL
)
2325 dom_toggle
= get_toplevel_domain(dom
);
2330 /* enable cookies for domain */
2331 wl_add(dom_toggle
, &c_wl
, 0);
2333 /* disable cookies for domain */
2334 RB_REMOVE(domain_list
, &c_wl
, d
);
2336 webkit_web_view_reload(t
->wv
);
2343 toggle_js(struct tab
*t
, struct karg
*args
)
2348 char *dom
= NULL
, *dom_toggle
= NULL
;
2353 g_object_get(G_OBJECT(t
->settings
),
2354 "enable-scripts", &es
, (char *)NULL
);
2355 if (args
->i
& XT_WL_TOGGLE
)
2357 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2359 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2364 uri
= get_uri(t
->wv
);
2365 dom
= find_domain(uri
, 1);
2367 if (uri
== NULL
|| dom
== NULL
) {
2368 show_oops(t
, "Can't toggle domain in JavaScript white list");
2372 if (args
->i
& XT_WL_TOPLEVEL
)
2373 dom_toggle
= get_toplevel_domain(dom
);
2378 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2379 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2381 d
= wl_find(dom_toggle
, &js_wl
);
2383 RB_REMOVE(domain_list
, &js_wl
, d
);
2384 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2386 g_object_set(G_OBJECT(t
->settings
),
2387 "enable-scripts", es
, (char *)NULL
);
2388 g_object_set(G_OBJECT(t
->settings
),
2389 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2390 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2391 webkit_web_view_reload(t
->wv
);
2399 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2403 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2408 toggle_src(struct tab
*t
, struct karg
*args
)
2415 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2416 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2417 webkit_web_view_reload(t
->wv
);
2423 focus_webview(struct tab
*t
)
2428 /* only grab focus if we are visible */
2429 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2430 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2434 focus(struct tab
*t
, struct karg
*args
)
2436 if (t
== NULL
|| args
== NULL
)
2442 if (args
->i
== XT_FOCUS_URI
)
2443 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2444 else if (args
->i
== XT_FOCUS_SEARCH
)
2445 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2451 stats(struct tab
*t
, struct karg
*args
)
2453 char *stats
, *s
, line
[64 * 1024];
2454 uint64_t line_count
= 0;
2458 show_oops_s("stats invalid parameters");
2461 if (save_rejected_cookies
) {
2462 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2464 s
= fgets(line
, sizeof line
, r_cookie_f
);
2465 if (s
== NULL
|| feof(r_cookie_f
) ||
2471 snprintf(line
, sizeof line
,
2472 "<br>Cookies blocked(*) total: %llu", line_count
);
2474 show_oops(t
, "Can't open blocked cookies file: %s",
2478 stats
= g_strdup_printf(XT_DOCTYPE
2481 "<title>Statistics</title>"
2483 "<h1>Statistics</h1>"
2485 "Cookies blocked(*) this session: %llu"
2487 "<p><small><b>*</b> results vary based on settings"
2493 load_webkit_string(t
, stats
, XT_URI_ABOUT_STATS
);
2500 marco(struct tab
*t
, struct karg
*args
)
2502 char *message
, line
[64 * 1024];
2506 show_oops_s("marco invalid parameters");
2509 snprintf(line
, sizeof line
, "<br>%s", marco_message(&len
));
2511 message
= g_strdup_printf(XT_DOCTYPE
2514 "<title>Marco Sez...</title>"
2523 load_webkit_string(t
, message
, XT_URI_ABOUT_MARCO
);
2530 blank(struct tab
*t
, struct karg
*args
)
2533 show_oops_s("blank invalid parameters");
2535 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2540 about(struct tab
*t
, struct karg
*args
)
2545 show_oops_s("about invalid parameters");
2547 about
= g_strdup_printf(XT_DOCTYPE
2550 "<title>About</title>"
2554 "<b>Version: %s</b><p>"
2557 "<li>Marco Peereboom <marco@peereboom.us></li>"
2558 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2559 "<li>Edd Barrett <vext01@gmail.com> </li>"
2560 "<li>Todd T. Fries <todd@fries.net> </li>"
2562 "Copyrights and licenses can be found on the XXXterm "
2563 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2569 load_webkit_string(t
, about
, XT_URI_ABOUT_ABOUT
);
2576 help(struct tab
*t
, struct karg
*args
)
2581 show_oops_s("help invalid parameters");
2586 "<title>XXXterm</title>"
2587 "<meta http-equiv=\"REFRESH\" content=\"0;"
2588 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2591 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2592 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2593 "cgi-bin/man-cgi?xxxterm</a>"
2598 load_webkit_string(t
, help
, XT_URI_ABOUT_HELP
);
2604 * update all favorite tabs apart from one. Pass NULL if
2605 * you want to update all.
2608 update_favorite_tabs(struct tab
*apart_from
)
2611 if (!updating_fl_tabs
) {
2612 updating_fl_tabs
= 1; /* stop infinite recursion */
2613 TAILQ_FOREACH(t
, &tabs
, entry
)
2614 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2615 && (t
!= apart_from
))
2616 xtp_page_fl(t
, NULL
);
2617 updating_fl_tabs
= 0;
2621 /* show a list of favorites (bookmarks) */
2623 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2625 char file
[PATH_MAX
];
2627 char *uri
= NULL
, *title
= NULL
;
2628 size_t len
, lineno
= 0;
2630 char *header
, *body
, *tmp
, *html
= NULL
;
2632 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2635 warn("%s: bad param", __func__
);
2637 /* mark tab as favorite list */
2638 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2640 /* new session key */
2641 if (!updating_fl_tabs
)
2642 generate_xtp_session_key(&fl_session_key
);
2644 /* open favorites */
2645 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2646 if ((f
= fopen(file
, "r")) == NULL
) {
2647 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2652 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2653 "<title>Favorites</title>\n"
2656 "<h1>Favorites</h1>\n",
2660 body
= g_strdup_printf("<div align='center'><table><tr>"
2661 "<th style='width: 4%%'>#</th><th>Link</th>"
2662 "<th style='width: 15%%'>Remove</th></tr>\n");
2665 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2666 if (feof(f
) || ferror(f
))
2674 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2675 if (feof(f
) || ferror(f
)) {
2676 show_oops(t
, "favorites file corrupt");
2682 body
= g_strdup_printf("%s<tr>"
2684 "<td><a href='%s'>%s</a></td>"
2685 "<td style='text-align: center'>"
2686 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2688 body
, i
, uri
, title
,
2689 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2701 /* if none, say so */
2704 body
= g_strdup_printf("%s<tr>"
2705 "<td colspan='3' style='text-align: center'>"
2706 "No favorites - To add one use the 'favadd' command."
2707 "</td></tr>", body
);
2718 html
= g_strdup_printf("%s%s</table></div></html>",
2720 load_webkit_string(t
, html
, XT_URI_ABOUT_FAVORITES
);
2723 update_favorite_tabs(t
);
2736 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2737 size_t cert_count
, char *title
)
2739 gnutls_datum_t cinfo
;
2740 char *tmp
, *header
, *body
, *footer
;
2743 header
= g_strdup_printf("<html><head><title>%s</title></head><body>", title
);
2744 footer
= g_strdup("</body></html>");
2745 body
= g_strdup("");
2747 for (i
= 0; i
< cert_count
; i
++) {
2748 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2753 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2754 body
, i
, cinfo
.data
);
2755 gnutls_free(cinfo
.data
);
2759 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2763 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2768 ca_cmd(struct tab
*t
, struct karg
*args
)
2771 int rv
= 1, certs
= 0, certs_read
;
2774 gnutls_x509_crt_t
*c
= NULL
;
2775 char *certs_buf
= NULL
, *s
;
2777 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2778 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2782 if (fstat(fileno(f
), &sb
) == -1) {
2783 show_oops(t
, "Can't stat CA file: %s", strerror(errno
));
2787 certs_buf
= g_malloc(sb
.st_size
+ 1);
2788 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2789 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2792 certs_buf
[sb
.st_size
] = '\0';
2795 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2797 s
+= strlen("BEGIN CERTIFICATE");
2800 bzero(&dt
, sizeof dt
);
2801 dt
.data
= certs_buf
;
2802 dt
.size
= sb
.st_size
;
2803 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2804 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2805 GNUTLS_X509_FMT_PEM
, 0);
2806 if (certs_read
<= 0) {
2807 show_oops(t
, "No cert(s) available");
2810 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2823 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2826 struct addrinfo hints
, *res
= NULL
, *ai
;
2830 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2833 su
= soup_uri_new(uri
);
2836 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2839 snprintf(port
, sizeof port
, "%d", su
->port
);
2840 bzero(&hints
, sizeof(struct addrinfo
));
2841 hints
.ai_flags
= AI_CANONNAME
;
2842 hints
.ai_family
= AF_UNSPEC
;
2843 hints
.ai_socktype
= SOCK_STREAM
;
2845 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2848 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2849 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2852 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2855 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2859 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2864 strlcpy(domain
, su
->host
, domain_sz
);
2875 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2878 gnutls_deinit(gsession
);
2880 gnutls_certificate_free_credentials(xcred
);
2886 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2887 gnutls_certificate_credentials_t
*xc
)
2889 gnutls_certificate_credentials_t xcred
;
2890 gnutls_session_t gsession
;
2893 if (gs
== NULL
|| xc
== NULL
)
2899 gnutls_certificate_allocate_credentials(&xcred
);
2900 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2901 GNUTLS_X509_FMT_PEM
);
2902 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2903 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2904 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2905 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2906 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2907 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2909 gnutls_error_is_fatal(rv
),
2910 gnutls_strerror_name(rv
));
2911 stop_tls(gsession
, xcred
);
2915 gnutls_credentials_type_t cred
;
2916 cred
= gnutls_auth_get_type(gsession
);
2917 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2918 stop_tls(gsession
, xcred
);
2930 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2934 const gnutls_datum_t
*cl
;
2935 gnutls_x509_crt_t
*all_certs
;
2938 if (certs
== NULL
|| cert_count
== NULL
)
2940 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2942 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2946 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2947 for (i
= 0; i
< len
; i
++) {
2948 gnutls_x509_crt_init(&all_certs
[i
]);
2949 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2950 GNUTLS_X509_FMT_PEM
< 0)) {
2964 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2968 for (i
= 0; i
< cert_count
; i
++)
2969 gnutls_x509_crt_deinit(certs
[i
]);
2974 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2975 size_t cert_count
, char *domain
)
2978 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2983 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2986 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2987 if ((f
= fopen(file
, "w")) == NULL
) {
2988 show_oops(t
, "Can't create cert file %s %s",
2989 file
, strerror(errno
));
2993 for (i
= 0; i
< cert_count
; i
++) {
2994 cert_buf_sz
= sizeof cert_buf
;
2995 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2996 cert_buf
, &cert_buf_sz
)) {
2997 show_oops(t
, "gnutls_x509_crt_export failed");
3000 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3001 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3006 /* not the best spot but oh well */
3007 gdk_color_parse("lightblue", &color
);
3008 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3009 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3010 gdk_color_parse(XT_COLOR_BLACK
, &color
);
3011 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3017 load_compare_cert(struct tab
*t
, struct karg
*args
)
3020 char domain
[8182], file
[PATH_MAX
];
3021 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3022 int s
= -1, rv
= 1, i
;
3026 gnutls_session_t gsession
;
3027 gnutls_x509_crt_t
*certs
;
3028 gnutls_certificate_credentials_t xcred
;
3033 if ((uri
= get_uri(t
->wv
)) == NULL
)
3036 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3040 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3041 show_oops(t
, "Start TLS failed");
3046 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3047 show_oops(t
, "Can't get connection certificates");
3051 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3052 if ((f
= fopen(file
, "r")) == NULL
)
3055 for (i
= 0; i
< cert_count
; i
++) {
3056 cert_buf_sz
= sizeof cert_buf
;
3057 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3058 cert_buf
, &cert_buf_sz
)) {
3061 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3062 rv
= -1; /* critical */
3065 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3066 rv
= -1; /* critical */
3075 free_connection_certs(certs
, cert_count
);
3077 /* we close the socket first for speed */
3080 stop_tls(gsession
, xcred
);
3086 cert_cmd(struct tab
*t
, struct karg
*args
)
3092 gnutls_session_t gsession
;
3093 gnutls_x509_crt_t
*certs
;
3094 gnutls_certificate_credentials_t xcred
;
3099 if ((uri
= get_uri(t
->wv
)) == NULL
) {
3100 show_oops(t
, "Invalid URI");
3104 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3105 show_oops(t
, "Invalid certidicate URI: %s", uri
);
3110 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3111 show_oops(t
, "Start TLS failed");
3116 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3117 show_oops(t
, "get_connection_certs failed");
3121 if (args
->i
& XT_SHOW
)
3122 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3123 else if (args
->i
& XT_SAVE
)
3124 save_certs(t
, certs
, cert_count
, domain
);
3126 free_connection_certs(certs
, cert_count
);
3128 /* we close the socket first for speed */
3131 stop_tls(gsession
, xcred
);
3137 remove_cookie(int index
)
3143 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3145 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3147 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3151 print_cookie("remove cookie", c
);
3152 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3157 soup_cookies_free(cf
);
3163 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3166 char *tmp
, *header
, *body
, *footer
;
3168 /* we set this to indicate we want to manually do navaction */
3169 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3171 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
3173 footer
= g_strdup("</body></html>");
3174 body
= g_strdup("");
3177 if (args
->i
& XT_WL_PERSISTENT
) {
3179 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3181 RB_FOREACH(d
, domain_list
, wl
) {
3185 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3191 if (args
->i
& XT_WL_SESSION
) {
3193 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3195 RB_FOREACH(d
, domain_list
, wl
) {
3199 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3204 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3209 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3211 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3217 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3219 char file
[PATH_MAX
];
3221 char *line
= NULL
, *lt
= NULL
;
3224 char *dom
= NULL
, *dom_save
= NULL
;
3230 if (t
== NULL
|| args
== NULL
)
3233 if (runtime_settings
[0] == '\0')
3236 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3237 if ((f
= fopen(file
, "r+")) == NULL
)
3240 uri
= get_uri(t
->wv
);
3241 dom
= find_domain(uri
, 1);
3242 if (uri
== NULL
|| dom
== NULL
) {
3243 show_oops(t
, "Can't add domain to %s white list",
3244 js
? "JavaScript" : "cookie");
3248 if (args
->i
& XT_WL_TOPLEVEL
) {
3250 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3251 show_oops(t
, "invalid domain: %s", dom
);
3254 } else if (args
->i
& XT_WL_FQDN
) {
3260 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3263 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3266 if (!strcmp(line
, lt
))
3272 fprintf(f
, "%s\n", lt
);
3277 d
= wl_find(dom_save
, &js_wl
);
3279 settings_add("js_wl", dom_save
);
3280 d
= wl_find(dom_save
, &js_wl
);
3284 d
= wl_find(dom_save
, &c_wl
);
3286 settings_add("cookie_wl", dom_save
);
3287 d
= wl_find(dom_save
, &c_wl
);
3291 /* find and add to persistent jar */
3292 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3293 for (;cf
; cf
= cf
->next
) {
3295 if (!strcmp(dom_save
, ci
->domain
) ||
3296 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3297 c
= soup_cookie_copy(ci
);
3298 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3301 soup_cookies_free(cf
);
3319 js_show_wl(struct tab
*t
, struct karg
*args
)
3321 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3322 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3328 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3330 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3331 wl_show(t
, args
, "Cookie White List", &c_wl
);
3337 cookie_cmd(struct tab
*t
, struct karg
*args
)
3339 if (args
->i
& XT_SHOW
)
3340 wl_show(t
, args
, "Cookie White List", &c_wl
);
3341 else if (args
->i
& XT_WL_TOGGLE
)
3342 toggle_cwl(t
, args
);
3343 else if (args
->i
& XT_SAVE
)
3344 wl_save(t
, args
, 0);
3345 else if (args
->i
& XT_DELETE
)
3346 show_oops(t
, "'cookie delete' currently unimplemented");
3352 js_cmd(struct tab
*t
, struct karg
*args
)
3354 if (args
->i
& XT_SHOW
)
3355 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3356 else if (args
->i
& XT_SAVE
)
3357 wl_save(t
, args
, 1);
3358 else if (args
->i
& XT_WL_TOGGLE
)
3360 else if (args
->i
& XT_DELETE
)
3361 show_oops(t
, "'js delete' currently unimplemented");
3367 add_favorite(struct tab
*t
, struct karg
*args
)
3369 char file
[PATH_MAX
];
3372 size_t urilen
, linelen
;
3373 const gchar
*uri
, *title
;
3378 /* don't allow adding of xtp pages to favorites */
3379 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3380 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3384 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3385 if ((f
= fopen(file
, "r+")) == NULL
) {
3386 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3390 title
= webkit_web_view_get_title(t
->wv
);
3391 uri
= get_uri(t
->wv
);
3396 if (title
== NULL
|| uri
== NULL
) {
3397 show_oops(t
, "can't add page to favorites");
3401 urilen
= strlen(uri
);
3404 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3405 if (feof(f
) || ferror(f
))
3408 if (linelen
== urilen
&& !strcmp(line
, uri
))
3415 fprintf(f
, "\n%s\n%s", title
, uri
);
3421 update_favorite_tabs(NULL
);
3427 navaction(struct tab
*t
, struct karg
*args
)
3429 WebKitWebHistoryItem
*item
;
3431 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3432 t
->tab_id
, args
->i
);
3435 if (args
->i
== XT_NAV_BACK
)
3436 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3438 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3440 return (XT_CB_PASSTHROUGH
);
3441 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3443 return (XT_CB_PASSTHROUGH
);
3448 webkit_web_view_go_back(t
->wv
);
3450 case XT_NAV_FORWARD
:
3451 webkit_web_view_go_forward(t
->wv
);
3454 webkit_web_view_reload(t
->wv
);
3456 case XT_NAV_RELOAD_CACHE
:
3457 webkit_web_view_reload_bypass_cache(t
->wv
);
3460 return (XT_CB_PASSTHROUGH
);
3464 move(struct tab
*t
, struct karg
*args
)
3466 GtkAdjustment
*adjust
;
3467 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3472 case XT_MOVE_BOTTOM
:
3474 case XT_MOVE_PAGEDOWN
:
3475 case XT_MOVE_PAGEUP
:
3476 case XT_MOVE_HALFDOWN
:
3477 case XT_MOVE_HALFUP
:
3478 adjust
= t
->adjust_v
;
3481 adjust
= t
->adjust_h
;
3485 pos
= gtk_adjustment_get_value(adjust
);
3486 ps
= gtk_adjustment_get_page_size(adjust
);
3487 upper
= gtk_adjustment_get_upper(adjust
);
3488 lower
= gtk_adjustment_get_lower(adjust
);
3489 si
= gtk_adjustment_get_step_increment(adjust
);
3490 pi
= gtk_adjustment_get_page_increment(adjust
);
3493 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3494 "max %f si %f pi %f\n",
3495 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3496 pos
, ps
, upper
, lower
, max
, si
, pi
);
3502 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3507 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3509 case XT_MOVE_BOTTOM
:
3510 case XT_MOVE_FARRIGHT
:
3511 gtk_adjustment_set_value(adjust
, max
);
3514 case XT_MOVE_FARLEFT
:
3515 gtk_adjustment_set_value(adjust
, lower
);
3517 case XT_MOVE_PAGEDOWN
:
3519 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3521 case XT_MOVE_PAGEUP
:
3523 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3525 case XT_MOVE_HALFDOWN
:
3527 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3529 case XT_MOVE_HALFUP
:
3531 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3534 return (XT_CB_PASSTHROUGH
);
3537 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3539 return (XT_CB_HANDLED
);
3543 url_set_visibility(void)
3547 TAILQ_FOREACH(t
, &tabs
, entry
) {
3548 if (show_url
== 0) {
3549 gtk_widget_hide(t
->toolbar
);
3552 gtk_widget_show(t
->toolbar
);
3557 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3560 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3562 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3566 statusbar_set_visibility(void)
3570 TAILQ_FOREACH(t
, &tabs
, entry
) {
3571 if (show_statusbar
== 0) {
3572 gtk_widget_hide(t
->statusbar
);
3575 gtk_widget_show(t
->statusbar
);
3580 url_set(struct tab
*t
, int enable_url_entry
)
3585 show_url
= enable_url_entry
;
3587 if (enable_url_entry
) {
3588 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3589 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3590 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3592 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3593 GTK_ENTRY_ICON_PRIMARY
);
3595 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3596 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3597 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3598 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3604 fullscreen(struct tab
*t
, struct karg
*args
)
3606 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3609 return (XT_CB_PASSTHROUGH
);
3611 if (show_url
== 0) {
3619 url_set_visibility();
3620 notebook_tab_set_visibility(notebook
);
3622 return (XT_CB_HANDLED
);
3626 statusaction(struct tab
*t
, struct karg
*args
)
3628 int rv
= XT_CB_HANDLED
;
3630 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3633 return (XT_CB_PASSTHROUGH
);
3636 case XT_STATUSBAR_SHOW
:
3637 if (show_statusbar
== 0) {
3639 statusbar_set_visibility();
3642 case XT_STATUSBAR_HIDE
:
3643 if (show_statusbar
== 1) {
3645 statusbar_set_visibility();
3653 urlaction(struct tab
*t
, struct karg
*args
)
3655 int rv
= XT_CB_HANDLED
;
3657 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3660 return (XT_CB_PASSTHROUGH
);
3664 if (show_url
== 0) {
3666 url_set_visibility();
3670 if (show_url
== 1) {
3672 url_set_visibility();
3680 tabaction(struct tab
*t
, struct karg
*args
)
3682 int rv
= XT_CB_HANDLED
;
3683 char *url
= args
->s
;
3686 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3689 return (XT_CB_PASSTHROUGH
);
3693 if (strlen(url
) > 0)
3694 create_new_tab(url
, NULL
, 1);
3696 create_new_tab(NULL
, NULL
, 1);
3701 case XT_TAB_DELQUIT
:
3702 if (gtk_notebook_get_n_pages(notebook
) > 1)
3708 if (strlen(url
) > 0)
3711 rv
= XT_CB_PASSTHROUGH
;
3717 if (show_tabs
== 0) {
3719 notebook_tab_set_visibility(notebook
);
3723 if (show_tabs
== 1) {
3725 notebook_tab_set_visibility(notebook
);
3728 case XT_TAB_UNDO_CLOSE
:
3729 if (undo_count
== 0) {
3730 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3734 u
= TAILQ_FIRST(&undos
);
3735 create_new_tab(u
->uri
, u
, 1);
3737 TAILQ_REMOVE(&undos
, u
, entry
);
3739 /* u->history is freed in create_new_tab() */
3744 rv
= XT_CB_PASSTHROUGH
;
3758 resizetab(struct tab
*t
, struct karg
*args
)
3760 if (t
== NULL
|| args
== NULL
) {
3761 show_oops_s("resizetab invalid parameters");
3762 return (XT_CB_PASSTHROUGH
);
3765 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3766 t
->tab_id
, args
->i
);
3768 adjustfont_webkit(t
, args
->i
);
3770 return (XT_CB_HANDLED
);
3774 movetab(struct tab
*t
, struct karg
*args
)
3779 if (t
== NULL
|| args
== NULL
) {
3780 show_oops_s("movetab invalid parameters");
3781 return (XT_CB_PASSTHROUGH
);
3784 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3785 t
->tab_id
, args
->i
);
3787 if (args
->i
== XT_TAB_INVALID
)
3788 return (XT_CB_PASSTHROUGH
);
3790 if (args
->i
< XT_TAB_INVALID
) {
3791 /* next or previous tab */
3792 if (TAILQ_EMPTY(&tabs
))
3793 return (XT_CB_PASSTHROUGH
);
3797 if (strlen(args
->s
) == 0) {
3798 /* if at the last page, loop around to the first */
3799 if (gtk_notebook_get_current_page(notebook
) ==
3800 gtk_notebook_get_n_pages(notebook
) - 1)
3801 gtk_notebook_set_current_page(notebook
, 0);
3803 gtk_notebook_next_page(notebook
);
3805 x
= atoi(args
->s
) - 1;
3807 return (XT_CB_PASSTHROUGH
);
3809 if (t
->tab_id
== x
) {
3810 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3811 return (XT_CB_HANDLED
);
3813 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3814 if (tt
->tab_id
== x
) {
3815 gtk_notebook_set_current_page(notebook
, x
);
3816 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3824 /* if at the first page, loop around to the last */
3825 if (gtk_notebook_current_page(notebook
) == 0)
3826 gtk_notebook_set_current_page(notebook
,
3827 gtk_notebook_get_n_pages(notebook
) - 1);
3829 gtk_notebook_prev_page(notebook
);
3832 gtk_notebook_set_current_page(notebook
, 0);
3835 gtk_notebook_set_current_page(notebook
, -1);
3838 return (XT_CB_PASSTHROUGH
);
3841 return (XT_CB_HANDLED
);
3844 return (XT_CB_HANDLED
);
3850 command(struct tab
*t
, struct karg
*args
)
3852 char *s
= NULL
, *ss
= NULL
;
3856 if (t
== NULL
|| args
== NULL
) {
3857 show_oops_s("command invalid parameters");
3858 return (XT_CB_PASSTHROUGH
);
3869 if (cmd_prefix
== 0)
3872 ss
= g_strdup_printf(":%d", cmd_prefix
);
3883 case XT_CMD_OPEN_CURRENT
:
3886 case XT_CMD_TABNEW_CURRENT
:
3887 if (!s
) /* FALL THROUGH? */
3889 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3890 ss
= g_strdup_printf("%s%s", s
, uri
);
3895 show_oops(t
, "command: invalid opcode %d", args
->i
);
3896 return (XT_CB_PASSTHROUGH
);
3899 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3901 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3902 gdk_color_parse(XT_COLOR_WHITE
, &color
);
3903 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3905 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3906 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3911 return (XT_CB_HANDLED
);
3915 * Return a new string with a download row (in html)
3916 * appended. Old string is freed.
3919 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3922 WebKitDownloadStatus stat
;
3923 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3925 char cur_sz
[FMT_SCALED_STRSIZE
];
3926 char tot_sz
[FMT_SCALED_STRSIZE
];
3929 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3931 /* All actions wil take this form:
3932 * xxxt://class/seskey
3934 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3935 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3937 stat
= webkit_download_get_status(dl
->download
);
3940 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3941 status_html
= g_strdup_printf("Finished");
3942 cmd_html
= g_strdup_printf(
3943 "<a href='%s%d/%d'>Remove</a>",
3944 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3946 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3947 /* gather size info */
3948 progress
= 100 * webkit_download_get_progress(dl
->download
);
3951 webkit_download_get_current_size(dl
->download
), cur_sz
);
3953 webkit_download_get_total_size(dl
->download
), tot_sz
);
3955 status_html
= g_strdup_printf(
3956 "<div style='width: 100%%' align='center'>"
3957 "<div class='progress-outer'>"
3958 "<div class='progress-inner' style='width: %.2f%%'>"
3959 "</div></div></div>"
3960 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3961 progress
, cur_sz
, tot_sz
, progress
);
3963 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3964 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3968 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3969 status_html
= g_strdup_printf("Cancelled");
3970 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3971 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3973 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3974 status_html
= g_strdup_printf("Error!");
3975 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3976 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3978 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3979 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3980 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3981 status_html
= g_strdup_printf("Starting");
3984 show_oops(t
, "%s: unknown download status", __func__
);
3987 new_html
= g_strdup_printf(
3988 "%s\n<tr><td>%s</td><td>%s</td>"
3989 "<td style='text-align:center'>%s</td></tr>\n",
3990 html
, basename(webkit_download_get_destination_uri(dl
->download
)),
3991 status_html
, cmd_html
);
3995 g_free(status_html
);
4006 * update all download tabs apart from one. Pass NULL if
4007 * you want to update all.
4010 update_download_tabs(struct tab
*apart_from
)
4013 if (!updating_dl_tabs
) {
4014 updating_dl_tabs
= 1; /* stop infinite recursion */
4015 TAILQ_FOREACH(t
, &tabs
, entry
)
4016 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4017 && (t
!= apart_from
))
4018 xtp_page_dl(t
, NULL
);
4019 updating_dl_tabs
= 0;
4024 * update all cookie tabs apart from one. Pass NULL if
4025 * you want to update all.
4028 update_cookie_tabs(struct tab
*apart_from
)
4031 if (!updating_cl_tabs
) {
4032 updating_cl_tabs
= 1; /* stop infinite recursion */
4033 TAILQ_FOREACH(t
, &tabs
, entry
)
4034 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4035 && (t
!= apart_from
))
4036 xtp_page_cl(t
, NULL
);
4037 updating_cl_tabs
= 0;
4042 * update all history tabs apart from one. Pass NULL if
4043 * you want to update all.
4046 update_history_tabs(struct tab
*apart_from
)
4050 if (!updating_hl_tabs
) {
4051 updating_hl_tabs
= 1; /* stop infinite recursion */
4052 TAILQ_FOREACH(t
, &tabs
, entry
)
4053 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4054 && (t
!= apart_from
))
4055 xtp_page_hl(t
, NULL
);
4056 updating_hl_tabs
= 0;
4060 /* cookie management XTP page */
4062 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4064 char *header
, *body
, *footer
, *page
, *tmp
;
4065 int i
= 1; /* all ids start 1 */
4066 GSList
*sc
, *pc
, *pc_start
;
4068 char *type
, *table_headers
;
4069 char *last_domain
= strdup("");
4071 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4074 show_oops_s("%s invalid parameters", __func__
);
4077 /* mark this tab as cookie jar */
4078 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
4080 /* Generate a new session key */
4081 if (!updating_cl_tabs
)
4082 generate_xtp_session_key(&cl_session_key
);
4085 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4086 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
4087 "</head><body><h1>Cookie Jar</h1>\n");
4090 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
4097 "<th>HTTP<br />only</th>"
4098 "<th>Rm</th></tr>\n");
4100 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4101 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4105 for (; sc
; sc
= sc
->next
) {
4108 if (strcmp(last_domain
, c
->domain
) != 0) {
4111 last_domain
= strdup(c
->domain
);
4115 body
= g_strdup_printf("%s</table></div>"
4117 body
, c
->domain
, table_headers
);
4121 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4122 c
->domain
, table_headers
);
4127 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4128 if (soup_cookie_equal(pc
->data
, c
)) {
4129 type
= "Session + Persistent";
4134 body
= g_strdup_printf(
4136 "<td style='width: text-align: center'>%s</td>"
4137 "<td style='width: 1px'>%s</td>"
4138 "<td style='width=70%%;overflow: visible'>"
4139 " <textarea rows='4'>%s</textarea>"
4143 "<td style='width: 1px; text-align: center'>%d</td>"
4144 "<td style='width: 1px; text-align: center'>%d</td>"
4145 "<td style='width: 1px; text-align: center'>"
4146 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4153 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4168 soup_cookies_free(sc
);
4169 soup_cookies_free(pc
);
4171 /* small message if there are none */
4173 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4174 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4178 footer
= g_strdup_printf("</table></div></body></html>");
4180 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4185 g_free(table_headers
);
4186 g_free(last_domain
);
4188 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4189 update_cookie_tabs(t
);
4197 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4199 char *header
, *body
, *footer
, *page
, *tmp
;
4201 int i
= 1; /* all ids start 1 */
4203 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4206 show_oops_s("%s invalid parameters", __func__
);
4210 /* mark this tab as history manager */
4211 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4213 /* Generate a new session key */
4214 if (!updating_hl_tabs
)
4215 generate_xtp_session_key(&hl_session_key
);
4218 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4219 "<title>History</title>\n"
4222 "<h1>History</h1>\n",
4226 body
= g_strdup_printf("<div align='center'><table><tr>"
4227 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4229 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4231 body
= g_strdup_printf(
4233 "<td><a href='%s'>%s</a></td>"
4235 "<td style='text-align: center'>"
4236 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4237 body
, h
->uri
, h
->uri
, h
->title
,
4238 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4239 XT_XTP_HL_REMOVE
, i
);
4245 /* small message if there are none */
4248 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4249 "colspan='3'>No History</td></tr>\n", body
);
4254 footer
= g_strdup_printf("</table></div></body></html>");
4256 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4259 * update all history manager tabs as the xtp session
4260 * key has now changed. No need to update the current tab.
4261 * Already did that above.
4263 update_history_tabs(t
);
4269 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4276 * Generate a web page detailing the status of any downloads
4279 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4281 struct download
*dl
;
4282 char *header
, *body
, *footer
, *page
, *tmp
;
4286 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4289 show_oops_s("%s invalid parameters", __func__
);
4292 /* mark as a download manager tab */
4293 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4296 * Generate a new session key for next page instance.
4297 * This only happens for the top level call to xtp_page_dl()
4298 * in which case updating_dl_tabs is 0.
4300 if (!updating_dl_tabs
)
4301 generate_xtp_session_key(&dl_session_key
);
4303 /* header - with refresh so as to update */
4304 if (refresh_interval
>= 1)
4305 ref
= g_strdup_printf(
4306 "<meta http-equiv='refresh' content='%u"
4307 ";url=%s%d/%s/%d' />\n",
4317 header
= g_strdup_printf(
4319 "<title>Downloads</title>\n%s%s</head>\n",
4320 XT_DOCTYPE XT_HTML_TAG
,
4324 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4325 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4326 "</p><table><tr><th style='width: 60%%'>"
4327 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4328 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4330 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4331 body
= xtp_page_dl_row(t
, body
, dl
);
4335 /* message if no downloads in list */
4338 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4339 " style='text-align: center'>"
4340 "No downloads</td></tr>\n", body
);
4345 footer
= g_strdup_printf("</table></div></body></html>");
4347 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4351 * update all download manager tabs as the xtp session
4352 * key has now changed. No need to update the current tab.
4353 * Already did that above.
4355 update_download_tabs(t
);
4362 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4369 search(struct tab
*t
, struct karg
*args
)
4373 if (t
== NULL
|| args
== NULL
) {
4374 show_oops_s("search invalid parameters");
4377 if (t
->search_text
== NULL
) {
4378 if (global_search
== NULL
)
4379 return (XT_CB_PASSTHROUGH
);
4381 t
->search_text
= g_strdup(global_search
);
4382 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4383 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4387 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4388 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4391 case XT_SEARCH_NEXT
:
4392 d
= t
->search_forward
;
4394 case XT_SEARCH_PREV
:
4395 d
= !t
->search_forward
;
4398 return (XT_CB_PASSTHROUGH
);
4401 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4403 return (XT_CB_HANDLED
);
4406 struct settings_args
{
4412 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4415 struct settings_args
*sa
= cb_args
;
4420 if (s
->flags
& XT_SF_RUNTIME
)
4426 *sa
->body
= g_strdup_printf(
4428 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4429 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4441 set(struct tab
*t
, struct karg
*args
)
4443 char *header
, *body
, *footer
, *page
, *tmp
;
4445 struct settings_args sa
;
4447 bzero(&sa
, sizeof sa
);
4451 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4452 "\n<head><title>Settings</title>\n"
4453 "</head><body><h1>Settings</h1>\n");
4456 body
= g_strdup_printf("<div align='center'><table><tr>"
4457 "<th align='left'>Setting</th>"
4458 "<th align='left'>Value</th></tr>\n");
4460 settings_walk(print_setting
, &sa
);
4463 /* small message if there are none */
4466 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4467 "colspan='2'>No settings</td></tr>\n", body
);
4472 footer
= g_strdup_printf("</table></div></body></html>");
4474 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4480 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4482 return (XT_CB_PASSTHROUGH
);
4486 session_save(struct tab
*t
, char *filename
)
4491 if (strlen(filename
) == 0)
4494 if (filename
[0] == '.' || filename
[0] == '/')
4498 if (save_tabs(t
, &a
))
4500 strlcpy(named_session
, filename
, sizeof named_session
);
4508 session_open(struct tab
*t
, char *filename
)
4513 if (strlen(filename
) == 0)
4516 if (filename
[0] == '.' || filename
[0] == '/')
4520 a
.i
= XT_SES_CLOSETABS
;
4521 if (open_tabs(t
, &a
))
4524 strlcpy(named_session
, filename
, sizeof named_session
);
4532 session_delete(struct tab
*t
, char *filename
)
4534 char file
[PATH_MAX
];
4537 if (strlen(filename
) == 0)
4540 if (filename
[0] == '.' || filename
[0] == '/')
4543 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4547 if (!strcmp(filename
, named_session
))
4548 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4549 sizeof named_session
);
4557 session_cmd(struct tab
*t
, struct karg
*args
)
4559 char *filename
= args
->s
;
4564 if (args
->i
& XT_SHOW
)
4565 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4566 XT_SAVED_TABS_FILE
: named_session
);
4567 else if (args
->i
& XT_SAVE
) {
4568 if (session_save(t
, filename
)) {
4569 show_oops(t
, "Can't save session: %s",
4570 filename
? filename
: "INVALID");
4573 } else if (args
->i
& XT_OPEN
) {
4574 if (session_open(t
, filename
)) {
4575 show_oops(t
, "Can't open session: %s",
4576 filename
? filename
: "INVALID");
4579 } else if (args
->i
& XT_DELETE
) {
4580 if (session_delete(t
, filename
)) {
4581 show_oops(t
, "Can't delete session: %s",
4582 filename
? filename
: "INVALID");
4587 return (XT_CB_PASSTHROUGH
);
4591 * Make a hardcopy of the page
4594 print_page(struct tab
*t
, struct karg
*args
)
4596 WebKitWebFrame
*frame
;
4598 GtkPrintOperation
*op
;
4599 GtkPrintOperationAction action
;
4600 GtkPrintOperationResult print_res
;
4601 GError
*g_err
= NULL
;
4602 int marg_l
, marg_r
, marg_t
, marg_b
;
4604 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4606 ps
= gtk_page_setup_new();
4607 op
= gtk_print_operation_new();
4608 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4609 frame
= webkit_web_view_get_main_frame(t
->wv
);
4611 /* the default margins are too small, so we will bump them */
4612 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4613 XT_PRINT_EXTRA_MARGIN
;
4614 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4615 XT_PRINT_EXTRA_MARGIN
;
4616 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4617 XT_PRINT_EXTRA_MARGIN
;
4618 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4619 XT_PRINT_EXTRA_MARGIN
;
4622 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4623 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4624 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4625 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4627 gtk_print_operation_set_default_page_setup(op
, ps
);
4629 /* this appears to free 'op' and 'ps' */
4630 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4632 /* check it worked */
4633 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4634 show_oops_s("can't print: %s", g_err
->message
);
4635 g_error_free (g_err
);
4643 go_home(struct tab
*t
, struct karg
*args
)
4650 restart(struct tab
*t
, struct karg
*args
)
4654 a
.s
= XT_RESTART_TABS_FILE
;
4656 execvp(start_argv
[0], start_argv
);
4662 #define CTRL GDK_CONTROL_MASK
4663 #define MOD1 GDK_MOD1_MASK
4664 #define SHFT GDK_SHIFT_MASK
4666 /* inherent to GTK not all keys will be caught at all times */
4667 /* XXX sort key bindings */
4668 struct key_binding
{
4673 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4675 { "cookiejar", MOD1
, 0, GDK_j
},
4676 { "downloadmgr", MOD1
, 0, GDK_d
},
4677 { "history", MOD1
, 0, GDK_h
},
4678 { "print", CTRL
, 0, GDK_p
},
4679 { "search", 0, 0, GDK_slash
},
4680 { "searchb", 0, 0, GDK_question
},
4681 { "command", 0, 0, GDK_colon
},
4682 { "qa", CTRL
, 0, GDK_q
},
4683 { "restart", MOD1
, 0, GDK_q
},
4684 { "js toggle", CTRL
, 0, GDK_j
},
4685 { "cookie toggle", MOD1
, 0, GDK_c
},
4686 { "togglesrc", CTRL
, 0, GDK_s
},
4687 { "yankuri", 0, 0, GDK_y
},
4688 { "pasteuricur", 0, 0, GDK_p
},
4689 { "pasteurinew", 0, 0, GDK_P
},
4692 { "searchnext", 0, 0, GDK_n
},
4693 { "searchprevious", 0, 0, GDK_N
},
4696 { "focusaddress", 0, 0, GDK_F6
},
4697 { "focussearch", 0, 0, GDK_F7
},
4700 { "hinting", 0, 0, GDK_f
},
4702 /* custom stylesheet */
4703 { "userstyle", 0, 0, GDK_i
},
4706 { "goback", 0, 0, GDK_BackSpace
},
4707 { "goback", MOD1
, 0, GDK_Left
},
4708 { "goforward", SHFT
, 0, GDK_BackSpace
},
4709 { "goforward", MOD1
, 0, GDK_Right
},
4710 { "reload", 0, 0, GDK_F5
},
4711 { "reload", CTRL
, 0, GDK_r
},
4712 { "reloadforce", CTRL
, 0, GDK_R
},
4713 { "reload", CTRL
, 0, GDK_l
},
4714 { "favorites", MOD1
, 1, GDK_f
},
4716 /* vertical movement */
4717 { "scrolldown", 0, 0, GDK_j
},
4718 { "scrolldown", 0, 0, GDK_Down
},
4719 { "scrollup", 0, 0, GDK_Up
},
4720 { "scrollup", 0, 0, GDK_k
},
4721 { "scrollbottom", 0, 0, GDK_G
},
4722 { "scrollbottom", 0, 0, GDK_End
},
4723 { "scrolltop", 0, 0, GDK_Home
},
4724 { "scrolltop", 0, 0, GDK_g
},
4725 { "scrollpagedown", 0, 0, GDK_space
},
4726 { "scrollpagedown", CTRL
, 0, GDK_f
},
4727 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4728 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4729 { "scrollpageup", 0, 0, GDK_Page_Up
},
4730 { "scrollpageup", CTRL
, 0, GDK_b
},
4731 { "scrollhalfup", CTRL
, 0, GDK_u
},
4732 /* horizontal movement */
4733 { "scrollright", 0, 0, GDK_l
},
4734 { "scrollright", 0, 0, GDK_Right
},
4735 { "scrollleft", 0, 0, GDK_Left
},
4736 { "scrollleft", 0, 0, GDK_h
},
4737 { "scrollfarright", 0, 0, GDK_dollar
},
4738 { "scrollfarleft", 0, 0, GDK_0
},
4741 { "tabnew", CTRL
, 0, GDK_t
},
4742 { "tabclose", CTRL
, 1, GDK_w
},
4743 { "tabundoclose", 0, 0, GDK_U
},
4744 { "tabnext 1", CTRL
, 0, GDK_1
},
4745 { "tabnext 2", CTRL
, 0, GDK_2
},
4746 { "tabnext 3", CTRL
, 0, GDK_3
},
4747 { "tabnext 4", CTRL
, 0, GDK_4
},
4748 { "tabnext 5", CTRL
, 0, GDK_5
},
4749 { "tabnext 6", CTRL
, 0, GDK_6
},
4750 { "tabnext 7", CTRL
, 0, GDK_7
},
4751 { "tabnext 8", CTRL
, 0, GDK_8
},
4752 { "tabnext 9", CTRL
, 0, GDK_9
},
4753 { "tabnext 10", CTRL
, 0, GDK_0
},
4754 { "tabfirst", CTRL
, 0, GDK_less
},
4755 { "tablast", CTRL
, 0, GDK_greater
},
4756 { "tabprevious", CTRL
, 0, GDK_Left
},
4757 { "tabnext", CTRL
, 0, GDK_Right
},
4758 { "focusout", CTRL
, 0, GDK_minus
},
4759 { "focusin", CTRL
, 0, GDK_plus
},
4760 { "focusin", CTRL
, 0, GDK_equal
},
4762 /* command aliases (handy when -S flag is used) */
4763 { "promptopen", 0, 0, GDK_F9
},
4764 { "promptopencurrent", 0, 0, GDK_F10
},
4765 { "prompttabnew", 0, 0, GDK_F11
},
4766 { "prompttabnewcurrent",0, 0, GDK_F12
},
4768 TAILQ_HEAD(keybinding_list
, key_binding
);
4771 walk_kb(struct settings
*s
,
4772 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4774 struct key_binding
*k
;
4777 if (s
== NULL
|| cb
== NULL
) {
4778 show_oops_s("walk_kb invalid parameters");
4782 TAILQ_FOREACH(k
, &kbl
, entry
) {
4788 if (gdk_keyval_name(k
->key
) == NULL
)
4791 strlcat(str
, k
->cmd
, sizeof str
);
4792 strlcat(str
, ",", sizeof str
);
4794 if (k
->mask
& GDK_SHIFT_MASK
)
4795 strlcat(str
, "S-", sizeof str
);
4796 if (k
->mask
& GDK_CONTROL_MASK
)
4797 strlcat(str
, "C-", sizeof str
);
4798 if (k
->mask
& GDK_MOD1_MASK
)
4799 strlcat(str
, "M1-", sizeof str
);
4800 if (k
->mask
& GDK_MOD2_MASK
)
4801 strlcat(str
, "M2-", sizeof str
);
4802 if (k
->mask
& GDK_MOD3_MASK
)
4803 strlcat(str
, "M3-", sizeof str
);
4804 if (k
->mask
& GDK_MOD4_MASK
)
4805 strlcat(str
, "M4-", sizeof str
);
4806 if (k
->mask
& GDK_MOD5_MASK
)
4807 strlcat(str
, "M5-", sizeof str
);
4809 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4810 cb(s
, str
, cb_args
);
4815 init_keybindings(void)
4818 struct key_binding
*k
;
4820 for (i
= 0; i
< LENGTH(keys
); i
++) {
4821 k
= g_malloc0(sizeof *k
);
4822 k
->cmd
= keys
[i
].cmd
;
4823 k
->mask
= keys
[i
].mask
;
4824 k
->use_in_entry
= keys
[i
].use_in_entry
;
4825 k
->key
= keys
[i
].key
;
4826 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4828 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4829 k
->cmd
? k
->cmd
: "unnamed key");
4834 keybinding_clearall(void)
4836 struct key_binding
*k
, *next
;
4838 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4839 next
= TAILQ_NEXT(k
, entry
);
4843 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4844 k
->cmd
? k
->cmd
: "unnamed key");
4845 TAILQ_REMOVE(&kbl
, k
, entry
);
4851 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
4853 struct key_binding
*k
;
4854 guint keyval
, mask
= 0;
4857 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
4859 /* Keys which are to be used in entry have been prefixed with an
4860 * exclamation mark. */
4864 /* find modifier keys */
4865 if (strstr(key
, "S-"))
4866 mask
|= GDK_SHIFT_MASK
;
4867 if (strstr(key
, "C-"))
4868 mask
|= GDK_CONTROL_MASK
;
4869 if (strstr(key
, "M1-"))
4870 mask
|= GDK_MOD1_MASK
;
4871 if (strstr(key
, "M2-"))
4872 mask
|= GDK_MOD2_MASK
;
4873 if (strstr(key
, "M3-"))
4874 mask
|= GDK_MOD3_MASK
;
4875 if (strstr(key
, "M4-"))
4876 mask
|= GDK_MOD4_MASK
;
4877 if (strstr(key
, "M5-"))
4878 mask
|= GDK_MOD5_MASK
;
4881 for (i
= strlen(key
) - 1; i
> 0; i
--)
4885 /* validate keyname */
4886 keyval
= gdk_keyval_from_name(key
);
4887 if (keyval
== GDK_VoidSymbol
) {
4888 warnx("invalid keybinding name %s", key
);
4891 /* must run this test too, gtk+ doesn't handle 10 for example */
4892 if (gdk_keyval_name(keyval
) == NULL
) {
4893 warnx("invalid keybinding name %s", key
);
4897 /* Remove eventual dupes. */
4898 TAILQ_FOREACH(k
, &kbl
, entry
)
4899 if (k
->key
== keyval
&& k
->mask
== mask
) {
4900 TAILQ_REMOVE(&kbl
, k
, entry
);
4906 k
= g_malloc0(sizeof *k
);
4907 k
->cmd
= g_strdup(cmd
);
4909 k
->use_in_entry
= use_in_entry
;
4912 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4917 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4918 k
->cmd
, gdk_keyval_name(keyval
));
4920 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4926 add_kb(struct settings
*s
, char *entry
)
4930 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4932 /* clearall is special */
4933 if (!strcmp(entry
, "clearall")) {
4934 keybinding_clearall();
4938 kb
= strstr(entry
, ",");
4944 return (keybinding_add(entry
, key
, key
[0] == '!'));
4950 int (*func
)(struct tab
*, struct karg
*);
4954 { "command", 0, command
, ':', 0 },
4955 { "search", 0, command
, '/', 0 },
4956 { "searchb", 0, command
, '?', 0 },
4957 { "togglesrc", 0, toggle_src
, 0, 0 },
4959 /* yanking and pasting */
4960 { "yankuri", 0, yank_uri
, 0, 0 },
4961 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
4962 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
4963 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
4966 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
4967 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
4970 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
4971 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
4974 { "hinting", 0, hint
, 0, 0 },
4976 /* custom stylesheet */
4977 { "userstyle", 0, userstyle
, 0, 0 },
4980 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
4981 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
4982 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
4983 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
4985 /* vertical movement */
4986 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
4987 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
4988 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
4989 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
4990 { "1", 0, move
, XT_MOVE_TOP
, 0 },
4991 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
4992 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
4993 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
4994 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
4995 /* horizontal movement */
4996 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
4997 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
4998 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
4999 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5002 { "favorites", 0, xtp_page_fl
, 0, 0 },
5003 { "fav", 0, xtp_page_fl
, 0, 0 },
5004 { "favadd", 0, add_favorite
, 0, 0 },
5006 { "qall", 0, quit
, 0, 0 },
5007 { "quitall", 0, quit
, 0, 0 },
5008 { "w", 0, save_tabs
, 0, 0 },
5009 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5010 { "help", 0, help
, 0, 0 },
5011 { "about", 0, about
, 0, 0 },
5012 { "stats", 0, stats
, 0, 0 },
5013 { "version", 0, about
, 0, 0 },
5016 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5017 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5018 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5019 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5020 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5021 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5022 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5023 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5024 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5025 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5026 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5028 /* cookie command */
5029 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5030 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5031 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5032 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5033 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5034 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5035 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5036 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5037 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5038 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5039 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5042 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5045 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5046 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5047 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5049 { "ca", 0, ca_cmd
, 0, 0 },
5050 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5051 { "dl", 0, xtp_page_dl
, 0, 0 },
5052 { "h", 0, xtp_page_hl
, 0, 0 },
5053 { "history", 0, xtp_page_hl
, 0, 0 },
5054 { "home", 0, go_home
, 0, 0 },
5055 { "restart", 0, restart
, 0, 0 },
5056 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5057 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5058 { "statushide", 0, statusaction
, XT_STATUSBAR_HIDE
, 0 },
5059 { "statusshow", 0, statusaction
, XT_STATUSBAR_SHOW
, 0 },
5061 { "print", 0, print_page
, 0, 0 },
5064 { "focusin", 0, resizetab
, 1, 0 },
5065 { "focusout", 0, resizetab
, -1, 0 },
5066 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5067 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5068 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_USERARG
| XT_URLARG
},
5069 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_USERARG
},
5070 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_USERARG
| XT_URLARG
},
5071 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5072 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5073 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5074 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_USERARG
| XT_URLARG
},
5075 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_USERARG
},
5076 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_USERARG
},
5077 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5078 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5079 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5081 /* command aliases (handy when -S flag is used) */
5082 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5083 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5084 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5085 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5088 { "set", 0, set
, 0, 0 },
5089 { "fullscreen", 0, fullscreen
, 0, 0 },
5090 { "f", 0, fullscreen
, 0, 0 },
5093 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5094 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5095 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5096 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5097 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5104 } cmd_status
= {-1, 0};
5107 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5113 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5119 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5121 a
.i
= XT_NAV_FORWARD
;
5131 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5133 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5135 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5142 * cancel, remove, etc. downloads
5145 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5147 struct download find
, *d
= NULL
;
5149 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5151 /* some commands require a valid download id */
5152 if (cmd
!= XT_XTP_DL_LIST
) {
5153 /* lookup download in question */
5155 d
= RB_FIND(download_list
, &downloads
, &find
);
5158 show_oops(t
, "%s: no such download", __func__
);
5163 /* decide what to do */
5165 case XT_XTP_DL_CANCEL
:
5166 webkit_download_cancel(d
->download
);
5168 case XT_XTP_DL_REMOVE
:
5169 webkit_download_cancel(d
->download
); /* just incase */
5170 g_object_unref(d
->download
);
5171 RB_REMOVE(download_list
, &downloads
, d
);
5173 case XT_XTP_DL_LIST
:
5177 show_oops(t
, "%s: unknown command", __func__
);
5180 xtp_page_dl(t
, NULL
);
5184 * Actions on history, only does one thing for now, but
5185 * we provide the function for future actions
5188 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5190 struct history
*h
, *next
;
5194 case XT_XTP_HL_REMOVE
:
5195 /* walk backwards, as listed in reverse */
5196 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5197 next
= RB_PREV(history_list
, &hl
, h
);
5199 RB_REMOVE(history_list
, &hl
, h
);
5200 g_free((gpointer
) h
->title
);
5201 g_free((gpointer
) h
->uri
);
5208 case XT_XTP_HL_LIST
:
5209 /* Nothing - just xtp_page_hl() below */
5212 show_oops(t
, "%s: unknown command", __func__
);
5216 xtp_page_hl(t
, NULL
);
5219 /* remove a favorite */
5221 remove_favorite(struct tab
*t
, int index
)
5223 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5224 char *new_favs
, *tmp
;
5229 /* open favorites */
5230 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5232 if ((f
= fopen(file
, "r")) == NULL
) {
5233 show_oops(t
, "%s: can't open favorites: %s",
5234 __func__
, strerror(errno
));
5238 /* build a string which will become the new favroites file */
5239 new_favs
= g_strdup("");
5242 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5243 if (feof(f
) || ferror(f
))
5245 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5252 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5253 if (feof(f
) || ferror(f
)) {
5254 show_oops(t
, "%s: can't parse favorites %s",
5255 __func__
, strerror(errno
));
5260 /* as long as this isn't the one we are deleting add to file */
5263 new_favs
= g_strdup_printf("%s%s\n%s\n",
5264 new_favs
, title
, uri
);
5276 /* write back new favorites file */
5277 if ((f
= fopen(file
, "w")) == NULL
) {
5278 show_oops(t
, "%s: can't open favorites: %s",
5279 __func__
, strerror(errno
));
5283 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5296 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5299 case XT_XTP_FL_LIST
:
5300 /* nothing, just the below call to xtp_page_fl() */
5302 case XT_XTP_FL_REMOVE
:
5303 remove_favorite(t
, arg
);
5306 show_oops(t
, "%s: invalid favorites command", __func__
);
5310 xtp_page_fl(t
, NULL
);
5314 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5317 case XT_XTP_CL_LIST
:
5318 /* nothing, just xtp_page_cl() */
5320 case XT_XTP_CL_REMOVE
:
5324 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5328 xtp_page_cl(t
, NULL
);
5331 /* link an XTP class to it's session key and handler function */
5332 struct xtp_despatch
{
5335 void (*handle_func
)(struct tab
*, uint8_t, int);
5338 struct xtp_despatch xtp_despatches
[] = {
5339 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5340 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5341 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5342 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5343 { XT_XTP_INVALID
, NULL
, NULL
}
5347 * is the url xtp protocol? (xxxt://)
5348 * if so, parse and despatch correct bahvior
5351 parse_xtp_url(struct tab
*t
, const char *url
)
5353 char *dup
= NULL
, *p
, *last
;
5354 uint8_t n_tokens
= 0;
5355 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5356 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5361 * tokens array meaning:
5363 * tokens[1] = session key
5364 * tokens[2] = action
5365 * tokens[3] = optional argument
5368 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5370 /*xtp tab meaning is normal unless proven special */
5371 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5373 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5376 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5378 /* split out the url */
5379 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5380 (p
= strtok_r(NULL
, "/", &last
))) {
5382 tokens
[n_tokens
++] = p
;
5385 /* should be atleast three fields 'class/seskey/command/arg' */
5389 dsp
= xtp_despatches
;
5390 req_class
= atoi(tokens
[0]);
5391 while (dsp
->xtp_class
) {
5392 if (dsp
->xtp_class
== req_class
) {
5399 /* did we find one atall? */
5400 if (dsp_match
== NULL
) {
5401 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5405 /* check session key and call despatch function */
5406 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5407 ret
= TRUE
; /* all is well, this was a valid xtp request */
5408 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5421 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5423 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5425 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5428 show_oops_s("activate_uri_entry_cb invalid parameters");
5433 show_oops(t
, "activate_uri_entry_cb no uri");
5437 uri
+= strspn(uri
, "\t ");
5439 /* if xxxt:// treat specially */
5440 if (parse_xtp_url(t
, uri
))
5443 /* otherwise continue to load page normally */
5444 load_uri(t
, (gchar
*)uri
);
5449 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5451 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5452 char *newuri
= NULL
;
5455 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5458 show_oops_s("activate_search_entry_cb invalid parameters");
5462 if (search_string
== NULL
) {
5463 show_oops(t
, "no search_string");
5467 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5468 newuri
= g_strdup_printf(search_string
, enc_search
);
5471 webkit_web_view_load_uri(t
->wv
, newuri
);
5479 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5481 struct domain
*d
= NULL
;
5484 if (uri
== NULL
|| t
== NULL
)
5487 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5492 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5493 es
? "enable" : "disable", uri
);
5495 g_object_set(G_OBJECT(t
->settings
),
5496 "enable-scripts", es
, (char *)NULL
);
5497 g_object_set(G_OBJECT(t
->settings
),
5498 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5499 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5501 button_set_stockid(t
->js_toggle
,
5502 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5506 show_ca_status(struct tab
*t
, const char *uri
)
5508 WebKitWebFrame
*frame
;
5509 WebKitWebDataSource
*source
;
5510 WebKitNetworkRequest
*request
;
5511 SoupMessage
*message
;
5513 gchar
*col_str
= XT_COLOR_WHITE
;
5516 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5517 ssl_strict_certs
, ssl_ca_file
, uri
);
5521 if (ssl_ca_file
== NULL
) {
5522 if (g_str_has_prefix(uri
, "http://"))
5524 if (g_str_has_prefix(uri
, "https://")) {
5525 col_str
= XT_COLOR_RED
;
5530 if (g_str_has_prefix(uri
, "http://") ||
5531 !g_str_has_prefix(uri
, "https://"))
5534 frame
= webkit_web_view_get_main_frame(t
->wv
);
5535 source
= webkit_web_frame_get_data_source(frame
);
5536 request
= webkit_web_data_source_get_request(source
);
5537 message
= webkit_network_request_get_message(request
);
5539 if (message
&& (soup_message_get_flags(message
) &
5540 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5541 col_str
= XT_COLOR_GREEN
;
5544 r
= load_compare_cert(t
, NULL
);
5546 col_str
= XT_COLOR_BLUE
;
5548 col_str
= XT_COLOR_YELLOW
;
5550 col_str
= XT_COLOR_RED
;
5555 gdk_color_parse(col_str
, &color
);
5556 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5558 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5559 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5561 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5562 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5565 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5567 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5568 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5575 free_favicon(struct tab
*t
)
5577 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5578 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5580 if (t
->icon_request
)
5581 g_object_unref(t
->icon_request
);
5583 g_object_unref(t
->icon_pixbuf
);
5584 if (t
->icon_dest_uri
)
5585 g_free(t
->icon_dest_uri
);
5587 t
->icon_pixbuf
= NULL
;
5588 t
->icon_request
= NULL
;
5589 t
->icon_dest_uri
= NULL
;
5593 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5595 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5596 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5598 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5599 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5601 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5602 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5606 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5608 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5609 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5611 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5612 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5614 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5615 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5619 is_valid_icon(char *file
)
5622 const char *mime_type
;
5626 gf
= g_file_new_for_path(file
);
5627 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5629 mime_type
= g_file_info_get_content_type(fi
);
5630 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5631 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5632 g_strcmp0(mime_type
, "image/png") == 0 ||
5633 g_strcmp0(mime_type
, "image/gif") == 0 ||
5634 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5642 set_favicon_from_file(struct tab
*t
, char *file
)
5645 GdkPixbuf
*pixbuf
, *scaled
;
5648 if (t
== NULL
|| file
== NULL
)
5650 if (t
->icon_pixbuf
) {
5651 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5655 if (g_str_has_prefix(file
, "file://"))
5656 file
+= strlen("file://");
5657 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5659 if (!stat(file
, &sb
)) {
5660 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5661 /* corrupt icon so trash it */
5662 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5665 /* no need to set icon to default here */
5670 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5671 if (pixbuf
== NULL
) {
5672 xt_icon_from_name(t
, "text-html");
5676 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5678 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5679 __func__
, t
->tab_id
, width
, height
);
5681 if (width
> 16 || height
> 16) {
5682 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5683 GDK_INTERP_BILINEAR
);
5684 g_object_unref(pixbuf
);
5688 if (scaled
== NULL
) {
5689 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5690 GDK_INTERP_BILINEAR
);
5694 t
->icon_pixbuf
= scaled
;
5695 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5699 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5702 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5703 struct tab
*tt
= NULL
, *t
= NULL
;
5706 * find the webview instead of passing in the tab as it could have been
5707 * deleted from underneath us.
5709 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5718 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5719 __func__
, t
->tab_id
, status
);
5722 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5724 t
->icon_download
= NULL
;
5727 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5730 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5733 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5735 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5736 __func__
, t
->tab_id
);
5737 t
->icon_download
= NULL
;
5740 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5743 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5744 __func__
, t
->icon_dest_uri
);
5745 set_favicon_from_file(t
, t
->icon_dest_uri
);
5746 /* these will be freed post callback */
5747 t
->icon_request
= NULL
;
5748 t
->icon_download
= NULL
;
5756 abort_favicon_download(struct tab
*t
)
5758 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5760 if (t
->icon_download
) {
5761 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5762 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5763 webkit_download_cancel(t
->icon_download
);
5764 t
->icon_download
= NULL
;
5768 xt_icon_from_name(t
, "text-html");
5772 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5774 gchar
*name_hash
, file
[PATH_MAX
];
5777 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5779 if (uri
== NULL
|| t
== NULL
)
5782 if (t
->icon_request
) {
5783 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5787 /* check to see if we got the icon in cache */
5788 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5789 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5792 if (!stat(file
, &sb
)) {
5793 if (sb
.st_size
> 0) {
5794 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5796 set_favicon_from_file(t
, file
);
5800 /* corrupt icon so trash it */
5801 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5806 /* create download for icon */
5807 t
->icon_request
= webkit_network_request_new(uri
);
5808 if (t
->icon_request
== NULL
) {
5809 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5814 t
->icon_download
= webkit_download_new(t
->icon_request
);
5815 if (t
->icon_download
== NULL
) {
5816 fprintf(stderr
, "%s: icon_download", __func__
);
5820 /* we have to free icon_dest_uri later */
5821 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5822 webkit_download_set_destination_uri(t
->icon_download
,
5825 if (webkit_download_get_status(t
->icon_download
) ==
5826 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5827 fprintf(stderr
, "%s: download failed to start", __func__
);
5828 g_object_unref(t
->icon_request
);
5829 g_free(t
->icon_dest_uri
);
5830 t
->icon_request
= NULL
;
5831 t
->icon_dest_uri
= NULL
;
5835 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5836 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5838 webkit_download_start(t
->icon_download
);
5842 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5844 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5845 struct history
*h
, find
;
5846 const gchar
*s_loading
;
5849 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
5850 webkit_web_view_get_load_status(wview
), get_uri(wview
) ? get_uri(wview
) : "NOTHING");
5853 show_oops_s("notify_load_status_cb invalid paramters");
5857 switch (webkit_web_view_get_load_status(wview
)) {
5858 case WEBKIT_LOAD_PROVISIONAL
:
5860 abort_favicon_download(t
);
5861 #if GTK_CHECK_VERSION(2, 20, 0)
5862 gtk_widget_show(t
->spinner
);
5863 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5865 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5867 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5873 case WEBKIT_LOAD_COMMITTED
:
5875 if ((uri
= get_uri(wview
)) != NULL
) {
5876 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5877 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5883 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5886 /* check if js white listing is enabled */
5887 if (enable_js_whitelist
) {
5888 uri
= get_uri(wview
);
5889 check_and_set_js(uri
, t
);
5895 show_ca_status(t
, uri
);
5897 /* we know enough to autosave the session */
5898 if (session_autosave
) {
5904 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5908 case WEBKIT_LOAD_FINISHED
:
5910 uri
= get_uri(wview
);
5914 if (!strncmp(uri
, "http://", strlen("http://")) ||
5915 !strncmp(uri
, "https://", strlen("https://")) ||
5916 !strncmp(uri
, "file://", strlen("file://"))) {
5918 h
= RB_FIND(history_list
, &hl
, &find
);
5920 title
= webkit_web_view_get_title(wview
);
5921 set
= title
? title
: uri
;
5922 h
= g_malloc(sizeof *h
);
5923 h
->uri
= g_strdup(uri
);
5924 h
->title
= g_strdup(set
);
5925 RB_INSERT(history_list
, &hl
, h
);
5926 completion_add_uri(h
->uri
);
5927 update_history_tabs(NULL
);
5931 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5932 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5933 case WEBKIT_LOAD_FAILED
:
5936 #if GTK_CHECK_VERSION(2, 20, 0)
5937 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5938 gtk_widget_hide(t
->spinner
);
5940 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5941 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5942 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5944 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5949 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5951 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5952 webkit_web_view_can_go_back(wview
));
5954 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5955 webkit_web_view_can_go_forward(wview
));
5957 /* take focus if we are visible */
5962 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5964 const gchar
*set
= NULL
, *title
= NULL
;
5966 title
= webkit_web_view_get_title(wview
);
5967 set
= title
? title
: get_uri(wview
);
5969 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5970 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5972 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5973 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
5978 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5980 run_script(t
, JS_HINTING
);
5984 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5986 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5987 progress
== 100 ? 0 : (double)progress
/ 100);
5988 if (show_url
== 0) {
5989 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5990 progress
== 100 ? 0 : (double)progress
/ 100);
5995 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5996 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5997 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6000 WebKitWebNavigationReason reason
;
6001 struct domain
*d
= NULL
;
6004 show_oops_s("webview_npd_cb invalid parameters");
6008 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6010 webkit_network_request_get_uri(request
));
6012 uri
= (char *)webkit_network_request_get_uri(request
);
6014 /* if this is an xtp url, we don't load anything else */
6015 if (parse_xtp_url(t
, uri
))
6018 if (t
->ctrl_click
) {
6020 create_new_tab(uri
, NULL
, ctrl_click_focus
);
6021 webkit_web_policy_decision_ignore(pd
);
6022 return (TRUE
); /* we made the decission */
6026 * This is a little hairy but it comes down to this:
6027 * when we run in whitelist mode we have to assist the browser in
6028 * opening the URL that it would have opened in a new tab.
6030 reason
= webkit_web_navigation_action_get_reason(na
);
6031 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6032 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6033 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6036 webkit_web_policy_decision_use(pd
);
6037 return (TRUE
); /* we made the decission */
6044 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6047 struct domain
*d
= NULL
;
6049 WebKitWebView
*webview
= NULL
;
6051 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6052 webkit_web_view_get_uri(wv
));
6055 /* open in current tab */
6057 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6058 uri
= webkit_web_view_get_uri(wv
);
6059 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6062 tt
= create_new_tab(NULL
, NULL
, 1);
6064 } else if (enable_scripts
== 1) {
6065 tt
= create_new_tab(NULL
, NULL
, 1);
6073 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6076 struct domain
*d
= NULL
;
6078 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6080 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6081 uri
= webkit_web_view_get_uri(wv
);
6082 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6086 } else if (enable_scripts
== 1)
6093 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6095 /* we can not eat the event without throwing gtk off so defer it */
6097 /* catch middle click */
6098 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6103 /* catch ctrl click */
6104 if (e
->type
== GDK_BUTTON_RELEASE
&&
6105 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6110 return (XT_CB_PASSTHROUGH
);
6114 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6116 struct mime_type
*m
;
6118 m
= find_mime_type(mime_type
);
6126 show_oops(t
, "can't fork mime handler");
6135 execlp(m
->mt_action
, m
->mt_action
,
6136 webkit_network_request_get_uri(request
), (void *)NULL
);
6145 get_mime_type(char *file
)
6147 const char *mime_type
;
6151 if (g_str_has_prefix(file
, "file://"))
6152 file
+= strlen("file://");
6154 gf
= g_file_new_for_path(file
);
6155 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6157 mime_type
= g_file_info_get_content_type(fi
);
6165 run_download_mimehandler(char *mime_type
, char *file
)
6167 struct mime_type
*m
;
6169 m
= find_mime_type(mime_type
);
6175 show_oops_s("can't fork download mime handler");
6184 if (g_str_has_prefix(file
, "file://"))
6185 file
+= strlen("file://");
6186 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6195 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6198 WebKitDownloadStatus status
;
6199 const gchar
*file
= NULL
, *mime
= NULL
;
6201 if (download
== NULL
)
6203 status
= webkit_download_get_status(download
);
6204 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6207 file
= webkit_download_get_destination_uri(download
);
6210 mime
= get_mime_type((char *)file
);
6214 run_download_mimehandler((char *)mime
, (char *)file
);
6218 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6219 WebKitNetworkRequest
*request
, char *mime_type
,
6220 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6223 show_oops_s("webview_mimetype_cb invalid parameters");
6227 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6228 t
->tab_id
, mime_type
);
6230 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6231 webkit_web_policy_decision_ignore(decision
);
6236 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6237 webkit_web_policy_decision_download(decision
);
6245 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6248 const gchar
*filename
;
6250 struct download
*download_entry
;
6253 if (wk_download
== NULL
|| t
== NULL
) {
6254 show_oops_s("%s invalid parameters", __func__
);
6258 filename
= webkit_download_get_suggested_filename(wk_download
);
6259 if (filename
== NULL
)
6260 return (FALSE
); /* abort download */
6262 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
6264 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6265 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6267 webkit_download_set_destination_uri(wk_download
, uri
);
6269 if (webkit_download_get_status(wk_download
) ==
6270 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6271 show_oops(t
, "%s: download failed to start", __func__
);
6273 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6275 /* connect "download first" mime handler */
6276 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6277 G_CALLBACK(download_status_changed_cb
), NULL
);
6279 download_entry
= g_malloc(sizeof(struct download
));
6280 download_entry
->download
= wk_download
;
6281 download_entry
->tab
= t
;
6282 download_entry
->id
= next_download_id
++;
6283 RB_INSERT(download_list
, &downloads
, download_entry
);
6284 /* get from history */
6285 g_object_ref(wk_download
);
6286 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6287 show_oops(t
, "Download of '%s' started...",
6288 basename(webkit_download_get_destination_uri(wk_download
)));
6294 /* sync other download manager tabs */
6295 update_download_tabs(NULL
);
6298 * NOTE: never redirect/render the current tab before this
6299 * function returns. This will cause the download to never start.
6301 return (ret
); /* start download */
6305 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6307 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6310 show_oops_s("webview_hover_cb");
6315 set_status(t
, uri
, XT_STATUS_LINK
);
6318 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6323 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6325 struct key_binding
*k
;
6327 TAILQ_FOREACH(k
, &kbl
, entry
)
6328 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6330 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6331 return (cmd_execute(t
, k
->cmd
));
6332 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6333 return (cmd_execute(t
, k
->cmd
));
6337 return (XT_CB_PASSTHROUGH
);
6341 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6343 char s
[2], buf
[128];
6344 const char *errstr
= NULL
;
6347 /* don't use w directly; use t->whatever instead */
6350 show_oops_s("wv_keypress_after_cb");
6351 return (XT_CB_PASSTHROUGH
);
6354 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6355 e
->keyval
, e
->state
, t
);
6359 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6361 return (XT_CB_HANDLED
);
6365 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6366 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6368 /* we have a string */
6370 /* we have a number */
6371 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6379 /* XXX unfuck this */
6380 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6381 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6382 /* last input was numerical */
6384 l
= strlen(t
->hint_num
);
6391 t
->hint_num
[l
] = '\0';
6395 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6396 /* last input was alphanumerical */
6398 l
= strlen(t
->hint_buf
);
6405 t
->hint_buf
[l
] = '\0';
6415 /* numerical input */
6416 if (CLEAN(e
->state
) == 0 &&
6417 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6418 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6419 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6420 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6423 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6425 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6428 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6430 t
->hint_mode
= XT_HINT_NUMERICAL
;
6434 /* empty the counter buffer */
6435 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6436 return (XT_CB_HANDLED
);
6439 /* alphanumerical input */
6441 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6442 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6443 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6444 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6445 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6446 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6447 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6450 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6453 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6455 t
->hint_mode
= XT_HINT_ALPHANUM
;
6458 /* empty the counter buffer */
6459 bzero(t
->hint_num
, sizeof t
->hint_num
);
6460 return (XT_CB_HANDLED
);
6463 return (XT_CB_HANDLED
);
6466 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6467 if (CLEAN(e
->state
) == 0 && isdigit(s
[0])) {
6468 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
6472 return (handle_keypress(t
, e
, 0));
6476 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6480 return (XT_CB_PASSTHROUGH
);
6484 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6486 const gchar
*c
= gtk_entry_get_text(w
);
6490 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6491 e
->keyval
, e
->state
, t
);
6494 show_oops_s("cmd_keyrelease_cb invalid parameters");
6495 return (XT_CB_PASSTHROUGH
);
6498 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6499 e
->keyval
, e
->state
, t
);
6503 if (strlen(c
) == 1) {
6504 webkit_web_view_unmark_text_matches(t
->wv
);
6510 else if (c
[0] == '?')
6516 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6518 /* not found, mark red */
6519 gdk_color_parse(XT_COLOR_RED
, &color
);
6520 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6521 /* unmark and remove selection */
6522 webkit_web_view_unmark_text_matches(t
->wv
);
6523 /* my kingdom for a way to unselect text in webview */
6525 /* found, highlight all */
6526 webkit_web_view_unmark_text_matches(t
->wv
);
6527 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6528 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6529 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6530 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6533 return (XT_CB_PASSTHROUGH
);
6537 match_uri(const gchar
*uri
, const gchar
*key
) {
6540 gboolean match
= FALSE
;
6544 if (!strncmp(key
, uri
, len
))
6547 voffset
= strstr(uri
, "/") + 2;
6548 if (!strncmp(key
, voffset
, len
))
6550 else if (g_str_has_prefix(voffset
, "www.")) {
6551 voffset
= voffset
+ strlen("www.");
6552 if (!strncmp(key
, voffset
, len
))
6561 cmd_getlist(int id
, char *key
)
6566 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
6567 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6568 if (match_uri(h
->uri
, key
)) {
6569 cmd_status
.list
[c
] = (char *)h
->uri
;
6578 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6580 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6581 if(cmds
[i
].level
< dep
)
6583 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6584 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6592 cmd_getnext(int dir
)
6594 cmd_status
.index
+= dir
;
6596 if (cmd_status
.index
< 0)
6597 cmd_status
.index
= cmd_status
.len
- 1;
6598 else if (cmd_status
.index
>= cmd_status
.len
)
6599 cmd_status
.index
= 0;
6601 return cmd_status
.list
[cmd_status
.index
];
6605 cmd_tokenize(char *s
, char *tokens
[])
6609 size_t len
= strlen(s
);
6610 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6612 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6622 cmd_complete(struct tab
*t
, char *str
, int dir
)
6624 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6625 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1, matchcount
= 0;
6626 char *tok
, *match
, *s
= g_strdup(str
);
6628 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6631 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
6634 for (i
= 0; isdigit(s
[i
]); i
++)
6637 for (; isspace(s
[i
]); i
++)
6642 levels
= cmd_tokenize(s
, tokens
);
6644 for (i
= 0; i
< levels
- 1; i
++) {
6647 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6648 if (cmds
[j
].level
< dep
)
6650 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, strlen(tok
))) {
6653 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
6660 if (matchcount
== 1) {
6661 strlcat(res
, tok
, sizeof res
);
6662 strlcat(res
, " ", sizeof res
);
6672 if (cmd_status
.index
== -1)
6673 cmd_getlist(parent
, tokens
[i
]);
6675 if (cmd_status
.len
> 0) {
6676 match
= cmd_getnext(dir
);
6677 strlcat(res
, match
, sizeof res
);
6678 gtk_entry_set_text(w
, res
);
6679 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6686 cmd_execute(struct tab
*t
, char *str
)
6688 struct cmd
*cmd
= NULL
;
6689 char *tok
, *last
, *s
= g_strdup(str
), *sc
, prefixstr
[4];
6690 int j
, len
, c
= 0, dep
= 0, matchcount
= 0, prefix
= -1;
6691 struct karg arg
= {0, NULL
, -1};
6692 int rv
= XT_CB_PASSTHROUGH
;
6697 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
6703 while (isspace(s
[0]))
6706 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
6707 prefix
= atoi(prefixstr
);
6711 for (tok
= strtok_r(s
, " ", &last
); tok
;
6712 tok
= strtok_r(NULL
, " ", &last
)) {
6714 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6715 if (cmds
[j
].level
< dep
)
6717 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1: strlen(tok
);
6718 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, len
)) {
6722 if (len
== strlen(cmds
[j
].cmd
)) {
6728 if (matchcount
== 1) {
6729 if (cmd
->type
& XT_USERARG
)
6733 show_oops(t
, "Invalid command: %s", str
);
6742 else if (cmd_prefix
> 0)
6745 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
6746 show_oops(t
, "No Prefix allowed: %s", str
);
6750 if (cmd
->type
& XT_USERARG
)
6751 arg
.s
= last
? g_strdup(last
) : g_strdup("");
6753 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
6769 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6772 show_oops_s("entry_key_cb invalid parameters");
6773 return (XT_CB_PASSTHROUGH
);
6776 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6777 e
->keyval
, e
->state
, t
);
6781 if (e
->keyval
== GDK_Escape
) {
6782 /* don't use focus_webview(t) because we want to type :cmds */
6783 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6786 return (handle_keypress(t
, e
, 1));
6790 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6792 int rv
= XT_CB_HANDLED
;
6793 const gchar
*c
= gtk_entry_get_text(w
);
6796 show_oops_s("cmd_keypress_cb parameters");
6797 return (XT_CB_PASSTHROUGH
);
6800 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6801 e
->keyval
, e
->state
, t
);
6805 e
->keyval
= GDK_Escape
;
6806 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6807 e
->keyval
= GDK_Escape
;
6809 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
6810 cmd_status
.index
= -1;
6812 switch (e
->keyval
) {
6815 cmd_complete(t
, (char *)&c
[1], 1);
6817 case GDK_ISO_Left_Tab
:
6819 cmd_complete(t
, (char *)&c
[1], -1);
6823 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6831 if (c
[0] == '/' || c
[0] == '?')
6832 webkit_web_view_unmark_text_matches(t
->wv
);
6836 rv
= XT_CB_PASSTHROUGH
;
6842 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6845 show_oops_s("cmd_focusout_cb invalid parameters");
6846 return (XT_CB_PASSTHROUGH
);
6848 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6853 if (show_url
== 0 || t
->focus_wv
)
6856 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6858 return (XT_CB_PASSTHROUGH
);
6862 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6865 const gchar
*c
= gtk_entry_get_text(entry
);
6868 show_oops_s("cmd_activate_cb invalid parameters");
6872 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6879 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6885 if (c
[0] == '/' || c
[0] == '?') {
6886 if (t
->search_text
) {
6887 g_free(t
->search_text
);
6888 t
->search_text
= NULL
;
6891 t
->search_text
= g_strdup(s
);
6893 g_free(global_search
);
6894 global_search
= g_strdup(s
);
6895 t
->search_forward
= c
[0] == '/';
6907 backward_cb(GtkWidget
*w
, struct tab
*t
)
6912 show_oops_s("backward_cb invalid parameters");
6916 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6923 forward_cb(GtkWidget
*w
, struct tab
*t
)
6928 show_oops_s("forward_cb invalid parameters");
6932 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6934 a
.i
= XT_NAV_FORWARD
;
6939 home_cb(GtkWidget
*w
, struct tab
*t
)
6942 show_oops_s("home_cb invalid parameters");
6946 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
6952 stop_cb(GtkWidget
*w
, struct tab
*t
)
6954 WebKitWebFrame
*frame
;
6957 show_oops_s("stop_cb invalid parameters");
6961 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6963 frame
= webkit_web_view_get_main_frame(t
->wv
);
6964 if (frame
== NULL
) {
6965 show_oops(t
, "stop_cb: no frame");
6969 webkit_web_frame_stop_loading(frame
);
6970 abort_favicon_download(t
);
6974 setup_webkit(struct tab
*t
)
6976 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
6977 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
6978 FALSE
, (char *)NULL
);
6980 warnx("webkit does not have \"enable-dns-prefetching\" property");
6981 g_object_set(G_OBJECT(t
->settings
),
6982 "user-agent", t
->user_agent
, (char *)NULL
);
6983 g_object_set(G_OBJECT(t
->settings
),
6984 "enable-scripts", enable_scripts
, (char *)NULL
);
6985 g_object_set(G_OBJECT(t
->settings
),
6986 "enable-plugins", enable_plugins
, (char *)NULL
);
6987 g_object_set(G_OBJECT(t
->settings
),
6988 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
6989 g_object_set(G_OBJECT(t
->wv
),
6990 "full-content-zoom", TRUE
, (char *)NULL
);
6991 adjustfont_webkit(t
, XT_FONT_SET
);
6993 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6997 create_browser(struct tab
*t
)
7003 show_oops_s("create_browser invalid parameters");
7007 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7008 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7009 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7010 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7012 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7013 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7014 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7016 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7017 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7020 t
->settings
= webkit_web_settings_new();
7022 if (user_agent
== NULL
) {
7023 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7025 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7028 t
->user_agent
= g_strdup(user_agent
);
7030 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7042 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7043 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7044 gtk_widget_set_name(w
, "xxxterm");
7045 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7046 g_signal_connect(G_OBJECT(w
), "delete_event",
7047 G_CALLBACK (gtk_main_quit
), NULL
);
7053 create_kiosk_toolbar(struct tab
*t
)
7055 GtkWidget
*toolbar
= NULL
, *b
;
7057 b
= gtk_hbox_new(FALSE
, 0);
7059 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7061 /* backward button */
7062 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7063 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7064 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7065 G_CALLBACK(backward_cb
), t
);
7066 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7068 /* forward button */
7069 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7070 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7071 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7072 G_CALLBACK(forward_cb
), t
);
7073 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7076 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7077 gtk_widget_set_sensitive(t
->gohome
, true);
7078 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7079 G_CALLBACK(home_cb
), t
);
7080 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7082 /* create widgets but don't use them */
7083 t
->uri_entry
= gtk_entry_new();
7084 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7085 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7086 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7092 create_toolbar(struct tab
*t
)
7094 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7096 b
= gtk_hbox_new(FALSE
, 0);
7098 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7101 /* backward button */
7102 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7103 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7104 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7105 G_CALLBACK(backward_cb
), t
);
7106 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7108 /* forward button */
7109 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7110 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7111 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7112 G_CALLBACK(forward_cb
), t
);
7113 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7117 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7118 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7119 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7120 G_CALLBACK(stop_cb
), t
);
7121 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7125 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7126 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7127 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7128 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7129 G_CALLBACK(js_toggle_cb
), t
);
7130 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7133 t
->uri_entry
= gtk_entry_new();
7134 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7135 G_CALLBACK(activate_uri_entry_cb
), t
);
7136 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7137 G_CALLBACK(entry_key_cb
), t
);
7139 eb1
= gtk_hbox_new(FALSE
, 0);
7140 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7141 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7142 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7145 if (fancy_bar
&& search_string
) {
7147 t
->search_entry
= gtk_entry_new();
7148 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7149 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7150 G_CALLBACK(activate_search_entry_cb
), t
);
7151 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7152 G_CALLBACK(entry_key_cb
), t
);
7153 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7154 eb2
= gtk_hbox_new(FALSE
, 0);
7155 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7156 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7158 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7168 TAILQ_FOREACH(t
, &tabs
, entry
)
7169 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7173 undo_close_tab_save(struct tab
*t
)
7177 struct undo
*u1
, *u2
;
7179 WebKitWebHistoryItem
*item
;
7181 if ((uri
= get_uri(t
->wv
)) == NULL
)
7184 u1
= g_malloc0(sizeof(struct undo
));
7185 u1
->uri
= g_strdup(uri
);
7187 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7189 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7190 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7193 /* forward history */
7194 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7198 u1
->history
= g_list_prepend(u1
->history
,
7199 webkit_web_history_item_copy(item
));
7200 items
= g_list_next(items
);
7205 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7206 u1
->history
= g_list_prepend(u1
->history
,
7207 webkit_web_history_item_copy(item
));
7211 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7215 u1
->history
= g_list_prepend(u1
->history
,
7216 webkit_web_history_item_copy(item
));
7217 items
= g_list_next(items
);
7220 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7222 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7223 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7224 TAILQ_REMOVE(&undos
, u2
, entry
);
7226 g_list_free(u2
->history
);
7235 delete_tab(struct tab
*t
)
7239 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7244 TAILQ_REMOVE(&tabs
, t
, entry
);
7246 /* halt all webkit activity */
7247 abort_favicon_download(t
);
7248 webkit_web_view_stop_loading(t
->wv
);
7249 undo_close_tab_save(t
);
7251 if (browser_mode
== XT_BM_KIOSK
) {
7252 gtk_widget_destroy(t
->uri_entry
);
7253 gtk_widget_destroy(t
->stop
);
7254 gtk_widget_destroy(t
->js_toggle
);
7257 gtk_widget_destroy(t
->vbox
);
7258 g_free(t
->user_agent
);
7259 g_free(t
->stylesheet
);
7262 if (TAILQ_EMPTY(&tabs
)) {
7263 if (browser_mode
== XT_BM_KIOSK
)
7264 create_new_tab(home
, NULL
, 1);
7266 create_new_tab(NULL
, NULL
, 1);
7269 /* recreate session */
7270 if (session_autosave
) {
7277 adjustfont_webkit(struct tab
*t
, int adjust
)
7282 show_oops_s("adjustfont_webkit invalid parameters");
7286 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7287 if (adjust
== XT_FONT_SET
) {
7288 t
->font_size
= default_font_size
;
7289 zoom
= default_zoom_level
;
7290 t
->font_size
+= adjust
;
7291 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7292 t
->font_size
, (char *)NULL
);
7293 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7294 &t
->font_size
, (char *)NULL
);
7296 t
->font_size
+= adjust
;
7297 zoom
+= adjust
/25.0;
7302 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7303 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7307 append_tab(struct tab
*t
)
7312 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7313 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7317 create_new_tab(char *title
, struct undo
*u
, int focus
)
7320 int load
= 1, id
, notfound
;
7322 WebKitWebHistoryItem
*item
;
7326 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7328 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7329 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7333 t
= g_malloc0(sizeof *t
);
7335 if (title
== NULL
) {
7336 title
= "(untitled)";
7340 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7342 /* label + button for tab */
7343 b
= gtk_hbox_new(FALSE
, 0);
7346 #if GTK_CHECK_VERSION(2, 20, 0)
7347 t
->spinner
= gtk_spinner_new ();
7349 t
->label
= gtk_label_new(title
);
7350 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7351 gtk_widget_set_size_request(t
->label
, 100, 0);
7352 gtk_widget_set_size_request(b
, 130, 0);
7354 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7355 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7356 #if GTK_CHECK_VERSION(2, 20, 0)
7357 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7361 if (browser_mode
== XT_BM_KIOSK
)
7362 t
->toolbar
= create_kiosk_toolbar(t
);
7364 t
->toolbar
= create_toolbar(t
);
7366 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7369 t
->browser_win
= create_browser(t
);
7370 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7372 /* oops message for user feedback */
7373 t
->oops
= gtk_entry_new();
7374 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7375 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7376 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7377 gdk_color_parse(XT_COLOR_RED
, &color
);
7378 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7379 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7382 t
->cmd
= gtk_entry_new();
7383 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7384 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7385 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7388 t
->statusbar
= gtk_entry_new();
7389 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
7390 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
7391 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
7392 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7393 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7394 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7395 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7396 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
7398 /* xtp meaning is normal by default */
7399 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7401 /* set empty favicon */
7402 xt_icon_from_name(t
, "text-html");
7404 /* and show it all */
7405 gtk_widget_show_all(b
);
7406 gtk_widget_show_all(t
->vbox
);
7408 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7412 id
= gtk_notebook_get_current_page(notebook
);
7413 TAILQ_FOREACH(tt
, &tabs
, entry
) {
7414 if (tt
->tab_id
== id
) {
7416 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
7417 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
7427 #if GTK_CHECK_VERSION(2, 20, 0)
7428 /* turn spinner off if we are a new tab without uri */
7430 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7431 gtk_widget_hide(t
->spinner
);
7434 /* make notebook tabs reorderable */
7435 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7437 g_object_connect(G_OBJECT(t
->cmd
),
7438 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7439 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7440 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7441 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7444 /* reuse wv_button_cb to hide oops */
7445 g_object_connect(G_OBJECT(t
->oops
),
7446 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7449 g_object_connect(G_OBJECT(t
->wv
),
7450 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7451 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7452 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7453 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7454 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7455 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7456 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7457 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7458 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7459 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7460 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7461 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7462 #if WEBKIT_CHECK_VERSION(1, 1, 18)
7463 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7465 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7467 g_signal_connect(t
->wv
,
7468 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7469 g_signal_connect(t
->wv
,
7470 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7472 /* hijack the unused keys as if we were the browser */
7473 g_object_connect(G_OBJECT(t
->toolbar
),
7474 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7477 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7478 G_CALLBACK(tab_close_cb
), t
);
7483 url_set_visibility();
7484 statusbar_set_visibility();
7487 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7488 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7493 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7497 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7502 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7503 /* restore the tab's history */
7504 if (u
&& u
->history
) {
7508 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7509 items
= g_list_next(items
);
7512 item
= g_list_nth_data(u
->history
, u
->back
);
7514 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7517 g_list_free(u
->history
);
7519 webkit_web_back_forward_list_clear(t
->bfl
);
7525 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
7531 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7533 if (gtk_notebook_get_current_page(notebook
) == -1)
7536 TAILQ_FOREACH(t
, &tabs
, entry
) {
7537 if (t
->tab_id
== pn
) {
7538 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7541 uri
= webkit_web_view_get_title(t
->wv
);
7544 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7556 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
7563 menuitem_response(struct tab
*t
)
7565 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7569 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7571 GtkWidget
*menu
, *menu_items
;
7572 GdkEventButton
*bevent
;
7576 if (event
->type
== GDK_BUTTON_PRESS
) {
7577 bevent
= (GdkEventButton
*) event
;
7578 menu
= gtk_menu_new();
7580 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7581 if ((uri
= get_uri(ti
->wv
)) == NULL
)
7582 /* XXX make sure there is something to print */
7583 /* XXX add gui pages in here to look purdy */
7585 menu_items
= gtk_menu_item_new_with_label(uri
);
7586 gtk_menu_append(GTK_MENU (menu
), menu_items
);
7587 gtk_widget_show(menu_items
);
7589 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
7590 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
7594 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7595 bevent
->button
, bevent
->time
);
7597 /* unref object so it'll free itself when popped down */
7598 g_object_ref_sink(menu
);
7599 g_object_unref(menu
);
7601 return (TRUE
/* eat event */);
7604 return (FALSE
/* propagate */);
7608 icon_size_map(int icon_size
)
7610 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7611 icon_size
> GTK_ICON_SIZE_DIALOG
)
7612 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7618 create_button(char *name
, char *stockid
, int size
)
7620 GtkWidget
*button
, *image
;
7624 rcstring
= g_strdup_printf(
7625 "style \"%s-style\"\n"
7627 " GtkWidget::focus-padding = 0\n"
7628 " GtkWidget::focus-line-width = 0\n"
7632 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
7633 gtk_rc_parse_string(rcstring
);
7635 button
= gtk_button_new();
7636 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7637 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
7639 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7640 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7641 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7642 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7643 gtk_widget_set_name(button
, name
);
7644 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7650 button_set_stockid(GtkWidget
*button
, char *stockid
)
7654 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7655 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7656 gtk_button_set_image(GTK_BUTTON(button
), image
);
7665 char file
[PATH_MAX
];
7668 vbox
= gtk_vbox_new(FALSE
, 0);
7669 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7670 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7671 gtk_notebook_set_tab_hborder(notebook
, 0);
7672 gtk_notebook_set_tab_vborder(notebook
, 0);
7673 gtk_notebook_set_scrollable(notebook
, TRUE
);
7674 notebook_tab_set_visibility(notebook
);
7675 gtk_notebook_set_show_border(notebook
, FALSE
);
7676 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7678 abtn
= gtk_button_new();
7679 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7680 gtk_widget_set_size_request(arrow
, -1, -1);
7681 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7682 gtk_widget_set_size_request(abtn
, -1, 20);
7684 #if GTK_CHECK_VERSION(2, 20, 0)
7685 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7687 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7688 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7689 gtk_widget_set_size_request(vbox
, -1, -1);
7691 g_object_connect(G_OBJECT(notebook
),
7692 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7694 g_object_connect(G_OBJECT(notebook
),
7695 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
), NULL
,
7697 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7698 G_CALLBACK(arrow_cb
), NULL
);
7700 main_window
= create_window();
7701 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7702 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7705 for (i
= 0; i
< LENGTH(icons
); i
++) {
7706 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7707 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7708 l
= g_list_append(l
, pb
);
7710 gtk_window_set_default_icon_list(l
);
7712 gtk_widget_show_all(abtn
);
7713 gtk_widget_show_all(main_window
);
7717 set_hook(void **hook
, char *name
)
7720 errx(1, "set_hook");
7722 if (*hook
== NULL
) {
7723 *hook
= dlsym(RTLD_NEXT
, name
);
7725 errx(1, "can't hook %s", name
);
7729 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7731 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7733 g_return_val_if_fail(cookie1
, FALSE
);
7734 g_return_val_if_fail(cookie2
, FALSE
);
7736 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7737 !strcmp (cookie1
->value
, cookie2
->value
) &&
7738 !strcmp (cookie1
->path
, cookie2
->path
) &&
7739 !strcmp (cookie1
->domain
, cookie2
->domain
));
7743 transfer_cookies(void)
7746 SoupCookie
*sc
, *pc
;
7748 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7750 for (;cf
; cf
= cf
->next
) {
7752 sc
= soup_cookie_copy(pc
);
7753 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7756 soup_cookies_free(cf
);
7760 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7765 print_cookie("soup_cookie_jar_delete_cookie", c
);
7767 if (cookies_enabled
== 0)
7770 if (jar
== NULL
|| c
== NULL
)
7773 /* find and remove from persistent jar */
7774 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7776 for (;cf
; cf
= cf
->next
) {
7778 if (soup_cookie_equal(ci
, c
)) {
7779 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7784 soup_cookies_free(cf
);
7786 /* delete from session jar */
7787 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7791 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7793 struct domain
*d
= NULL
;
7797 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7798 jar
, p_cookiejar
, s_cookiejar
);
7800 if (cookies_enabled
== 0)
7803 /* see if we are up and running */
7804 if (p_cookiejar
== NULL
) {
7805 _soup_cookie_jar_add_cookie(jar
, cookie
);
7808 /* disallow p_cookiejar adds, shouldn't happen */
7809 if (jar
== p_cookiejar
)
7813 if (jar
== NULL
|| cookie
== NULL
)
7816 if (enable_cookie_whitelist
&&
7817 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7819 DNPRINTF(XT_D_COOKIE
,
7820 "soup_cookie_jar_add_cookie: reject %s\n",
7822 if (save_rejected_cookies
) {
7823 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7824 show_oops_s("can't open reject cookie file");
7827 fseek(r_cookie_f
, 0, SEEK_END
);
7828 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7829 cookie
->http_only
? "#HttpOnly_" : "",
7831 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7833 cookie
->secure
? "TRUE" : "FALSE",
7835 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7842 if (!allow_volatile_cookies
)
7846 if (cookie
->expires
== NULL
&& session_timeout
) {
7847 soup_cookie_set_expires(cookie
,
7848 soup_date_new_from_now(session_timeout
));
7849 print_cookie("modified add cookie", cookie
);
7852 /* see if we are white listed for persistence */
7853 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7854 /* add to persistent jar */
7855 c
= soup_cookie_copy(cookie
);
7856 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7857 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7860 /* add to session jar */
7861 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7862 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7868 char file
[PATH_MAX
];
7870 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7871 "soup_cookie_jar_add_cookie");
7872 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7873 "soup_cookie_jar_delete_cookie");
7875 if (cookies_enabled
== 0)
7879 * the following code is intricate due to overriding several libsoup
7881 * do not alter order of these operations.
7884 /* rejected cookies */
7885 if (save_rejected_cookies
)
7886 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
, XT_REJECT_FILE
);
7888 /* persistent cookies */
7889 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
7890 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7892 /* session cookies */
7893 s_cookiejar
= soup_cookie_jar_new();
7894 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7895 cookie_policy
, (void *)NULL
);
7898 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7902 setup_proxy(char *uri
)
7905 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7906 soup_uri_free(proxy_uri
);
7910 if (http_proxy
!= uri
) {
7917 http_proxy
= g_strdup(uri
);
7918 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7919 proxy_uri
= soup_uri_new(http_proxy
);
7920 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7925 send_cmd_to_socket(char *cmd
)
7928 struct sockaddr_un sa
;
7930 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7931 warnx("%s: socket", __func__
);
7935 sa
.sun_family
= AF_UNIX
;
7936 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7937 work_dir
, XT_SOCKET_FILE
);
7940 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7941 warnx("%s: connect", __func__
);
7945 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
7946 warnx("%s: send", __func__
);
7957 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7960 char str
[XT_MAX_URL_LENGTH
];
7961 socklen_t t
= sizeof(struct sockaddr_un
);
7962 struct sockaddr_un sa
;
7968 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7973 if (getpeereid(s
, &uid
, &gid
) == -1) {
7977 if (uid
!= getuid() || gid
!= getgid()) {
7978 warnx("unauthorized user");
7984 warnx("not a valid user");
7988 n
= recv(s
, str
, sizeof(str
), 0);
7992 tt
= TAILQ_LAST(&tabs
, tab_list
);
7993 cmd_execute(tt
, str
);
8000 struct sockaddr_un sa
;
8002 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8003 warn("is_running: socket");
8007 sa
.sun_family
= AF_UNIX
;
8008 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8009 work_dir
, XT_SOCKET_FILE
);
8012 /* connect to see if there is a listener */
8013 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
8014 rv
= 0; /* not running */
8016 rv
= 1; /* already running */
8027 struct sockaddr_un sa
;
8029 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8030 warn("build_socket: socket");
8034 sa
.sun_family
= AF_UNIX
;
8035 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8036 work_dir
, XT_SOCKET_FILE
);
8039 /* connect to see if there is a listener */
8040 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8041 /* no listener so we will */
8042 unlink(sa
.sun_path
);
8044 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8045 warn("build_socket: bind");
8049 if (listen(s
, 1) == -1) {
8050 warn("build_socket: listen");
8063 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8064 GtkTreeIter
*iter
, struct tab
*t
)
8068 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8076 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8077 GtkTreeIter
*iter
, struct tab
*t
)
8081 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8082 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
8083 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
8090 completion_add_uri(const gchar
*uri
)
8094 /* add uri to list_store */
8095 gtk_list_store_append(completion_model
, &iter
);
8096 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8100 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8101 GtkTreeIter
*iter
, gpointer user_data
)
8104 gboolean match
= FALSE
;
8106 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8112 match
= match_uri(value
, key
);
8119 completion_add(struct tab
*t
)
8121 /* enable completion for tab */
8122 t
->completion
= gtk_entry_completion_new();
8123 gtk_entry_completion_set_text_column(t
->completion
, 0);
8124 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8125 gtk_entry_completion_set_model(t
->completion
,
8126 GTK_TREE_MODEL(completion_model
));
8127 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8129 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8130 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
8131 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8132 G_CALLBACK(completion_select_cb
), t
);
8133 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
8134 G_CALLBACK(completion_hover_cb
), t
);
8141 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8146 main(int argc
, char *argv
[])
8149 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8150 char conf
[PATH_MAX
] = { '\0' };
8151 char file
[PATH_MAX
];
8152 char *env_proxy
= NULL
;
8155 struct sigaction sact
;
8156 gchar
*priority
= g_strdup("NORMAL");
8160 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8162 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8171 errx(0 , "Version: %s", version
);
8174 strlcpy(conf
, optarg
, sizeof(conf
));
8177 strlcpy(named_session
, optarg
, sizeof(named_session
));
8198 RB_INIT(&downloads
);
8202 TAILQ_INIT(&aliases
);
8208 gnutls_global_init();
8210 /* generate session keys for xtp pages */
8211 generate_xtp_session_key(&dl_session_key
);
8212 generate_xtp_session_key(&hl_session_key
);
8213 generate_xtp_session_key(&cl_session_key
);
8214 generate_xtp_session_key(&fl_session_key
);
8217 gtk_init(&argc
, &argv
);
8218 if (!g_thread_supported())
8219 g_thread_init(NULL
);
8222 bzero(&sact
, sizeof(sact
));
8223 sigemptyset(&sact
.sa_mask
);
8224 sact
.sa_handler
= sigchild
;
8225 sact
.sa_flags
= SA_NOCLDSTOP
;
8226 sigaction(SIGCHLD
, &sact
, NULL
);
8228 /* set download dir */
8229 pwd
= getpwuid(getuid());
8231 errx(1, "invalid user %d", getuid());
8232 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8234 /* set default string settings */
8235 home
= g_strdup("https://www.cyphertite.com");
8236 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8237 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8238 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
8240 /* read config file */
8241 if (strlen(conf
) == 0)
8242 snprintf(conf
, sizeof conf
, "%s/.%s",
8243 pwd
->pw_dir
, XT_CONF_FILE
);
8244 config_parse(conf
, 0);
8246 /* working directory */
8247 if (strlen(work_dir
) == 0)
8248 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8249 pwd
->pw_dir
, XT_DIR
);
8250 if (stat(work_dir
, &sb
)) {
8251 if (mkdir(work_dir
, S_IRWXU
) == -1)
8252 err(1, "mkdir work_dir");
8253 if (stat(work_dir
, &sb
))
8254 err(1, "stat work_dir");
8256 if (S_ISDIR(sb
.st_mode
) == 0)
8257 errx(1, "%s not a dir", work_dir
);
8258 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8259 warnx("fixing invalid permissions on %s", work_dir
);
8260 if (chmod(work_dir
, S_IRWXU
) == -1)
8264 /* icon cache dir */
8265 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8266 if (stat(cache_dir
, &sb
)) {
8267 if (mkdir(cache_dir
, S_IRWXU
) == -1)
8268 err(1, "mkdir cache_dir");
8269 if (stat(cache_dir
, &sb
))
8270 err(1, "stat cache_dir");
8272 if (S_ISDIR(sb
.st_mode
) == 0)
8273 errx(1, "%s not a dir", cache_dir
);
8274 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8275 warnx("fixing invalid permissions on %s", cache_dir
);
8276 if (chmod(cache_dir
, S_IRWXU
) == -1)
8281 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8282 if (stat(certs_dir
, &sb
)) {
8283 if (mkdir(certs_dir
, S_IRWXU
) == -1)
8284 err(1, "mkdir certs_dir");
8285 if (stat(certs_dir
, &sb
))
8286 err(1, "stat certs_dir");
8288 if (S_ISDIR(sb
.st_mode
) == 0)
8289 errx(1, "%s not a dir", certs_dir
);
8290 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8291 warnx("fixing invalid permissions on %s", certs_dir
);
8292 if (chmod(certs_dir
, S_IRWXU
) == -1)
8297 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8298 work_dir
, XT_SESSIONS_DIR
);
8299 if (stat(sessions_dir
, &sb
)) {
8300 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
8301 err(1, "mkdir sessions_dir");
8302 if (stat(sessions_dir
, &sb
))
8303 err(1, "stat sessions_dir");
8305 if (S_ISDIR(sb
.st_mode
) == 0)
8306 errx(1, "%s not a dir", sessions_dir
);
8307 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8308 warnx("fixing invalid permissions on %s", sessions_dir
);
8309 if (chmod(sessions_dir
, S_IRWXU
) == -1)
8312 /* runtime settings that can override config file */
8313 if (runtime_settings
[0] != '\0')
8314 config_parse(runtime_settings
, 1);
8317 if (!strcmp(download_dir
, pwd
->pw_dir
))
8318 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8319 if (stat(download_dir
, &sb
)) {
8320 if (mkdir(download_dir
, S_IRWXU
) == -1)
8321 err(1, "mkdir download_dir");
8322 if (stat(download_dir
, &sb
))
8323 err(1, "stat download_dir");
8325 if (S_ISDIR(sb
.st_mode
) == 0)
8326 errx(1, "%s not a dir", download_dir
);
8327 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8328 warnx("fixing invalid permissions on %s", download_dir
);
8329 if (chmod(download_dir
, S_IRWXU
) == -1)
8333 /* favorites file */
8334 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8335 if (stat(file
, &sb
)) {
8336 warnx("favorites file doesn't exist, creating it");
8337 if ((f
= fopen(file
, "w")) == NULL
)
8338 err(1, "favorites");
8343 session
= webkit_get_default_session();
8344 /* XXX ssl-priority property not quite available yet */
8345 if (is_g_object_setting(G_OBJECT(session
), "ssl-priority"))
8346 g_object_set(G_OBJECT(session
), "ssl-priority", priority
,
8349 warnx("session does not have \"ssl-priority\" property");
8354 if (stat(ssl_ca_file
, &sb
)) {
8355 warn("no CA file: %s", ssl_ca_file
);
8356 g_free(ssl_ca_file
);
8359 g_object_set(session
,
8360 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8361 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8366 env_proxy
= getenv("http_proxy");
8368 setup_proxy(env_proxy
);
8370 setup_proxy(http_proxy
);
8373 send_cmd_to_socket(argv
[0]);
8377 /* set some connection parameters */
8378 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8379 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8382 /* see if there is already an xxxterm running */
8383 if (single_instance
&& is_running()) {
8385 warnx("already running");
8391 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8392 send_cmd_to_socket(cmd
);
8402 /* uri completion */
8403 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8408 if (save_global_history
)
8409 restore_global_history();
8411 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8412 restore_saved_tabs();
8414 a
.s
= named_session
;
8415 a
.i
= XT_SES_DONOTHING
;
8416 open_tabs(NULL
, &a
);
8420 create_new_tab(argv
[0], NULL
, focus
);
8427 if (TAILQ_EMPTY(&tabs
))
8428 create_new_tab(home
, NULL
, 1);
8431 if ((s
= build_socket()) != -1)
8432 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
8436 gnutls_global_deinit();