2 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
4 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
5 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
6 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
7 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
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 static char *version
= XXXTERM_VERSION
;
26 /* hooked functions */
27 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
28 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
32 u_int32_t swm_debug
= 0
53 GCRY_THREAD_OPTION_PTHREAD_IMPL
;
65 RB_ENTRY(history
) entry
;
69 RB_HEAD(history_list
, history
);
72 TAILQ_ENTRY(session
) entry
;
75 TAILQ_HEAD(session_list
, session
);
78 RB_ENTRY(download
) entry
;
80 WebKitDownload
*download
;
83 RB_HEAD(download_list
, download
);
86 RB_ENTRY(domain
) entry
;
88 int handy
; /* app use */
90 RB_HEAD(domain_list
, domain
);
93 TAILQ_ENTRY(undo
) entry
;
96 int back
; /* Keeps track of how many back
97 * history items there are. */
99 TAILQ_HEAD(undo_tailq
, undo
);
103 TAILQ_ENTRY(sp
) entry
;
105 TAILQ_HEAD(sp_list
, sp
);
107 struct command_entry
{
109 TAILQ_ENTRY(command_entry
) entry
;
111 TAILQ_HEAD(command_list
, command_entry
);
113 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
114 int next_download_id
= 1;
117 #define XT_NAME ("XXXTerm")
118 #define XT_DIR (".xxxterm")
119 #define XT_CACHE_DIR ("cache")
120 #define XT_CERT_DIR ("certs/")
121 #define XT_SESSIONS_DIR ("sessions/")
122 #define XT_CONF_FILE ("xxxterm.conf")
123 #define XT_FAVS_FILE ("favorites")
124 #define XT_QMARKS_FILE ("quickmarks")
125 #define XT_SAVED_TABS_FILE ("main_session")
126 #define XT_RESTART_TABS_FILE ("restart_tabs")
127 #define XT_SOCKET_FILE ("socket")
128 #define XT_HISTORY_FILE ("history")
129 #define XT_REJECT_FILE ("rejected.txt")
130 #define XT_COOKIE_FILE ("cookies.txt")
131 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
132 #define XT_SEARCH_FILE ("search_history")
133 #define XT_COMMAND_FILE ("command_history")
134 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
135 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
136 #define XT_DLMAN_REFRESH "10"
137 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
138 "td{overflow: hidden;" \
139 " padding: 2px 2px 2px 2px;" \
140 " border: 1px solid black;" \
141 " vertical-align:top;" \
142 " word-wrap: break-word}\n" \
143 "tr:hover{background: #ffff99}\n" \
144 "th{background-color: #cccccc;" \
145 " border: 1px solid black}\n" \
146 "table{width: 100%%;" \
147 " border: 1px black solid;" \
148 " border-collapse:collapse}\n" \
150 "border: 1px solid black;" \
153 ".progress-inner{float: left;" \
155 " background: green}\n" \
156 ".dlstatus{font-size: small;" \
157 " text-align: center}\n" \
159 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
160 #define XT_MAX_UNDO_CLOSE_TAB (32)
161 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
162 #define XT_PRINT_EXTRA_MARGIN 10
163 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
164 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
167 #define XT_COLOR_RED "#cc0000"
168 #define XT_COLOR_YELLOW "#ffff66"
169 #define XT_COLOR_BLUE "lightblue"
170 #define XT_COLOR_GREEN "#99ff66"
171 #define XT_COLOR_WHITE "white"
172 #define XT_COLOR_BLACK "black"
174 #define XT_COLOR_CT_BACKGROUND "#000000"
175 #define XT_COLOR_CT_INACTIVE "#dddddd"
176 #define XT_COLOR_CT_ACTIVE "#bbbb00"
177 #define XT_COLOR_CT_SEPARATOR "#555555"
179 #define XT_COLOR_SB_SEPARATOR "#555555"
181 #define XT_PROTO_DELIM "://"
184 * xxxterm "protocol" (xtp)
185 * We use this for managing stuff like downloads and favorites. They
186 * make magical HTML pages in memory which have xxxt:// links in order
187 * to communicate with xxxterm's internals. These links take the format:
188 * xxxt://class/session_key/action/arg
190 * Don't begin xtp class/actions as 0. atoi returns that on error.
192 * Typically we have not put addition of items in this framework, as
193 * adding items is either done via an ex-command or via a keybinding instead.
196 #define XT_XTP_STR "xxxt://"
198 /* XTP classes (xxxt://<class>) */
199 #define XT_XTP_INVALID 0 /* invalid */
200 #define XT_XTP_DL 1 /* downloads */
201 #define XT_XTP_HL 2 /* history */
202 #define XT_XTP_CL 3 /* cookies */
203 #define XT_XTP_FL 4 /* favorites */
205 /* XTP download actions */
206 #define XT_XTP_DL_LIST 1
207 #define XT_XTP_DL_CANCEL 2
208 #define XT_XTP_DL_REMOVE 3
210 /* XTP history actions */
211 #define XT_XTP_HL_LIST 1
212 #define XT_XTP_HL_REMOVE 2
214 /* XTP cookie actions */
215 #define XT_XTP_CL_LIST 1
216 #define XT_XTP_CL_REMOVE 2
218 /* XTP cookie actions */
219 #define XT_XTP_FL_LIST 1
220 #define XT_XTP_FL_REMOVE 2
223 #define XT_MOVE_INVALID (0)
224 #define XT_MOVE_DOWN (1)
225 #define XT_MOVE_UP (2)
226 #define XT_MOVE_BOTTOM (3)
227 #define XT_MOVE_TOP (4)
228 #define XT_MOVE_PAGEDOWN (5)
229 #define XT_MOVE_PAGEUP (6)
230 #define XT_MOVE_HALFDOWN (7)
231 #define XT_MOVE_HALFUP (8)
232 #define XT_MOVE_LEFT (9)
233 #define XT_MOVE_FARLEFT (10)
234 #define XT_MOVE_RIGHT (11)
235 #define XT_MOVE_FARRIGHT (12)
236 #define XT_MOVE_PERCENT (13)
238 #define XT_QMARK_SET (0)
239 #define XT_QMARK_OPEN (1)
240 #define XT_QMARK_TAB (2)
242 #define XT_MARK_SET (0)
243 #define XT_MARK_GOTO (1)
245 #define XT_TAB_LAST (-4)
246 #define XT_TAB_FIRST (-3)
247 #define XT_TAB_PREV (-2)
248 #define XT_TAB_NEXT (-1)
249 #define XT_TAB_INVALID (0)
250 #define XT_TAB_NEW (1)
251 #define XT_TAB_DELETE (2)
252 #define XT_TAB_DELQUIT (3)
253 #define XT_TAB_OPEN (4)
254 #define XT_TAB_UNDO_CLOSE (5)
255 #define XT_TAB_SHOW (6)
256 #define XT_TAB_HIDE (7)
257 #define XT_TAB_NEXTSTYLE (8)
259 #define XT_NAV_INVALID (0)
260 #define XT_NAV_BACK (1)
261 #define XT_NAV_FORWARD (2)
262 #define XT_NAV_RELOAD (3)
264 #define XT_FOCUS_INVALID (0)
265 #define XT_FOCUS_URI (1)
266 #define XT_FOCUS_SEARCH (2)
268 #define XT_SEARCH_INVALID (0)
269 #define XT_SEARCH_NEXT (1)
270 #define XT_SEARCH_PREV (2)
272 #define XT_PASTE_CURRENT_TAB (0)
273 #define XT_PASTE_NEW_TAB (1)
275 #define XT_ZOOM_IN (-1)
276 #define XT_ZOOM_OUT (-2)
277 #define XT_ZOOM_NORMAL (100)
279 #define XT_URL_SHOW (1)
280 #define XT_URL_HIDE (2)
282 #define XT_WL_TOGGLE (1<<0)
283 #define XT_WL_ENABLE (1<<1)
284 #define XT_WL_DISABLE (1<<2)
285 #define XT_WL_FQDN (1<<3) /* default */
286 #define XT_WL_TOPLEVEL (1<<4)
287 #define XT_WL_PERSISTENT (1<<5)
288 #define XT_WL_SESSION (1<<6)
289 #define XT_WL_RELOAD (1<<7)
291 #define XT_SHOW (1<<7)
292 #define XT_DELETE (1<<8)
293 #define XT_SAVE (1<<9)
294 #define XT_OPEN (1<<10)
296 #define XT_CMD_OPEN (0)
297 #define XT_CMD_OPEN_CURRENT (1)
298 #define XT_CMD_TABNEW (2)
299 #define XT_CMD_TABNEW_CURRENT (3)
301 #define XT_STATUS_NOTHING (0)
302 #define XT_STATUS_LINK (1)
303 #define XT_STATUS_URI (2)
304 #define XT_STATUS_LOADING (3)
306 #define XT_SES_DONOTHING (0)
307 #define XT_SES_CLOSETABS (1)
309 #define XT_BM_NORMAL (0)
310 #define XT_BM_WHITELIST (1)
311 #define XT_BM_KIOSK (2)
313 #define XT_PREFIX (1<<0)
314 #define XT_USERARG (1<<1)
315 #define XT_URLARG (1<<2)
316 #define XT_INTARG (1<<3)
317 #define XT_SESSARG (1<<4)
318 #define XT_SETARG (1<<5)
320 #define XT_HINT_NEWTAB (1<<0)
322 #define XT_MODE_INSERT (0)
323 #define XT_MODE_COMMAND (1)
325 #define XT_TABS_NORMAL 0
326 #define XT_TABS_COMPACT 1
328 #define XT_BUFCMD_SZ (8)
330 #define XT_EJS_SHOW (1<<0)
338 TAILQ_ENTRY(mime_type
) entry
;
340 TAILQ_HEAD(mime_type_list
, mime_type
);
346 TAILQ_ENTRY(alias
) entry
;
348 TAILQ_HEAD(alias_list
, alias
);
350 /* settings that require restart */
351 int tabless
= 0; /* allow only 1 tab */
352 int enable_socket
= 0;
353 int single_instance
= 0; /* only allow one xxxterm to run */
354 int fancy_bar
= 1; /* fancy toolbar */
355 int browser_mode
= XT_BM_NORMAL
;
356 int enable_localstorage
= 1;
357 char *statusbar_elems
= NULL
;
359 /* runtime settings */
360 int show_tabs
= 1; /* show tabs on notebook */
361 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
362 int show_url
= 1; /* show url toolbar on notebook */
363 int show_statusbar
= 0; /* vimperator style status bar */
364 int ctrl_click_focus
= 0; /* ctrl click gets focus */
365 int cookies_enabled
= 1; /* enable cookies */
366 int read_only_cookies
= 0; /* enable to not write cookies */
367 int enable_scripts
= 1;
368 int enable_plugins
= 1;
369 gfloat default_zoom_level
= 1.0;
370 char default_script
[PATH_MAX
];
371 int window_height
= 768;
372 int window_width
= 1024;
373 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
374 int refresh_interval
= 10; /* download refresh interval */
375 int enable_plugin_whitelist
= 0;
376 int enable_cookie_whitelist
= 0;
377 int enable_js_whitelist
= 0;
378 int session_timeout
= 3600; /* cookie session timeout */
379 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
380 char *ssl_ca_file
= NULL
;
381 char *resource_dir
= NULL
;
382 gboolean ssl_strict_certs
= FALSE
;
383 int append_next
= 1; /* append tab after current tab */
385 char *search_string
= NULL
;
386 char *http_proxy
= NULL
;
387 char download_dir
[PATH_MAX
];
388 char runtime_settings
[PATH_MAX
]; /* override of settings */
389 int allow_volatile_cookies
= 0;
390 int save_global_history
= 0; /* save global history to disk */
391 char *user_agent
= NULL
;
392 int save_rejected_cookies
= 0;
393 int session_autosave
= 0;
394 int guess_search
= 0;
395 int dns_prefetch
= FALSE
;
396 gint max_connections
= 25;
397 gint max_host_connections
= 5;
398 gint enable_spell_checking
= 0;
399 char *spell_check_languages
= NULL
;
400 int xterm_workaround
= 0;
401 char *url_regex
= NULL
;
402 int history_autosave
= 0;
403 char search_file
[PATH_MAX
];
404 char command_file
[PATH_MAX
];
405 char *encoding
= NULL
;
406 int autofocus_onload
= 0;
408 char *cmd_font_name
= NULL
;
409 char *oops_font_name
= NULL
;
410 char *statusbar_font_name
= NULL
;
411 char *tabbar_font_name
= NULL
;
412 PangoFontDescription
*cmd_font
;
413 PangoFontDescription
*oops_font
;
414 PangoFontDescription
*statusbar_font
;
415 PangoFontDescription
*tabbar_font
;
416 char *qmarks
[XT_NOMARKS
];
418 int btn_down
; /* M1 down in any wv */
419 regex_t url_re
; /* guess_search regex */
423 int set_browser_mode(struct settings
*, char *);
424 int set_cookie_policy(struct settings
*, char *);
425 int set_download_dir(struct settings
*, char *);
426 int set_default_script(struct settings
*, char *);
427 int set_runtime_dir(struct settings
*, char *);
428 int set_tab_style(struct settings
*, char *);
429 int set_work_dir(struct settings
*, char *);
430 int add_alias(struct settings
*, char *);
431 int add_mime_type(struct settings
*, char *);
432 int add_cookie_wl(struct settings
*, char *);
433 int add_js_wl(struct settings
*, char *);
434 int add_pl_wl(struct settings
*, char *);
435 int add_kb(struct settings
*, char *);
436 void button_set_stockid(GtkWidget
*, char *);
437 GtkWidget
* create_button(char *, char *, int);
439 char *get_browser_mode(struct settings
*);
440 char *get_cookie_policy(struct settings
*);
441 char *get_download_dir(struct settings
*);
442 char *get_default_script(struct settings
*);
443 char *get_runtime_dir(struct settings
*);
444 char *get_tab_style(struct settings
*);
445 char *get_work_dir(struct settings
*);
446 void startpage_add(const char *, ...);
448 void walk_alias(struct settings
*, void (*)(struct settings
*,
449 char *, void *), void *);
450 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
451 char *, void *), void *);
452 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
453 char *, void *), void *);
454 void walk_pl_wl(struct settings
*, void (*)(struct settings
*,
455 char *, void *), void *);
456 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
458 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
459 char *, void *), void *);
461 void recalc_tabs(void);
462 void recolor_compact_tabs(void);
463 void set_current_tab(int page_num
);
464 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
465 void marks_clear(struct tab
*t
);
467 int set_http_proxy(char *);
470 int (*set
)(struct settings
*, char *);
471 char *(*get
)(struct settings
*);
472 void (*walk
)(struct settings
*,
473 void (*cb
)(struct settings
*, char *, void *),
477 struct special s_browser_mode
= {
483 struct special s_cookie
= {
489 struct special s_alias
= {
495 struct special s_mime
= {
501 struct special s_js
= {
507 struct special s_pl
= {
513 struct special s_kb
= {
519 struct special s_cookie_wl
= {
525 struct special s_default_script
= {
531 struct special s_download_dir
= {
537 struct special s_work_dir
= {
543 struct special s_tab_style
= {
552 #define XT_S_INVALID (0)
555 #define XT_S_FLOAT (3)
557 #define XT_SF_RESTART (1<<0)
558 #define XT_SF_RUNTIME (1<<1)
563 int (*activate
)(char *);
565 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
566 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
567 { "autofocus_onload", XT_S_INT
, 0, &autofocus_onload
, NULL
, NULL
},
568 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
569 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
570 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
571 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
572 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
573 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
574 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
575 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
576 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
577 { "enable_plugin_whitelist", XT_S_INT
, 0, &enable_plugin_whitelist
, NULL
, NULL
},
578 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
579 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
580 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
581 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
582 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
583 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
584 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
585 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
586 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
587 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
588 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
589 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
590 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
591 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
592 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
593 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
594 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
595 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
596 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
597 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
598 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
599 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
600 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
601 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
602 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
603 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
604 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
605 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
606 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
607 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
608 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
609 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
610 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
611 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
612 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
613 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
614 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
617 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
618 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
619 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
620 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
622 /* runtime settings */
623 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
624 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
625 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
626 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
627 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
628 { "pl_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_pl
},
631 int about(struct tab
*, struct karg
*);
632 int blank(struct tab
*, struct karg
*);
633 int ca_cmd(struct tab
*, struct karg
*);
634 int cookie_show_wl(struct tab
*, struct karg
*);
635 int js_show_wl(struct tab
*, struct karg
*);
636 int pl_show_wl(struct tab
*, struct karg
*);
637 int help(struct tab
*, struct karg
*);
638 int set(struct tab
*, struct karg
*);
639 int stats(struct tab
*, struct karg
*);
640 int marco(struct tab
*, struct karg
*);
641 int startpage(struct tab
*, struct karg
*);
642 const char * marco_message(int *);
643 int xtp_page_cl(struct tab
*, struct karg
*);
644 int xtp_page_dl(struct tab
*, struct karg
*);
645 int xtp_page_fl(struct tab
*, struct karg
*);
646 int xtp_page_hl(struct tab
*, struct karg
*);
647 void xt_icon_from_file(struct tab
*, char *);
648 const gchar
*get_uri(struct tab
*);
649 const gchar
*get_title(struct tab
*, bool);
651 #define XT_URI_ABOUT ("about:")
652 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
653 #define XT_URI_ABOUT_ABOUT ("about")
654 #define XT_URI_ABOUT_BLANK ("blank")
655 #define XT_URI_ABOUT_CERTS ("certs")
656 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
657 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
658 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
659 #define XT_URI_ABOUT_FAVORITES ("favorites")
660 #define XT_URI_ABOUT_HELP ("help")
661 #define XT_URI_ABOUT_HISTORY ("history")
662 #define XT_URI_ABOUT_JSWL ("jswl")
663 #define XT_URI_ABOUT_PLUGINWL ("plwl")
664 #define XT_URI_ABOUT_SET ("set")
665 #define XT_URI_ABOUT_STATS ("stats")
666 #define XT_URI_ABOUT_MARCO ("marco")
667 #define XT_URI_ABOUT_STARTPAGE ("startpage")
671 int (*func
)(struct tab
*, struct karg
*);
673 { XT_URI_ABOUT_ABOUT
, about
},
674 { XT_URI_ABOUT_BLANK
, blank
},
675 { XT_URI_ABOUT_CERTS
, ca_cmd
},
676 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
677 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
678 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
679 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
680 { XT_URI_ABOUT_HELP
, help
},
681 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
682 { XT_URI_ABOUT_JSWL
, js_show_wl
},
683 { XT_URI_ABOUT_SET
, set
},
684 { XT_URI_ABOUT_STATS
, stats
},
685 { XT_URI_ABOUT_MARCO
, marco
},
686 { XT_URI_ABOUT_STARTPAGE
, startpage
},
687 { XT_URI_ABOUT_PLUGINWL
, pl_show_wl
},
690 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
691 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
692 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
693 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
694 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
695 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
696 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
699 extern char *__progname
;
702 GtkWidget
*main_window
;
703 GtkNotebook
*notebook
;
705 GtkWidget
*arrow
, *abtn
;
706 struct tab_list tabs
;
707 struct history_list hl
;
708 struct session_list sessions
;
709 struct download_list downloads
;
710 struct domain_list c_wl
;
711 struct domain_list js_wl
;
712 struct domain_list pl_wl
;
713 struct undo_tailq undos
;
714 struct keybinding_list kbl
;
716 struct command_list chl
;
717 struct command_list shl
;
718 struct command_entry
*history_at
;
719 struct command_entry
*search_at
;
721 int updating_dl_tabs
= 0;
722 int updating_hl_tabs
= 0;
723 int updating_cl_tabs
= 0;
724 int updating_fl_tabs
= 0;
725 int cmd_history_count
= 0;
726 int search_history_count
= 0;
728 long long unsigned int blocked_cookies
= 0;
729 char named_session
[PATH_MAX
];
730 GtkListStore
*completion_model
;
731 GtkListStore
*buffers_store
;
733 void xxx_dir(char *);
734 int icon_size_map(int);
735 void completion_add(struct tab
*);
736 void completion_add_uri(const gchar
*);
737 void show_oops(struct tab
*, const char *, ...);
740 history_delete(struct command_list
*l
, int *counter
)
742 struct command_entry
*c
;
744 if (l
== NULL
|| counter
== NULL
)
747 c
= TAILQ_LAST(l
, command_list
);
751 TAILQ_REMOVE(l
, c
, entry
);
758 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
760 struct command_entry
*c
;
763 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
766 /* don't add the same line */
767 c
= TAILQ_FIRST(list
);
769 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
772 c
= g_malloc0(sizeof *c
);
773 c
->line
= g_strdup_printf(" %s", l
);
776 TAILQ_INSERT_HEAD(list
, c
, entry
);
779 history_delete(list
, counter
);
781 if (history_autosave
&& file
) {
782 f
= fopen(file
, "w");
784 show_oops(NULL
, "couldn't write history %s", file
);
788 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
790 fprintf(f
, "%s\n", c
->line
);
798 history_read(struct command_list
*list
, char *file
, int *counter
)
801 char *s
, line
[65536];
803 if (list
== NULL
|| file
== NULL
)
806 f
= fopen(file
, "r");
808 startpage_add("couldn't open history file %s", file
);
813 s
= fgets(line
, sizeof line
, f
);
814 if (s
== NULL
|| feof(f
) || ferror(f
))
816 if ((s
= strchr(line
, '\n')) == NULL
) {
817 startpage_add("invalid history file %s", file
);
823 history_add(list
, NULL
, line
+ 1, counter
);
831 /* marks and quickmarks array storage.
832 * first a-z, then A-Z, then 0-9 */
839 if (i
>= 0 && i
<= 'z' - 'a')
843 if (i
>= 0 && i
<= 'Z' - 'A')
858 if (m
>= 'a' && m
<= 'z')
859 return ret
+ m
- 'a';
861 ret
+= 'z' - 'a' + 1;
862 if (m
>= 'A' && m
<= 'Z')
863 return ret
+ m
- 'A';
865 ret
+= 'Z' - 'A' + 1;
866 if (m
>= '0' && m
<= '9')
867 return ret
+ m
- '0';
876 int saved_errno
, status
;
881 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
885 if (errno
!= ECHILD
) {
887 clog_warn("sigchild: waitpid:");
893 if (WIFEXITED(status
)) {
894 if (WEXITSTATUS(status
) != 0) {
896 clog_warnx("sigchild: child exit status: %d",
897 WEXITSTATUS(status));
902 clog_warnx("sigchild: child is terminated abnormally");
911 is_g_object_setting(GObject
*o
, char *str
)
913 guint n_props
= 0, i
;
914 GParamSpec
**proplist
;
916 if (! G_IS_OBJECT(o
))
919 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
922 for (i
=0; i
< n_props
; i
++) {
923 if (! strcmp(proplist
[i
]->name
, str
))
930 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
934 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
936 "<title>%s</title>\n"
945 addstyles
? XT_PAGE_STYLE
: "",
954 * Display a web page from a HTML string in memory, rather than from a URL
957 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
962 /* we set this to indicate we want to manually do navaction */
964 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
966 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
968 /* set t->xtp_meaning */
969 for (i
= 0; i
< LENGTH(about_list
); i
++)
970 if (!strcmp(title
, about_list
[i
].name
)) {
975 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
977 #if GTK_CHECK_VERSION(2, 20, 0)
978 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
979 gtk_widget_hide(t
->spinner
);
981 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
982 xt_icon_from_file(t
, file
);
987 get_current_tab(void)
991 TAILQ_FOREACH(t
, &tabs
, entry
) {
992 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
996 warnx("%s: no current tab", __func__
);
1002 set_status(struct tab
*t
, gchar
*s
, int status
)
1010 case XT_STATUS_LOADING
:
1011 type
= g_strdup_printf("Loading: %s", s
);
1014 case XT_STATUS_LINK
:
1015 type
= g_strdup_printf("Link: %s", s
);
1017 t
->status
= g_strdup(gtk_entry_get_text(
1018 GTK_ENTRY(t
->sbe
.statusbar
)));
1022 type
= g_strdup_printf("%s", s
);
1024 t
->status
= g_strdup(type
);
1028 t
->status
= g_strdup(s
);
1030 case XT_STATUS_NOTHING
:
1035 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1041 hide_cmd(struct tab
*t
)
1043 history_at
= NULL
; /* just in case */
1044 search_at
= NULL
; /* just in case */
1045 gtk_widget_hide(t
->cmd
);
1049 show_cmd(struct tab
*t
)
1053 gtk_widget_hide(t
->oops
);
1054 gtk_widget_show(t
->cmd
);
1058 hide_buffers(struct tab
*t
)
1060 gtk_widget_hide(t
->buffers
);
1061 gtk_list_store_clear(buffers_store
);
1071 sort_tabs_by_page_num(struct tab
***stabs
)
1076 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1078 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1080 TAILQ_FOREACH(t
, &tabs
, entry
)
1081 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1087 buffers_make_list(void)
1090 const gchar
*title
= NULL
;
1092 struct tab
**stabs
= NULL
;
1094 num_tabs
= sort_tabs_by_page_num(&stabs
);
1096 for (i
= 0; i
< num_tabs
; i
++)
1098 gtk_list_store_append(buffers_store
, &iter
);
1099 title
= get_title(stabs
[i
], FALSE
);
1100 gtk_list_store_set(buffers_store
, &iter
,
1101 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1111 show_buffers(struct tab
*t
)
1113 buffers_make_list();
1114 gtk_widget_show(t
->buffers
);
1115 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1119 toggle_buffers(struct tab
*t
)
1121 if (gtk_widget_get_visible(t
->buffers
))
1128 buffers(struct tab
*t
, struct karg
*args
)
1136 hide_oops(struct tab
*t
)
1138 gtk_widget_hide(t
->oops
);
1142 show_oops(struct tab
*at
, const char *fmt
, ...)
1146 struct tab
*t
= NULL
;
1152 if ((t
= get_current_tab()) == NULL
)
1158 if (vasprintf(&msg
, fmt
, ap
) == -1)
1159 errx(1, "show_oops failed");
1162 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1163 gtk_widget_hide(t
->cmd
);
1164 gtk_widget_show(t
->oops
);
1171 get_as_string(struct settings
*s
)
1182 warnx("get_as_string skip %s\n", s
->name
);
1183 } else if (s
->type
== XT_S_INT
)
1184 r
= g_strdup_printf("%d", *s
->ival
);
1185 else if (s
->type
== XT_S_STR
)
1186 r
= g_strdup(*s
->sval
);
1187 else if (s
->type
== XT_S_FLOAT
)
1188 r
= g_strdup_printf("%f", *s
->fval
);
1190 r
= g_strdup_printf("INVALID TYPE");
1196 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1201 for (i
= 0; i
< LENGTH(rs
); i
++) {
1202 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1203 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1205 s
= get_as_string(&rs
[i
]);
1206 cb(&rs
[i
], s
, cb_args
);
1213 set_browser_mode(struct settings
*s
, char *val
)
1215 if (!strcmp(val
, "whitelist")) {
1216 browser_mode
= XT_BM_WHITELIST
;
1217 allow_volatile_cookies
= 0;
1218 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1219 cookies_enabled
= 1;
1220 enable_cookie_whitelist
= 1;
1221 enable_plugin_whitelist
= 1;
1223 read_only_cookies
= 0;
1224 save_rejected_cookies
= 0;
1225 session_timeout
= 3600;
1227 enable_js_whitelist
= 1;
1228 enable_localstorage
= 0;
1229 } else if (!strcmp(val
, "normal")) {
1230 browser_mode
= XT_BM_NORMAL
;
1231 allow_volatile_cookies
= 0;
1232 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1233 cookies_enabled
= 1;
1234 enable_cookie_whitelist
= 0;
1235 enable_plugin_whitelist
= 0;
1237 read_only_cookies
= 0;
1238 save_rejected_cookies
= 0;
1239 session_timeout
= 3600;
1241 enable_js_whitelist
= 0;
1242 enable_localstorage
= 1;
1243 } else if (!strcmp(val
, "kiosk")) {
1244 browser_mode
= XT_BM_KIOSK
;
1245 allow_volatile_cookies
= 0;
1246 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1247 cookies_enabled
= 1;
1248 enable_cookie_whitelist
= 0;
1249 enable_plugin_whitelist
= 0;
1251 read_only_cookies
= 0;
1252 save_rejected_cookies
= 0;
1253 session_timeout
= 3600;
1255 enable_js_whitelist
= 0;
1256 enable_localstorage
= 1;
1266 get_browser_mode(struct settings
*s
)
1270 if (browser_mode
== XT_BM_WHITELIST
)
1271 r
= g_strdup("whitelist");
1272 else if (browser_mode
== XT_BM_NORMAL
)
1273 r
= g_strdup("normal");
1274 else if (browser_mode
== XT_BM_KIOSK
)
1275 r
= g_strdup("kiosk");
1283 set_cookie_policy(struct settings
*s
, char *val
)
1285 if (!strcmp(val
, "no3rdparty"))
1286 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1287 else if (!strcmp(val
, "accept"))
1288 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1289 else if (!strcmp(val
, "reject"))
1290 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1298 get_cookie_policy(struct settings
*s
)
1302 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1303 r
= g_strdup("no3rdparty");
1304 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1305 r
= g_strdup("accept");
1306 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1307 r
= g_strdup("reject");
1315 get_default_script(struct settings
*s
)
1317 if (default_script
[0] == '\0')
1319 return (g_strdup(default_script
));
1323 set_default_script(struct settings
*s
, char *val
)
1326 snprintf(default_script
, sizeof default_script
, "%s/%s",
1327 pwd
->pw_dir
, &val
[1]);
1329 strlcpy(default_script
, val
, sizeof default_script
);
1335 get_download_dir(struct settings
*s
)
1337 if (download_dir
[0] == '\0')
1339 return (g_strdup(download_dir
));
1343 set_download_dir(struct settings
*s
, char *val
)
1346 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1347 pwd
->pw_dir
, &val
[1]);
1349 strlcpy(download_dir
, val
, sizeof download_dir
);
1356 * We use these to prevent people putting xxxt:// URLs on
1357 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1359 #define XT_XTP_SES_KEY_SZ 8
1360 #define XT_XTP_SES_KEY_HEX_FMT \
1361 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1362 char *dl_session_key
; /* downloads */
1363 char *hl_session_key
; /* history list */
1364 char *cl_session_key
; /* cookie list */
1365 char *fl_session_key
; /* favorites list */
1367 char work_dir
[PATH_MAX
];
1368 char certs_dir
[PATH_MAX
];
1369 char cache_dir
[PATH_MAX
];
1370 char sessions_dir
[PATH_MAX
];
1371 char cookie_file
[PATH_MAX
];
1372 SoupURI
*proxy_uri
= NULL
;
1373 SoupSession
*session
;
1374 SoupCookieJar
*s_cookiejar
;
1375 SoupCookieJar
*p_cookiejar
;
1376 char rc_fname
[PATH_MAX
];
1378 struct mime_type_list mtl
;
1379 struct alias_list aliases
;
1382 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1383 void delete_tab(struct tab
*);
1384 void setzoom_webkit(struct tab
*, int);
1385 int run_script(struct tab
*, char *);
1386 int download_rb_cmp(struct download
*, struct download
*);
1387 gboolean
cmd_execute(struct tab
*t
, char *str
);
1390 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1392 return (strcmp(h1
->uri
, h2
->uri
));
1394 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1397 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1399 return (strcmp(d1
->d
, d2
->d
));
1401 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1404 get_work_dir(struct settings
*s
)
1406 if (work_dir
[0] == '\0')
1408 return (g_strdup(work_dir
));
1412 set_work_dir(struct settings
*s
, char *val
)
1415 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1416 pwd
->pw_dir
, &val
[1]);
1418 strlcpy(work_dir
, val
, sizeof work_dir
);
1424 get_tab_style(struct settings
*s
)
1426 if (tab_style
== XT_TABS_NORMAL
)
1427 return (g_strdup("normal"));
1429 return (g_strdup("compact"));
1433 set_tab_style(struct settings
*s
, char *val
)
1435 if (!strcmp(val
, "normal"))
1436 tab_style
= XT_TABS_NORMAL
;
1437 else if (!strcmp(val
, "compact"))
1438 tab_style
= XT_TABS_COMPACT
;
1446 * generate a session key to secure xtp commands.
1447 * pass in a ptr to the key in question and it will
1448 * be modified in place.
1451 generate_xtp_session_key(char **key
)
1453 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1459 /* make a new one */
1460 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1461 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1462 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1463 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1465 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1469 * validate a xtp session key.
1473 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1475 if (strcmp(trusted
, untrusted
) != 0) {
1476 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1485 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1487 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1489 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1491 struct valid_url_types
{
1502 valid_url_type(char *url
)
1506 for (i
= 0; i
< LENGTH(vut
); i
++)
1507 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1514 print_cookie(char *msg
, SoupCookie
*c
)
1520 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1521 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1522 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1523 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1524 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1525 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1526 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1527 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1528 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1529 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1533 walk_alias(struct settings
*s
,
1534 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1539 if (s
== NULL
|| cb
== NULL
) {
1540 show_oops(NULL
, "walk_alias invalid parameters");
1544 TAILQ_FOREACH(a
, &aliases
, entry
) {
1545 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1546 cb(s
, str
, cb_args
);
1552 match_alias(char *url_in
)
1556 char *url_out
= NULL
, *search
, *enc_arg
;
1558 search
= g_strdup(url_in
);
1560 if (strsep(&arg
, " \t") == NULL
) {
1561 show_oops(NULL
, "match_alias: NULL URL");
1565 TAILQ_FOREACH(a
, &aliases
, entry
) {
1566 if (!strcmp(search
, a
->a_name
))
1571 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1574 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1575 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1578 url_out
= g_strdup_printf(a
->a_uri
, "");
1586 guess_url_type(char *url_in
)
1589 char *url_out
= NULL
, *enc_search
= NULL
;
1592 /* substitute aliases */
1593 url_out
= match_alias(url_in
);
1594 if (url_out
!= NULL
)
1597 /* see if we are an about page */
1598 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1599 for (i
= 0; i
< LENGTH(about_list
); i
++)
1600 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1601 about_list
[i
].name
)) {
1602 url_out
= g_strdup(url_in
);
1606 if (guess_search
&& url_regex
&&
1607 !(g_str_has_prefix(url_in
, "http://") ||
1608 g_str_has_prefix(url_in
, "https://"))) {
1609 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1610 /* invalid URI so search instead */
1611 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1612 url_out
= g_strdup_printf(search_string
, enc_search
);
1618 /* XXX not sure about this heuristic */
1619 if (stat(url_in
, &sb
) == 0)
1620 url_out
= g_strdup_printf("file://%s", url_in
);
1622 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1624 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1630 load_uri(struct tab
*t
, gchar
*uri
)
1633 gchar
*newuri
= NULL
;
1639 /* Strip leading spaces. */
1640 while (*uri
&& isspace(*uri
))
1643 if (strlen(uri
) == 0) {
1648 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1650 if (valid_url_type(uri
)) {
1651 newuri
= guess_url_type(uri
);
1655 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1656 for (i
= 0; i
< LENGTH(about_list
); i
++)
1657 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1658 bzero(&args
, sizeof args
);
1659 about_list
[i
].func(t
, &args
);
1660 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1664 show_oops(t
, "invalid about page");
1668 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1670 webkit_web_view_load_uri(t
->wv
, uri
);
1677 get_uri(struct tab
*t
)
1679 const gchar
*uri
= NULL
;
1681 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1683 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1684 uri
= webkit_web_view_get_uri(t
->wv
);
1686 /* use tmp_uri to make sure it is g_freed */
1689 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1690 about_list
[t
->xtp_meaning
].name
);
1697 get_title(struct tab
*t
, bool window
)
1699 const gchar
*set
= NULL
, *title
= NULL
;
1700 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1702 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1703 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1706 title
= webkit_web_view_get_title(t
->wv
);
1707 if ((set
= title
? title
: get_uri(t
)))
1711 set
= window
? XT_NAME
: "(untitled)";
1717 add_alias(struct settings
*s
, char *line
)
1720 struct alias
*a
= NULL
;
1722 if (s
== NULL
|| line
== NULL
) {
1723 show_oops(NULL
, "add_alias invalid parameters");
1728 a
= g_malloc(sizeof(*a
));
1730 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1731 show_oops(NULL
, "add_alias: incomplete alias definition");
1734 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1735 show_oops(NULL
, "add_alias: invalid alias definition");
1739 a
->a_name
= g_strdup(alias
);
1740 a
->a_uri
= g_strdup(l
);
1742 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1744 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1754 add_mime_type(struct settings
*s
, char *line
)
1758 struct mime_type
*m
= NULL
;
1759 int downloadfirst
= 0;
1761 /* XXX this could be smarter */
1763 if (line
== NULL
|| strlen(line
) == 0) {
1764 show_oops(NULL
, "add_mime_type invalid parameters");
1773 m
= g_malloc(sizeof(*m
));
1775 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1776 show_oops(NULL
, "add_mime_type: invalid mime_type");
1779 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1780 mime_type
[strlen(mime_type
) - 1] = '\0';
1785 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1786 show_oops(NULL
, "add_mime_type: invalid mime_type");
1790 m
->mt_type
= g_strdup(mime_type
);
1791 m
->mt_action
= g_strdup(l
);
1792 m
->mt_download
= downloadfirst
;
1794 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1795 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1797 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1807 find_mime_type(char *mime_type
)
1809 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1811 TAILQ_FOREACH(m
, &mtl
, entry
) {
1812 if (m
->mt_default
&&
1813 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1816 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1829 walk_mime_type(struct settings
*s
,
1830 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1832 struct mime_type
*m
;
1835 if (s
== NULL
|| cb
== NULL
) {
1836 show_oops(NULL
, "walk_mime_type invalid parameters");
1840 TAILQ_FOREACH(m
, &mtl
, entry
) {
1841 str
= g_strdup_printf("%s%s --> %s",
1843 m
->mt_default
? "*" : "",
1845 cb(s
, str
, cb_args
);
1851 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1857 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1860 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1862 /* treat *.moo.com the same as .moo.com */
1863 if (str
[0] == '*' && str
[1] == '.')
1865 else if (str
[0] == '.')
1870 /* slice off port number */
1871 p
= g_strrstr(str
, ":");
1875 d
= g_malloc(sizeof *d
);
1877 d
->d
= g_strdup_printf(".%s", str
);
1879 d
->d
= g_strdup(str
);
1882 if (RB_INSERT(domain_list
, wl
, d
))
1885 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1896 add_cookie_wl(struct settings
*s
, char *entry
)
1898 wl_add(entry
, &c_wl
, 1);
1903 walk_cookie_wl(struct settings
*s
,
1904 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1908 if (s
== NULL
|| cb
== NULL
) {
1909 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1913 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1914 cb(s
, d
->d
, cb_args
);
1918 walk_js_wl(struct settings
*s
,
1919 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1923 if (s
== NULL
|| cb
== NULL
) {
1924 show_oops(NULL
, "walk_js_wl invalid parameters");
1928 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1929 cb(s
, d
->d
, cb_args
);
1933 add_js_wl(struct settings
*s
, char *entry
)
1935 wl_add(entry
, &js_wl
, 1 /* persistent */);
1940 walk_pl_wl(struct settings
*s
,
1941 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1945 if (s
== NULL
|| cb
== NULL
) {
1946 show_oops(NULL
, "walk_pl_wl invalid parameters");
1950 RB_FOREACH_REVERSE(d
, domain_list
, &pl_wl
)
1951 cb(s
, d
->d
, cb_args
);
1955 add_pl_wl(struct settings
*s
, char *entry
)
1957 wl_add(entry
, &pl_wl
, 1 /* persistent */);
1962 wl_find(const gchar
*search
, struct domain_list
*wl
)
1965 struct domain
*d
= NULL
, dfind
;
1968 if (search
== NULL
|| wl
== NULL
)
1970 if (strlen(search
) < 2)
1973 if (search
[0] != '.')
1974 s
= g_strdup_printf(".%s", search
);
1976 s
= g_strdup(search
);
1978 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1981 d
= RB_FIND(domain_list
, wl
, &dfind
);
1995 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2001 if (s
== NULL
|| wl
== NULL
)
2004 if (!strncmp(s
, "http://", strlen("http://")))
2005 s
= &s
[strlen("http://")];
2006 else if (!strncmp(s
, "https://", strlen("https://")))
2007 s
= &s
[strlen("https://")];
2012 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2013 /* chop string at first slash */
2014 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2017 r
= wl_find(ss
, wl
);
2026 settings_add(char *var
, char *val
)
2033 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2034 if (strcmp(var
, rs
[i
].name
))
2038 if (rs
[i
].s
->set(&rs
[i
], val
))
2039 errx(1, "invalid value for %s: %s", var
, val
);
2043 switch (rs
[i
].type
) {
2052 errx(1, "invalid sval for %s",
2066 errx(1, "invalid type for %s", var
);
2075 config_parse(char *filename
, int runtime
)
2078 char *line
, *cp
, *var
, *val
;
2079 size_t len
, lineno
= 0;
2081 char file
[PATH_MAX
];
2084 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2086 if (filename
== NULL
)
2089 if (runtime
&& runtime_settings
[0] != '\0') {
2090 snprintf(file
, sizeof file
, "%s/%s",
2091 work_dir
, runtime_settings
);
2092 if (stat(file
, &sb
)) {
2093 warnx("runtime file doesn't exist, creating it");
2094 if ((f
= fopen(file
, "w")) == NULL
)
2096 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2100 strlcpy(file
, filename
, sizeof file
);
2102 if ((config
= fopen(file
, "r")) == NULL
) {
2103 warn("config_parse: cannot open %s", filename
);
2108 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2109 if (feof(config
) || ferror(config
))
2113 cp
+= (long)strspn(cp
, WS
);
2114 if (cp
[0] == '\0') {
2120 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2121 startpage_add("invalid configuration file entry: %s",
2124 cp
+= (long)strspn(cp
, WS
);
2126 if ((val
= strsep(&cp
, "\0")) == NULL
)
2129 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",
2131 handled
= settings_add(var
, val
);
2134 startpage_add("invalid configuration file entry"
2135 ": %s=%s", var
, val
);
2145 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2151 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2155 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2158 JSStringGetUTF8CString(jsref
, s
, l
);
2159 JSStringRelease(jsref
);
2165 disable_hints(struct tab
*t
)
2167 DNPRINTF(XT_D_JS
, "%s\n", __func__
);
2169 run_script(t
, "hints.clearHints();");
2175 enable_hints(struct tab
*t
)
2177 DNPRINTF(XT_D_JS
, "%s\n", __func__
);
2179 run_script(t
, JS_HINTING
);
2182 run_script(t
, "hints.createHints('', 'F');");
2184 run_script(t
, "hints.createHints('', 'f');");
2188 #define XT_JS_DONE ("done;")
2189 #define XT_JS_DONE_LEN (strlen(XT_JS_DONE))
2190 #define XT_JS_INSERT ("insert;")
2191 #define XT_JS_INSERT_LEN (strlen(XT_JS_INSERT))
2194 run_script(struct tab
*t
, char *s
)
2196 JSGlobalContextRef ctx
;
2197 WebKitWebFrame
*frame
;
2199 JSValueRef val
, exception
;
2202 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2203 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2205 /* a bit silly but it prevents some heartburn */
2206 if (t
->script_init
== 0) {
2208 run_script(t
, JS_HINTING
);
2211 frame
= webkit_web_view_get_main_frame(t
->wv
);
2212 ctx
= webkit_web_frame_get_global_context(frame
);
2214 str
= JSStringCreateWithUTF8CString(s
);
2215 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2216 NULL
, 0, &exception
);
2217 JSStringRelease(str
);
2219 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2221 es
= js_ref_to_string(ctx
, exception
);
2223 /* show_oops(t, "script exception: %s", es); */
2224 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2229 es
= js_ref_to_string(ctx
, val
);
2232 if (!strncmp(es
, XT_JS_DONE
, XT_JS_DONE_LEN
))
2234 if (!strncmp(es
, XT_JS_INSERT
, XT_JS_INSERT_LEN
))
2238 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2247 hint(struct tab
*t
, struct karg
*args
)
2250 DNPRINTF(XT_D_JS
, "hint: tab %d args %d\n", t
->tab_id
, args
->i
);
2252 if (t
->hints_on
== 0) {
2253 if (args
->i
== XT_HINT_NEWTAB
)
2263 apply_style(struct tab
*t
)
2265 g_object_set(G_OBJECT(t
->settings
),
2266 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2270 userstyle(struct tab
*t
, struct karg
*args
)
2272 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2276 g_object_set(G_OBJECT(t
->settings
),
2277 "user-stylesheet-uri", NULL
, (char *)NULL
);
2286 * Doesn't work fully, due to the following bug:
2287 * https://bugs.webkit.org/show_bug.cgi?id=51747
2290 restore_global_history(void)
2292 char file
[PATH_MAX
];
2297 const char delim
[3] = {'\\', '\\', '\0'};
2299 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2301 if ((f
= fopen(file
, "r")) == NULL
) {
2302 warnx("%s: fopen", __func__
);
2307 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2308 if (feof(f
) || ferror(f
))
2311 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2312 if (feof(f
) || ferror(f
)) {
2314 warnx("%s: broken history file\n", __func__
);
2318 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2319 webkit_web_history_item_new_with_data(uri
, title
);
2320 h
= g_malloc(sizeof(struct history
));
2321 h
->uri
= g_strdup(uri
);
2322 h
->title
= g_strdup(title
);
2323 RB_INSERT(history_list
, &hl
, h
);
2324 completion_add_uri(h
->uri
);
2326 warnx("%s: failed to restore history\n", __func__
);
2342 save_global_history_to_disk(struct tab
*t
)
2344 char file
[PATH_MAX
];
2348 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2350 if ((f
= fopen(file
, "w")) == NULL
) {
2351 show_oops(t
, "%s: global history file: %s",
2352 __func__
, strerror(errno
));
2356 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2357 if (h
->uri
&& h
->title
)
2358 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2367 quit(struct tab
*t
, struct karg
*args
)
2369 if (save_global_history
)
2370 save_global_history_to_disk(t
);
2378 restore_sessions_list(void)
2381 struct dirent
*dp
= NULL
;
2384 sdir
= opendir(sessions_dir
);
2386 while ((dp
= readdir(sdir
)) != NULL
)
2387 if (dp
->d_type
== DT_REG
) {
2388 s
= g_malloc(sizeof(struct session
));
2389 s
->name
= g_strdup(dp
->d_name
);
2390 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2397 open_tabs(struct tab
*t
, struct karg
*a
)
2399 char file
[PATH_MAX
];
2403 struct tab
*ti
, *tt
;
2408 ti
= TAILQ_LAST(&tabs
, tab_list
);
2410 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2411 if ((f
= fopen(file
, "r")) == NULL
)
2415 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2416 if (feof(f
) || ferror(f
))
2419 /* retrieve session name */
2420 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2421 strlcpy(named_session
,
2422 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2423 sizeof named_session
);
2427 if (uri
&& strlen(uri
))
2428 create_new_tab(uri
, NULL
, 1, -1);
2434 /* close open tabs */
2435 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2437 tt
= TAILQ_FIRST(&tabs
);
2458 restore_saved_tabs(void)
2460 char file
[PATH_MAX
];
2461 int unlink_file
= 0;
2466 snprintf(file
, sizeof file
, "%s/%s",
2467 sessions_dir
, XT_RESTART_TABS_FILE
);
2468 if (stat(file
, &sb
) == -1)
2469 a
.s
= XT_SAVED_TABS_FILE
;
2472 a
.s
= XT_RESTART_TABS_FILE
;
2475 a
.i
= XT_SES_DONOTHING
;
2476 rv
= open_tabs(NULL
, &a
);
2485 save_tabs(struct tab
*t
, struct karg
*a
)
2487 char file
[PATH_MAX
];
2489 int num_tabs
= 0, i
;
2490 struct tab
**stabs
= NULL
;
2492 /* tab may be null here */
2497 snprintf(file
, sizeof file
, "%s/%s",
2498 sessions_dir
, named_session
);
2500 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2502 if ((f
= fopen(file
, "w")) == NULL
) {
2503 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2507 /* save session name */
2508 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2510 /* Save tabs, in the order they are arranged in the notebook. */
2511 num_tabs
= sort_tabs_by_page_num(&stabs
);
2513 for (i
= 0; i
< num_tabs
; i
++)
2515 if (get_uri(stabs
[i
]) != NULL
)
2516 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2517 else if (gtk_entry_get_text(GTK_ENTRY(
2518 stabs
[i
]->uri_entry
)))
2519 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2520 stabs
[i
]->uri_entry
)));
2525 /* try and make sure this gets to disk NOW. XXX Backup first? */
2526 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2527 show_oops(t
, "May not have managed to save session: %s",
2537 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2549 run_page_script(struct tab
*t
, struct karg
*args
)
2552 char *tmp
, script
[PATH_MAX
];
2554 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2555 if (tmp
[0] == '\0') {
2556 show_oops(t
, "no script specified");
2560 if ((uri
= get_uri(t
)) == NULL
) {
2561 show_oops(t
, "tab is empty, not running script");
2566 snprintf(script
, sizeof script
, "%s/%s",
2567 pwd
->pw_dir
, &tmp
[1]);
2569 strlcpy(script
, tmp
, sizeof script
);
2573 show_oops(t
, "can't fork to run script");
2583 execlp(script
, script
, uri
, (void *)NULL
);
2593 yank_uri(struct tab
*t
, struct karg
*args
)
2596 GtkClipboard
*clipboard
;
2598 if ((uri
= get_uri(t
)) == NULL
)
2601 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2602 gtk_clipboard_set_text(clipboard
, uri
, -1);
2608 paste_uri(struct tab
*t
, struct karg
*args
)
2610 GtkClipboard
*clipboard
;
2611 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2613 gchar
*p
= NULL
, *uri
;
2615 /* try primary clipboard first */
2616 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2617 p
= gtk_clipboard_wait_for_text(clipboard
);
2619 /* if it failed get whatever text is in cut_buffer0 */
2620 if (p
== NULL
&& xterm_workaround
)
2621 if (gdk_property_get(gdk_get_default_root_window(),
2623 gdk_atom_intern("STRING", FALSE
),
2625 1024 * 1024 /* picked out of my butt */,
2631 /* yes sir, we need to NUL the string */
2637 while (*uri
&& isspace(*uri
))
2639 if (strlen(uri
) == 0) {
2640 show_oops(t
, "empty paste buffer");
2643 if (guess_search
== 0 && valid_url_type(uri
)) {
2644 /* we can be clever and paste this in search box */
2645 show_oops(t
, "not a valid URL");
2649 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2651 else if (args
->i
== XT_PASTE_NEW_TAB
)
2652 create_new_tab(uri
, NULL
, 1, -1);
2663 find_domain(const gchar
*s
, int toplevel
)
2671 uri
= soup_uri_new(s
);
2673 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2677 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2678 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2679 while(--p
>= uri
->host
&& *p
!= '.');
2686 ret
= g_strdup_printf(".%s", p
);
2694 toggle_cwl(struct tab
*t
, struct karg
*args
)
2705 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2707 if (uri
== NULL
|| dom
== NULL
||
2708 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2709 show_oops(t
, "Can't toggle domain in cookie white list");
2712 d
= wl_find(dom
, &c_wl
);
2719 if (args
->i
& XT_WL_TOGGLE
)
2721 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2723 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2727 /* enable cookies for domain */
2728 wl_add(dom
, &c_wl
, 0);
2730 /* disable cookies for domain */
2732 RB_REMOVE(domain_list
, &c_wl
, d
);
2735 if (args
->i
& XT_WL_RELOAD
)
2736 webkit_web_view_reload(t
->wv
);
2744 toggle_js(struct tab
*t
, struct karg
*args
)
2754 g_object_get(G_OBJECT(t
->settings
),
2755 "enable-scripts", &es
, (char *)NULL
);
2756 if (args
->i
& XT_WL_TOGGLE
)
2758 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2760 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2766 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2768 if (uri
== NULL
|| dom
== NULL
||
2769 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2770 show_oops(t
, "Can't toggle domain in JavaScript white list");
2775 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2776 wl_add(dom
, &js_wl
, 0 /* session */);
2778 d
= wl_find(dom
, &js_wl
);
2780 RB_REMOVE(domain_list
, &js_wl
, d
);
2781 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2783 g_object_set(G_OBJECT(t
->settings
),
2784 "enable-scripts", es
, (char *)NULL
);
2785 g_object_set(G_OBJECT(t
->settings
),
2786 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2787 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2789 if (args
->i
& XT_WL_RELOAD
)
2790 webkit_web_view_reload(t
->wv
);
2798 toggle_pl(struct tab
*t
, struct karg
*args
)
2808 g_object_get(G_OBJECT(t
->settings
),
2809 "enable-plugins", &es
, (char *)NULL
);
2810 if (args
->i
& XT_WL_TOGGLE
)
2812 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2814 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2820 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2822 if (uri
== NULL
|| dom
== NULL
||
2823 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2824 show_oops(t
, "Can't toggle domain in plugins white list");
2829 wl_add(dom
, &pl_wl
, 0 /* session */);
2831 d
= wl_find(dom
, &pl_wl
);
2833 RB_REMOVE(domain_list
, &pl_wl
, d
);
2835 g_object_set(G_OBJECT(t
->settings
),
2836 "enable-plugins", es
, (char *)NULL
);
2837 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2839 if (args
->i
& XT_WL_RELOAD
)
2840 webkit_web_view_reload(t
->wv
);
2848 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2853 g_object_get(G_OBJECT(t
->settings
),
2854 "enable-scripts", &es
, (char *)NULL
);
2859 set
= XT_WL_DISABLE
;
2861 a
.i
= set
| XT_WL_TOPLEVEL
;
2864 a
.i
= set
| XT_WL_TOPLEVEL
;
2867 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2872 toggle_src(struct tab
*t
, struct karg
*args
)
2879 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2880 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2881 webkit_web_view_reload(t
->wv
);
2887 focus_webview(struct tab
*t
)
2892 /* only grab focus if we are visible */
2893 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2894 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2898 focus(struct tab
*t
, struct karg
*args
)
2900 if (t
== NULL
|| args
== NULL
)
2906 if (args
->i
== XT_FOCUS_URI
)
2907 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2908 else if (args
->i
== XT_FOCUS_SEARCH
)
2909 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2915 stats(struct tab
*t
, struct karg
*args
)
2917 char *page
, *body
, *s
, line
[64 * 1024];
2918 long long unsigned int line_count
= 0;
2922 show_oops(NULL
, "stats invalid parameters");
2925 if (save_rejected_cookies
) {
2926 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2928 s
= fgets(line
, sizeof line
, r_cookie_f
);
2929 if (s
== NULL
|| feof(r_cookie_f
) ||
2935 snprintf(line
, sizeof line
,
2936 "<br/>Cookies blocked(*) total: %llu", line_count
);
2938 show_oops(t
, "Can't open blocked cookies file: %s",
2942 body
= g_strdup_printf(
2943 "Cookies blocked(*) this session: %llu"
2945 "<p><small><b>*</b> results vary based on settings</small></p>",
2949 page
= get_html_page("Statistics", body
, "", 0);
2952 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2959 marco(struct tab
*t
, struct karg
*args
)
2961 char *page
, line
[64 * 1024];
2965 show_oops(NULL
, "marco invalid parameters");
2968 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2970 page
= get_html_page("Marco Sez...", line
, "", 0);
2972 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2979 blank(struct tab
*t
, struct karg
*args
)
2982 show_oops(NULL
, "blank invalid parameters");
2984 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2990 about(struct tab
*t
, struct karg
*args
)
2995 show_oops(NULL
, "about invalid parameters");
2997 body
= g_strdup_printf("<b>Version: %s</b>"
2998 #ifdef XXXTERM_BUILDSTR
2999 "<br><b>Build: %s</b>"
3004 "<li>Marco Peereboom <marco@peereboom.us></li>"
3005 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3006 "<li>Edd Barrett <vext01@gmail.com> </li>"
3007 "<li>Todd T. Fries <todd@fries.net> </li>"
3008 "<li>Raphael Graf <r@undefined.ch> </li>"
3010 "Copyrights and licenses can be found on the XXXTerm "
3011 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3013 #ifdef XXXTERM_BUILDSTR
3014 version
, XXXTERM_BUILDSTR
3020 page
= get_html_page("About", body
, "", 0);
3023 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3030 help(struct tab
*t
, struct karg
*args
)
3032 char *page
, *head
, *body
;
3035 show_oops(NULL
, "help invalid parameters");
3037 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3038 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3040 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3041 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3042 "cgi-bin/man-cgi?xxxterm</a>";
3044 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3046 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3053 startpage(struct tab
*t
, struct karg
*args
)
3055 char *page
, *body
, *b
;
3059 show_oops(NULL
, "startpage invalid parameters");
3061 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3063 TAILQ_FOREACH(s
, &spl
, entry
) {
3065 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3069 page
= get_html_page("Startup Exception", body
, "", 0);
3072 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3079 startpage_add(const char *fmt
, ...)
3089 if (vasprintf(&msg
, fmt
, ap
) == -1)
3090 errx(1, "startpage_add failed");
3093 s
= g_malloc0(sizeof *s
);
3096 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3100 * update all favorite tabs apart from one. Pass NULL if
3101 * you want to update all.
3104 update_favorite_tabs(struct tab
*apart_from
)
3107 if (!updating_fl_tabs
) {
3108 updating_fl_tabs
= 1; /* stop infinite recursion */
3109 TAILQ_FOREACH(t
, &tabs
, entry
)
3110 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3111 && (t
!= apart_from
))
3112 xtp_page_fl(t
, NULL
);
3113 updating_fl_tabs
= 0;
3117 /* show a list of favorites (bookmarks) */
3119 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3121 char file
[PATH_MAX
];
3123 char *uri
= NULL
, *title
= NULL
;
3124 size_t len
, lineno
= 0;
3126 char *body
, *tmp
, *page
= NULL
;
3127 const char delim
[3] = {'\\', '\\', '\0'};
3129 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3132 warn("%s: bad param", __func__
);
3134 /* new session key */
3135 if (!updating_fl_tabs
)
3136 generate_xtp_session_key(&fl_session_key
);
3138 /* open favorites */
3139 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3140 if ((f
= fopen(file
, "r")) == NULL
) {
3141 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3146 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3147 "<th style='width: 40px'>#</th><th>Link</th>"
3148 "<th style='width: 40px'>Rm</th></tr>\n");
3151 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3153 if (strlen(title
) == 0 || title
[0] == '#') {
3159 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3160 if (feof(f
) || ferror(f
)) {
3161 show_oops(t
, "favorites file corrupt");
3167 body
= g_strdup_printf("%s<tr>"
3169 "<td><a href='%s'>%s</a></td>"
3170 "<td style='text-align: center'>"
3171 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3173 body
, i
, uri
, title
,
3174 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3186 /* if none, say so */
3189 body
= g_strdup_printf("%s<tr>"
3190 "<td colspan='3' style='text-align: center'>"
3191 "No favorites - To add one use the 'favadd' command."
3192 "</td></tr>", body
);
3197 body
= g_strdup_printf("%s</table>", body
);
3207 page
= get_html_page("Favorites", body
, "", 1);
3208 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3212 update_favorite_tabs(t
);
3221 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3222 size_t cert_count
, char *title
)
3224 gnutls_datum_t cinfo
;
3228 body
= g_strdup("");
3230 for (i
= 0; i
< cert_count
; i
++) {
3231 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3236 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3237 body
, i
, cinfo
.data
);
3238 gnutls_free(cinfo
.data
);
3242 tmp
= get_html_page(title
, body
, "", 0);
3245 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3250 ca_cmd(struct tab
*t
, struct karg
*args
)
3253 int rv
= 1, certs
= 0, certs_read
;
3256 gnutls_x509_crt_t
*c
= NULL
;
3257 char *certs_buf
= NULL
, *s
;
3259 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3260 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3264 if (fstat(fileno(f
), &sb
) == -1) {
3265 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3269 certs_buf
= g_malloc(sb
.st_size
+ 1);
3270 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3271 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3274 certs_buf
[sb
.st_size
] = '\0';
3277 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3279 s
+= strlen("BEGIN CERTIFICATE");
3282 bzero(&dt
, sizeof dt
);
3283 dt
.data
= (unsigned char *)certs_buf
;
3284 dt
.size
= sb
.st_size
;
3285 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3286 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3287 GNUTLS_X509_FMT_PEM
, 0);
3288 if (certs_read
<= 0) {
3289 show_oops(t
, "No cert(s) available");
3292 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3305 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
3309 struct addrinfo hints
, *res
= NULL
, *ai
;
3310 int rv
= -1, s
= -1, on
, error
;
3312 static gchar myerror
[256]; /* this is not thread safe */
3315 *error_str
= myerror
;
3316 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3317 *error_str
= "invalid URI";
3321 su
= soup_uri_new(uri
);
3323 *error_str
= "invalid soup URI";
3326 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3327 *error_str
= "invalid HTTPS URI";
3331 snprintf(port
, sizeof port
, "%d", su
->port
);
3332 bzero(&hints
, sizeof(struct addrinfo
));
3333 hints
.ai_flags
= AI_CANONNAME
;
3334 hints
.ai_family
= AF_UNSPEC
;
3335 hints
.ai_socktype
= SOCK_STREAM
;
3337 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3338 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
3339 gai_strerror(errno
));
3343 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3349 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3351 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3354 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3357 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3361 snprintf(myerror
, sizeof myerror
,
3362 "could not obtain certificates from: %s",
3368 strlcpy(domain
, su
->host
, domain_sz
);
3375 if (rv
== -1 && s
!= -1)
3382 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3385 gnutls_deinit(gsession
);
3387 gnutls_certificate_free_credentials(xcred
);
3393 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
3394 gnutls_certificate_credentials_t
*xc
)
3396 gnutls_certificate_credentials_t xcred
;
3397 gnutls_session_t gsession
;
3399 static gchar myerror
[1024]; /* this is not thread safe */
3401 if (gs
== NULL
|| xc
== NULL
)
3408 gnutls_certificate_allocate_credentials(&xcred
);
3409 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3410 GNUTLS_X509_FMT_PEM
);
3412 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3413 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3414 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3415 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3416 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3417 snprintf(myerror
, sizeof myerror
,
3418 "gnutls_handshake failed %d fatal %d %s",
3420 gnutls_error_is_fatal(rv
),
3421 gnutls_strerror_name(rv
));
3422 stop_tls(gsession
, xcred
);
3426 gnutls_credentials_type_t cred
;
3427 cred
= gnutls_auth_get_type(gsession
);
3428 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3429 snprintf(myerror
, sizeof myerror
,
3430 "gnutls_auth_get_type failed %d",
3432 stop_tls(gsession
, xcred
);
3440 *error_str
= myerror
;
3445 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3449 const gnutls_datum_t
*cl
;
3450 gnutls_x509_crt_t
*all_certs
;
3453 if (certs
== NULL
|| cert_count
== NULL
)
3455 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3457 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3461 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3462 for (i
= 0; i
< len
; i
++) {
3463 gnutls_x509_crt_init(&all_certs
[i
]);
3464 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3465 GNUTLS_X509_FMT_PEM
< 0)) {
3479 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3483 for (i
= 0; i
< cert_count
; i
++)
3484 gnutls_x509_crt_deinit(certs
[i
]);
3489 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3491 GdkColor c_text
, c_base
;
3493 gdk_color_parse(text
, &c_text
);
3494 gdk_color_parse(base
, &c_base
);
3496 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3497 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3498 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3499 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3501 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3502 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3503 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3504 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3508 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3509 size_t cert_count
, char *domain
)
3512 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3517 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3520 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3521 if ((f
= fopen(file
, "w")) == NULL
) {
3522 show_oops(t
, "Can't create cert file %s %s",
3523 file
, strerror(errno
));
3527 for (i
= 0; i
< cert_count
; i
++) {
3528 cert_buf_sz
= sizeof cert_buf
;
3529 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3530 cert_buf
, &cert_buf_sz
)) {
3531 show_oops(t
, "gnutls_x509_crt_export failed");
3534 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3535 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3540 /* not the best spot but oh well */
3541 gdk_color_parse("lightblue", &color
);
3542 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3543 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3556 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
3558 char domain
[8182], file
[PATH_MAX
];
3559 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3561 unsigned int error
= 0;
3563 size_t cert_buf_sz
, cert_count
;
3564 enum cert_trust rv
= CERT_UNTRUSTED
;
3565 static gchar serr
[80]; /* this isn't thread safe */
3566 gnutls_session_t gsession
;
3567 gnutls_x509_crt_t
*certs
;
3568 gnutls_certificate_credentials_t xcred
;
3570 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3574 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
3575 sizeof domain
)) == -1)
3578 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3581 if (start_tls(error_str
, s
, &gsession
, &xcred
))
3583 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3585 /* verify certs in case cert file doesn't exist */
3586 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3588 *error_str
= "Invalid certificates";
3593 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3594 *error_str
= "Can't get connection certificates";
3598 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3599 if ((f
= fopen(file
, "r")) == NULL
) {
3605 for (i
= 0; i
< cert_count
; i
++) {
3606 cert_buf_sz
= sizeof cert_buf
;
3607 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3608 cert_buf
, &cert_buf_sz
)) {
3611 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3612 rv
= CERT_BAD
; /* critical */
3615 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3616 rv
= CERT_BAD
; /* critical */
3625 free_connection_certs(certs
, cert_count
);
3627 /* we close the socket first for speed */
3631 /* only complain if we didn't save it locally */
3632 if (error
&& rv
!= CERT_LOCAL
) {
3633 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3634 if (error
& GNUTLS_CERT_INVALID
)
3635 strlcat(serr
, "invalid, ", sizeof serr
);
3636 if (error
& GNUTLS_CERT_REVOKED
)
3637 strlcat(serr
, "revoked, ", sizeof serr
);
3638 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3639 strlcat(serr
, "signer not found, ", sizeof serr
);
3640 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3641 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3642 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3643 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3644 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3645 strlcat(serr
, "not activated, ", sizeof serr
);
3646 if (error
& GNUTLS_CERT_EXPIRED
)
3647 strlcat(serr
, "expired, ", sizeof serr
);
3648 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3649 if (serr
[i
] == ',') {
3656 stop_tls(gsession
, xcred
);
3662 cert_cmd(struct tab
*t
, struct karg
*args
)
3664 const gchar
*uri
, *error_str
= NULL
;
3668 gnutls_session_t gsession
;
3669 gnutls_x509_crt_t
*certs
;
3670 gnutls_certificate_credentials_t xcred
;
3675 if (ssl_ca_file
== NULL
) {
3676 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3680 if ((uri
= get_uri(t
)) == NULL
) {
3681 show_oops(t
, "Invalid URI");
3685 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
3686 sizeof domain
)) == -1) {
3687 show_oops(t
, "%s", error_str
);
3692 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
3696 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3697 show_oops(t
, "get_connection_certs failed");
3701 if (args
->i
& XT_SHOW
)
3702 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3703 else if (args
->i
& XT_SAVE
)
3704 save_certs(t
, certs
, cert_count
, domain
);
3706 free_connection_certs(certs
, cert_count
);
3708 /* we close the socket first for speed */
3711 stop_tls(gsession
, xcred
);
3712 if (error_str
&& strlen(error_str
))
3713 show_oops(t
, "%s", error_str
);
3718 remove_cookie(int index
)
3724 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3726 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3728 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3732 print_cookie("remove cookie", c
);
3733 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3738 soup_cookies_free(cf
);
3744 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3749 body
= g_strdup("");
3752 if (args
->i
& XT_WL_PERSISTENT
) {
3754 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3756 RB_FOREACH(d
, domain_list
, wl
) {
3760 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3766 if (args
->i
& XT_WL_SESSION
) {
3768 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3770 RB_FOREACH(d
, domain_list
, wl
) {
3774 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3779 tmp
= get_html_page(title
, body
, "", 0);
3782 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3783 else if (wl
== &c_wl
)
3784 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3786 load_webkit_string(t
, tmp
, XT_URI_ABOUT_PLUGINWL
);
3791 #define XT_WL_INVALID (0)
3792 #define XT_WL_JAVASCRIPT (1)
3793 #define XT_WL_COOKIE (2)
3794 #define XT_WL_PLUGIN (3)
3796 wl_save(struct tab
*t
, struct karg
*args
, int list
)
3798 char file
[PATH_MAX
], *lst_str
= NULL
;
3800 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3808 if (t
== NULL
|| args
== NULL
)
3811 if (runtime_settings
[0] == '\0')
3814 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3815 if ((f
= fopen(file
, "r+")) == NULL
)
3819 case XT_WL_JAVASCRIPT
:
3820 lst_str
= "JavaScript";
3821 lt
= g_strdup_printf("js_wl=%s", dom
);
3825 lt
= g_strdup_printf("cookie_wl=%s", dom
);
3829 lt
= g_strdup_printf("pl_wl=%s", dom
);
3832 show_oops(t
, "Invalid list id: %d", list
);
3837 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3838 if (uri
== NULL
|| dom
== NULL
||
3839 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3840 show_oops(t
, "Can't add domain to %s white list", lst_str
);
3845 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3848 if (!strcmp(line
, lt
))
3854 fprintf(f
, "%s\n", lt
);
3859 case XT_WL_JAVASCRIPT
:
3860 d
= wl_find(dom
, &js_wl
);
3862 settings_add("js_wl", dom
);
3863 d
= wl_find(dom
, &js_wl
);
3869 d
= wl_find(dom
, &c_wl
);
3871 settings_add("cookie_wl", dom
);
3872 d
= wl_find(dom
, &c_wl
);
3876 /* find and add to persistent jar */
3877 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3878 for (;cf
; cf
= cf
->next
) {
3880 if (!strcmp(dom
, ci
->domain
) ||
3881 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3882 c
= soup_cookie_copy(ci
);
3883 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3886 soup_cookies_free(cf
);
3890 d
= wl_find(dom
, &pl_wl
);
3892 settings_add("pl_wl", dom
);
3893 d
= wl_find(dom
, &pl_wl
);
3898 abort(); /* can't happen */
3916 js_show_wl(struct tab
*t
, struct karg
*args
)
3918 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3919 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3925 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3927 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3928 wl_show(t
, args
, "Cookie White List", &c_wl
);
3934 cookie_cmd(struct tab
*t
, struct karg
*args
)
3936 if (args
->i
& XT_SHOW
)
3937 wl_show(t
, args
, "Cookie White List", &c_wl
);
3938 else if (args
->i
& XT_WL_TOGGLE
) {
3939 args
->i
|= XT_WL_RELOAD
;
3940 toggle_cwl(t
, args
);
3941 } else if (args
->i
& XT_SAVE
) {
3942 args
->i
|= XT_WL_RELOAD
;
3943 wl_save(t
, args
, XT_WL_COOKIE
);
3944 } else if (args
->i
& XT_DELETE
)
3945 show_oops(t
, "'cookie delete' currently unimplemented");
3951 js_cmd(struct tab
*t
, struct karg
*args
)
3953 if (args
->i
& XT_SHOW
)
3954 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3955 else if (args
->i
& XT_SAVE
) {
3956 args
->i
|= XT_WL_RELOAD
;
3957 wl_save(t
, args
, XT_WL_JAVASCRIPT
);
3958 } else if (args
->i
& XT_WL_TOGGLE
) {
3959 args
->i
|= XT_WL_RELOAD
;
3961 } else if (args
->i
& XT_DELETE
)
3962 show_oops(t
, "'js delete' currently unimplemented");
3968 pl_show_wl(struct tab
*t
, struct karg
*args
)
3970 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3971 wl_show(t
, args
, "Plugin White List", &pl_wl
);
3977 pl_cmd(struct tab
*t
, struct karg
*args
)
3979 if (args
->i
& XT_SHOW
)
3980 wl_show(t
, args
, "Plugin White List", &pl_wl
);
3981 else if (args
->i
& XT_SAVE
) {
3982 args
->i
|= XT_WL_RELOAD
;
3983 wl_save(t
, args
, XT_WL_PLUGIN
);
3984 } else if (args
->i
& XT_WL_TOGGLE
) {
3985 args
->i
|= XT_WL_RELOAD
;
3987 } else if (args
->i
& XT_DELETE
)
3988 show_oops(t
, "'plugin delete' currently unimplemented");
3994 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3996 js_toggle_cb(t
->js_toggle
, t
);
4002 add_favorite(struct tab
*t
, struct karg
*args
)
4004 char file
[PATH_MAX
];
4007 size_t urilen
, linelen
;
4008 const gchar
*uri
, *title
;
4013 /* don't allow adding of xtp pages to favorites */
4014 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4015 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4019 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4020 if ((f
= fopen(file
, "r+")) == NULL
) {
4021 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4025 title
= get_title(t
, FALSE
);
4028 if (title
== NULL
|| uri
== NULL
) {
4029 show_oops(t
, "can't add page to favorites");
4033 urilen
= strlen(uri
);
4036 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4037 if (feof(f
) || ferror(f
))
4040 if (linelen
== urilen
&& !strcmp(line
, uri
))
4047 fprintf(f
, "\n%s\n%s", title
, uri
);
4053 update_favorite_tabs(NULL
);
4059 can_go_back_for_real(struct tab
*t
)
4062 WebKitWebHistoryItem
*item
;
4068 /* rely on webkit to make sure we can go backward when on an about page */
4070 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4071 return (webkit_web_view_can_go_back(t
->wv
));
4073 /* the back/forwars list is stupid so help determine if we can go back */
4074 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4076 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4077 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4085 can_go_forward_for_real(struct tab
*t
)
4088 WebKitWebHistoryItem
*item
;
4094 /* rely on webkit to make sure we can go forward when on an about page */
4096 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4097 return (webkit_web_view_can_go_forward(t
->wv
));
4099 /* the back/forwars list is stupid so help selecting a different item */
4100 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4102 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4103 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4111 go_back_for_real(struct tab
*t
)
4114 WebKitWebHistoryItem
*item
;
4122 webkit_web_view_go_back(t
->wv
);
4125 /* the back/forwars list is stupid so help selecting a different item */
4126 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4128 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4129 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4130 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4137 go_forward_for_real(struct tab
*t
)
4140 WebKitWebHistoryItem
*item
;
4148 webkit_web_view_go_forward(t
->wv
);
4151 /* the back/forwars list is stupid so help selecting a different item */
4152 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4154 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4155 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4156 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4163 navaction(struct tab
*t
, struct karg
*args
)
4165 WebKitWebHistoryItem
*item
;
4166 WebKitWebFrame
*frame
;
4168 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4169 t
->tab_id
, args
->i
);
4171 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4173 if (args
->i
== XT_NAV_BACK
)
4174 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4176 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4178 return (XT_CB_PASSTHROUGH
);
4179 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4181 return (XT_CB_PASSTHROUGH
);
4187 go_back_for_real(t
);
4189 case XT_NAV_FORWARD
:
4191 go_forward_for_real(t
);
4194 frame
= webkit_web_view_get_main_frame(t
->wv
);
4195 webkit_web_frame_reload(frame
);
4198 return (XT_CB_PASSTHROUGH
);
4202 move(struct tab
*t
, struct karg
*args
)
4204 GtkAdjustment
*adjust
;
4205 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4211 case XT_MOVE_BOTTOM
:
4213 case XT_MOVE_PAGEDOWN
:
4214 case XT_MOVE_PAGEUP
:
4215 case XT_MOVE_HALFDOWN
:
4216 case XT_MOVE_HALFUP
:
4217 case XT_MOVE_PERCENT
:
4218 adjust
= t
->adjust_v
;
4221 adjust
= t
->adjust_h
;
4225 pos
= gtk_adjustment_get_value(adjust
);
4226 ps
= gtk_adjustment_get_page_size(adjust
);
4227 upper
= gtk_adjustment_get_upper(adjust
);
4228 lower
= gtk_adjustment_get_lower(adjust
);
4229 si
= gtk_adjustment_get_step_increment(adjust
);
4230 pi
= gtk_adjustment_get_page_increment(adjust
);
4233 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4234 "max %f si %f pi %f\n",
4235 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4236 pos
, ps
, upper
, lower
, max
, si
, pi
);
4242 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4247 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4249 case XT_MOVE_BOTTOM
:
4250 case XT_MOVE_FARRIGHT
:
4251 gtk_adjustment_set_value(adjust
, max
);
4254 case XT_MOVE_FARLEFT
:
4255 gtk_adjustment_set_value(adjust
, lower
);
4257 case XT_MOVE_PAGEDOWN
:
4259 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4261 case XT_MOVE_PAGEUP
:
4263 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4265 case XT_MOVE_HALFDOWN
:
4267 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4269 case XT_MOVE_HALFUP
:
4271 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4273 case XT_MOVE_PERCENT
:
4274 percent
= atoi(args
->s
) / 100.0;
4275 pos
= max
* percent
;
4276 if (pos
< 0.0 || pos
> max
)
4278 gtk_adjustment_set_value(adjust
, pos
);
4281 return (XT_CB_PASSTHROUGH
);
4284 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4286 return (XT_CB_HANDLED
);
4290 url_set_visibility(void)
4294 TAILQ_FOREACH(t
, &tabs
, entry
)
4295 if (show_url
== 0) {
4296 gtk_widget_hide(t
->toolbar
);
4299 gtk_widget_show(t
->toolbar
);
4303 notebook_tab_set_visibility(void)
4305 if (show_tabs
== 0) {
4306 gtk_widget_hide(tab_bar
);
4307 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4309 if (tab_style
== XT_TABS_NORMAL
) {
4310 gtk_widget_hide(tab_bar
);
4311 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4312 } else if (tab_style
== XT_TABS_COMPACT
) {
4313 gtk_widget_show(tab_bar
);
4314 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4320 statusbar_set_visibility(void)
4324 TAILQ_FOREACH(t
, &tabs
, entry
)
4325 if (show_statusbar
== 0) {
4326 gtk_widget_hide(t
->statusbar_box
);
4329 gtk_widget_show(t
->statusbar_box
);
4333 url_set(struct tab
*t
, int enable_url_entry
)
4338 show_url
= enable_url_entry
;
4340 if (enable_url_entry
) {
4341 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4342 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4343 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4345 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4346 GTK_ENTRY_ICON_PRIMARY
);
4348 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4349 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4350 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4351 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4357 fullscreen(struct tab
*t
, struct karg
*args
)
4359 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4362 return (XT_CB_PASSTHROUGH
);
4364 if (show_url
== 0) {
4372 url_set_visibility();
4373 notebook_tab_set_visibility();
4375 return (XT_CB_HANDLED
);
4379 statustoggle(struct tab
*t
, struct karg
*args
)
4381 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4383 if (show_statusbar
== 1) {
4385 statusbar_set_visibility();
4386 } else if (show_statusbar
== 0) {
4388 statusbar_set_visibility();
4390 return (XT_CB_HANDLED
);
4394 urlaction(struct tab
*t
, struct karg
*args
)
4396 int rv
= XT_CB_HANDLED
;
4398 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4401 return (XT_CB_PASSTHROUGH
);
4405 if (show_url
== 0) {
4407 url_set_visibility();
4411 if (show_url
== 1) {
4413 url_set_visibility();
4421 tabaction(struct tab
*t
, struct karg
*args
)
4423 int rv
= XT_CB_HANDLED
;
4424 char *url
= args
->s
;
4428 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4431 return (XT_CB_PASSTHROUGH
);
4435 if (strlen(url
) > 0)
4436 create_new_tab(url
, NULL
, 1, args
->precount
);
4438 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4441 if (args
->precount
< 0)
4444 TAILQ_FOREACH(tt
, &tabs
, entry
)
4445 if (tt
->tab_id
== args
->precount
- 1) {
4450 case XT_TAB_DELQUIT
:
4451 if (gtk_notebook_get_n_pages(notebook
) > 1)
4457 if (strlen(url
) > 0)
4460 rv
= XT_CB_PASSTHROUGH
;
4466 if (show_tabs
== 0) {
4468 notebook_tab_set_visibility();
4472 if (show_tabs
== 1) {
4474 notebook_tab_set_visibility();
4477 case XT_TAB_NEXTSTYLE
:
4478 if (tab_style
== XT_TABS_NORMAL
) {
4479 tab_style
= XT_TABS_COMPACT
;
4480 recolor_compact_tabs();
4483 tab_style
= XT_TABS_NORMAL
;
4484 notebook_tab_set_visibility();
4486 case XT_TAB_UNDO_CLOSE
:
4487 if (undo_count
== 0) {
4488 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4493 u
= TAILQ_FIRST(&undos
);
4494 create_new_tab(u
->uri
, u
, 1, -1);
4496 TAILQ_REMOVE(&undos
, u
, entry
);
4498 /* u->history is freed in create_new_tab() */
4503 rv
= XT_CB_PASSTHROUGH
;
4517 resizetab(struct tab
*t
, struct karg
*args
)
4519 if (t
== NULL
|| args
== NULL
) {
4520 show_oops(NULL
, "resizetab invalid parameters");
4521 return (XT_CB_PASSTHROUGH
);
4524 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4525 t
->tab_id
, args
->i
);
4527 setzoom_webkit(t
, args
->i
);
4529 return (XT_CB_HANDLED
);
4533 movetab(struct tab
*t
, struct karg
*args
)
4537 if (t
== NULL
|| args
== NULL
) {
4538 show_oops(NULL
, "movetab invalid parameters");
4539 return (XT_CB_PASSTHROUGH
);
4542 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4543 t
->tab_id
, args
->i
);
4545 if (args
->i
>= XT_TAB_INVALID
)
4546 return (XT_CB_PASSTHROUGH
);
4548 if (TAILQ_EMPTY(&tabs
))
4549 return (XT_CB_PASSTHROUGH
);
4551 n
= gtk_notebook_get_n_pages(notebook
);
4552 dest
= gtk_notebook_get_current_page(notebook
);
4556 if (args
->precount
< 0)
4557 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4559 dest
= args
->precount
- 1;
4563 if (args
->precount
< 0)
4566 dest
-= args
->precount
% n
;
4579 return (XT_CB_PASSTHROUGH
);
4582 if (dest
< 0 || dest
>= n
)
4583 return (XT_CB_PASSTHROUGH
);
4584 if (t
->tab_id
== dest
) {
4585 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4586 return (XT_CB_HANDLED
);
4589 set_current_tab(dest
);
4591 return (XT_CB_HANDLED
);
4597 command_mode(struct tab
*t
, struct karg
*args
)
4599 if (args
->i
== XT_MODE_COMMAND
)
4600 run_script(t
, "hints.clearFocus();");
4602 run_script(t
, "hints.focusInput();");
4603 return (XT_CB_HANDLED
);
4607 command(struct tab
*t
, struct karg
*args
)
4609 char *s
= NULL
, *ss
= NULL
;
4614 if (t
== NULL
|| args
== NULL
) {
4615 show_oops(NULL
, "command invalid parameters");
4616 return (XT_CB_PASSTHROUGH
);
4627 if (cmd_prefix
== 0)
4630 ss
= g_strdup_printf(":%d", cmd_prefix
);
4636 bzero(&a
, sizeof a
);
4642 bzero(&a
, sizeof a
);
4643 a
.i
= XT_HINT_NEWTAB
;
4653 case XT_CMD_OPEN_CURRENT
:
4656 case XT_CMD_TABNEW_CURRENT
:
4657 if (!s
) /* FALL THROUGH? */
4659 if ((uri
= get_uri(t
)) != NULL
) {
4660 ss
= g_strdup_printf("%s%s", s
, uri
);
4665 show_oops(t
, "command: invalid opcode %d", args
->i
);
4666 return (XT_CB_PASSTHROUGH
);
4669 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4671 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4672 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4673 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4675 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4676 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4681 return (XT_CB_HANDLED
);
4685 * Return a new string with a download row (in html)
4686 * appended. Old string is freed.
4689 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4692 WebKitDownloadStatus stat
;
4693 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4695 char cur_sz
[FMT_SCALED_STRSIZE
];
4696 char tot_sz
[FMT_SCALED_STRSIZE
];
4699 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4701 /* All actions wil take this form:
4702 * xxxt://class/seskey
4704 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4705 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4707 stat
= webkit_download_get_status(dl
->download
);
4710 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4711 status_html
= g_strdup_printf("Finished");
4712 cmd_html
= g_strdup_printf(
4713 "<a href='%s%d/%d'>Remove</a>",
4714 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4716 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4717 /* gather size info */
4718 progress
= 100 * webkit_download_get_progress(dl
->download
);
4721 webkit_download_get_current_size(dl
->download
), cur_sz
);
4723 webkit_download_get_total_size(dl
->download
), tot_sz
);
4725 status_html
= g_strdup_printf(
4726 "<div style='width: 100%%' align='center'>"
4727 "<div class='progress-outer'>"
4728 "<div class='progress-inner' style='width: %.2f%%'>"
4729 "</div></div></div>"
4730 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4731 progress
, cur_sz
, tot_sz
, progress
);
4733 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4734 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4738 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4739 status_html
= g_strdup_printf("Cancelled");
4740 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4741 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4743 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4744 status_html
= g_strdup_printf("Error!");
4745 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4746 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4748 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4749 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4750 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4751 status_html
= g_strdup_printf("Starting");
4754 show_oops(t
, "%s: unknown download status", __func__
);
4757 new_html
= g_strdup_printf(
4758 "%s\n<tr><td>%s</td><td>%s</td>"
4759 "<td style='text-align:center'>%s</td></tr>\n",
4760 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4761 status_html
, cmd_html
);
4765 g_free(status_html
);
4776 * update all download tabs apart from one. Pass NULL if
4777 * you want to update all.
4780 update_download_tabs(struct tab
*apart_from
)
4783 if (!updating_dl_tabs
) {
4784 updating_dl_tabs
= 1; /* stop infinite recursion */
4785 TAILQ_FOREACH(t
, &tabs
, entry
)
4786 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4787 && (t
!= apart_from
))
4788 xtp_page_dl(t
, NULL
);
4789 updating_dl_tabs
= 0;
4794 * update all cookie tabs apart from one. Pass NULL if
4795 * you want to update all.
4798 update_cookie_tabs(struct tab
*apart_from
)
4801 if (!updating_cl_tabs
) {
4802 updating_cl_tabs
= 1; /* stop infinite recursion */
4803 TAILQ_FOREACH(t
, &tabs
, entry
)
4804 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4805 && (t
!= apart_from
))
4806 xtp_page_cl(t
, NULL
);
4807 updating_cl_tabs
= 0;
4812 * update all history tabs apart from one. Pass NULL if
4813 * you want to update all.
4816 update_history_tabs(struct tab
*apart_from
)
4820 if (!updating_hl_tabs
) {
4821 updating_hl_tabs
= 1; /* stop infinite recursion */
4822 TAILQ_FOREACH(t
, &tabs
, entry
)
4823 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4824 && (t
!= apart_from
))
4825 xtp_page_hl(t
, NULL
);
4826 updating_hl_tabs
= 0;
4830 /* cookie management XTP page */
4832 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4834 char *body
, *page
, *tmp
;
4835 int i
= 1; /* all ids start 1 */
4836 GSList
*sc
, *pc
, *pc_start
;
4838 char *type
, *table_headers
, *last_domain
;
4840 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4843 show_oops(NULL
, "%s invalid parameters", __func__
);
4847 /* Generate a new session key */
4848 if (!updating_cl_tabs
)
4849 generate_xtp_session_key(&cl_session_key
);
4852 table_headers
= g_strdup_printf("<table><tr>"
4855 "<th style='width:200px'>Value</th>"
4859 "<th>HTTP<br />only</th>"
4860 "<th style='width:40px'>Rm</th></tr>\n");
4862 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4863 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4867 last_domain
= strdup("");
4868 for (; sc
; sc
= sc
->next
) {
4871 if (strcmp(last_domain
, c
->domain
) != 0) {
4874 last_domain
= strdup(c
->domain
);
4878 body
= g_strdup_printf("%s</table>"
4880 body
, c
->domain
, table_headers
);
4884 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4885 c
->domain
, table_headers
);
4890 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4891 if (soup_cookie_equal(pc
->data
, c
)) {
4892 type
= "Session + Persistent";
4897 body
= g_strdup_printf(
4900 "<td style='word-wrap:normal'>%s</td>"
4902 " <textarea rows='4'>%s</textarea>"
4908 "<td style='text-align:center'>"
4909 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4916 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4931 soup_cookies_free(sc
);
4932 soup_cookies_free(pc
);
4934 /* small message if there are none */
4936 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4937 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4940 body
= g_strdup_printf("%s</table>", body
);
4943 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4945 g_free(table_headers
);
4946 g_free(last_domain
);
4948 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4949 update_cookie_tabs(t
);
4957 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4959 char *body
, *page
, *tmp
;
4961 int i
= 1; /* all ids start 1 */
4963 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4966 show_oops(NULL
, "%s invalid parameters", __func__
);
4970 /* Generate a new session key */
4971 if (!updating_hl_tabs
)
4972 generate_xtp_session_key(&hl_session_key
);
4975 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4976 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4978 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4980 body
= g_strdup_printf(
4982 "<td><a href='%s'>%s</a></td>"
4984 "<td style='text-align: center'>"
4985 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4986 body
, h
->uri
, h
->uri
, h
->title
,
4987 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4988 XT_XTP_HL_REMOVE
, i
);
4994 /* small message if there are none */
4997 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4998 "colspan='3'>No History</td></tr>\n", body
);
5003 body
= g_strdup_printf("%s</table>", body
);
5006 page
= get_html_page("History", body
, "", TRUE
);
5010 * update all history manager tabs as the xtp session
5011 * key has now changed. No need to update the current tab.
5012 * Already did that above.
5014 update_history_tabs(t
);
5016 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5023 * Generate a web page detailing the status of any downloads
5026 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5028 struct download
*dl
;
5029 char *body
, *page
, *tmp
;
5033 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5036 show_oops(NULL
, "%s invalid parameters", __func__
);
5041 * Generate a new session key for next page instance.
5042 * This only happens for the top level call to xtp_page_dl()
5043 * in which case updating_dl_tabs is 0.
5045 if (!updating_dl_tabs
)
5046 generate_xtp_session_key(&dl_session_key
);
5048 /* header - with refresh so as to update */
5049 if (refresh_interval
>= 1)
5050 ref
= g_strdup_printf(
5051 "<meta http-equiv='refresh' content='%u"
5052 ";url=%s%d/%s/%d' />\n",
5061 body
= g_strdup_printf("<div align='center'>"
5062 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5063 "</p><table><tr><th style='width: 60%%'>"
5064 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5065 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5067 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5068 body
= xtp_page_dl_row(t
, body
, dl
);
5072 /* message if no downloads in list */
5075 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5076 " style='text-align: center'>"
5077 "No downloads</td></tr>\n", body
);
5082 body
= g_strdup_printf("%s</table></div>", body
);
5085 page
= get_html_page("Downloads", body
, ref
, 1);
5090 * update all download manager tabs as the xtp session
5091 * key has now changed. No need to update the current tab.
5092 * Already did that above.
5094 update_download_tabs(t
);
5096 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5103 search(struct tab
*t
, struct karg
*args
)
5107 if (t
== NULL
|| args
== NULL
) {
5108 show_oops(NULL
, "search invalid parameters");
5113 case XT_SEARCH_NEXT
:
5114 d
= t
->search_forward
;
5116 case XT_SEARCH_PREV
:
5117 d
= !t
->search_forward
;
5120 return (XT_CB_PASSTHROUGH
);
5123 if (t
->search_text
== NULL
) {
5124 if (global_search
== NULL
)
5125 return (XT_CB_PASSTHROUGH
);
5127 d
= t
->search_forward
= TRUE
;
5128 t
->search_text
= g_strdup(global_search
);
5129 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5130 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5134 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5135 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5137 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5139 return (XT_CB_HANDLED
);
5142 struct settings_args
{
5148 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5151 struct settings_args
*sa
= cb_args
;
5156 if (s
->flags
& XT_SF_RUNTIME
)
5162 *sa
->body
= g_strdup_printf(
5164 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5165 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5177 set_show(struct tab
*t
, struct karg
*args
)
5179 char *body
, *page
, *tmp
;
5181 struct settings_args sa
;
5183 bzero(&sa
, sizeof sa
);
5187 body
= g_strdup_printf("<div align='center'><table><tr>"
5188 "<th align='left'>Setting</th>"
5189 "<th align='left'>Value</th></tr>\n");
5191 settings_walk(print_setting
, &sa
);
5194 /* small message if there are none */
5197 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5198 "colspan='2'>No settings</td></tr>\n", body
);
5203 body
= g_strdup_printf("%s</table></div>", body
);
5206 page
= get_html_page("Settings", body
, "", 0);
5210 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5214 return (XT_CB_PASSTHROUGH
);
5218 set(struct tab
*t
, struct karg
*args
)
5223 if (args
== NULL
|| args
->s
== NULL
)
5224 return (set_show(t
, args
));
5227 p
= g_strstrip(args
->s
);
5230 return (set_show(t
, args
));
5232 /* we got some sort of string */
5233 val
= g_strrstr(p
, "=");
5236 val
= g_strchomp(val
);
5239 for (i
= 0; i
< LENGTH(rs
); i
++) {
5240 if (strcmp(rs
[i
].name
, p
))
5243 if (rs
[i
].activate
) {
5244 if (rs
[i
].activate(val
))
5245 show_oops(t
, "%s invalid value %s",
5248 show_oops(t
, ":set %s = %s", p
, val
);
5251 show_oops(t
, "not a runtime option: %s", p
);
5255 show_oops(t
, "unknown option: %s", p
);
5259 for (i
= 0; i
< LENGTH(rs
); i
++) {
5260 if (strcmp(rs
[i
].name
, p
))
5263 /* XXX this could use some cleanup */
5264 switch (rs
[i
].type
) {
5267 show_oops(t
, "%s = %d",
5268 rs
[i
].name
, *rs
[i
].ival
);
5269 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5270 show_oops(t
, "%s = %s",
5272 rs
[i
].s
->get(&rs
[i
]));
5273 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5274 show_oops(t
, "%s = ...", rs
[i
].name
);
5276 show_oops(t
, "%s = ", rs
[i
].name
);
5280 show_oops(t
, "%s = %f",
5281 rs
[i
].name
, *rs
[i
].fval
);
5282 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5283 show_oops(t
, "%s = %s",
5285 rs
[i
].s
->get(&rs
[i
]));
5286 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5287 show_oops(t
, "%s = ...", rs
[i
].name
);
5289 show_oops(t
, "%s = ", rs
[i
].name
);
5292 if (rs
[i
].sval
&& *rs
[i
].sval
)
5293 show_oops(t
, "%s = %s",
5294 rs
[i
].name
, *rs
[i
].sval
);
5295 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5296 show_oops(t
, "%s = %s",
5298 rs
[i
].s
->get(&rs
[i
]));
5299 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5300 show_oops(t
, "%s = ...", rs
[i
].name
);
5302 show_oops(t
, "%s = ", rs
[i
].name
);
5305 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5311 show_oops(t
, "unknown option: %s", p
);
5314 return (XT_CB_PASSTHROUGH
);
5318 session_save(struct tab
*t
, char *filename
)
5324 if (strlen(filename
) == 0)
5327 if (filename
[0] == '.' || filename
[0] == '/')
5331 if (save_tabs(t
, &a
))
5333 strlcpy(named_session
, filename
, sizeof named_session
);
5335 /* add the new session to the list of sessions */
5336 s
= g_malloc(sizeof(struct session
));
5337 s
->name
= g_strdup(filename
);
5338 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5346 session_open(struct tab
*t
, char *filename
)
5351 if (strlen(filename
) == 0)
5354 if (filename
[0] == '.' || filename
[0] == '/')
5358 a
.i
= XT_SES_CLOSETABS
;
5359 if (open_tabs(t
, &a
))
5362 strlcpy(named_session
, filename
, sizeof named_session
);
5370 session_delete(struct tab
*t
, char *filename
)
5372 char file
[PATH_MAX
];
5376 if (strlen(filename
) == 0)
5379 if (filename
[0] == '.' || filename
[0] == '/')
5382 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5386 if (!strcmp(filename
, named_session
))
5387 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5388 sizeof named_session
);
5390 /* remove session from sessions list */
5391 TAILQ_FOREACH(s
, &sessions
, entry
) {
5392 if (!strcmp(s
->name
, filename
))
5397 TAILQ_REMOVE(&sessions
, s
, entry
);
5398 g_free((gpointer
) s
->name
);
5407 session_cmd(struct tab
*t
, struct karg
*args
)
5409 char *filename
= args
->s
;
5414 if (args
->i
& XT_SHOW
)
5415 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5416 XT_SAVED_TABS_FILE
: named_session
);
5417 else if (args
->i
& XT_SAVE
) {
5418 if (session_save(t
, filename
)) {
5419 show_oops(t
, "Can't save session: %s",
5420 filename
? filename
: "INVALID");
5423 } else if (args
->i
& XT_OPEN
) {
5424 if (session_open(t
, filename
)) {
5425 show_oops(t
, "Can't open session: %s",
5426 filename
? filename
: "INVALID");
5429 } else if (args
->i
& XT_DELETE
) {
5430 if (session_delete(t
, filename
)) {
5431 show_oops(t
, "Can't delete session: %s",
5432 filename
? filename
: "INVALID");
5437 return (XT_CB_PASSTHROUGH
);
5441 script_cmd(struct tab
*t
, struct karg
*args
)
5443 JSGlobalContextRef ctx
;
5444 WebKitWebFrame
*frame
;
5446 JSValueRef val
, exception
;
5455 if ((f
= fopen(args
->s
, "r")) == NULL
) {
5456 show_oops(t
, "Can't open script file: %s", args
->s
);
5460 if (fstat(fileno(f
), &sb
) == -1) {
5461 show_oops(t
, "Can't stat script file: %s", args
->s
);
5465 buf
= g_malloc0(sb
.st_size
+ 1);
5466 if (fread(buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
5467 show_oops(t
, "Can't read script file: %s", args
->s
);
5471 /* this code needs to be redone */
5472 frame
= webkit_web_view_get_main_frame(t
->wv
);
5473 ctx
= webkit_web_frame_get_global_context(frame
);
5475 str
= JSStringCreateWithUTF8CString(buf
);
5476 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
5477 NULL
, 0, &exception
);
5478 JSStringRelease(str
);
5480 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
5482 es
= js_ref_to_string(ctx
, exception
);
5484 show_oops(t
, "script exception: %s", es
);
5489 es
= js_ref_to_string(ctx
, val
);
5492 if (!strncmp(es
, XT_JS_DONE
, XT_JS_DONE_LEN
))
5494 if (!strncmp(es
, XT_JS_INSERT
, XT_JS_INSERT_LEN
))
5498 show_oops(t
, "script complete return value: '%s'", es
);
5501 show_oops(t
, "script complete: without a return value");
5510 return (XT_CB_PASSTHROUGH
);
5514 * Make a hardcopy of the page
5517 print_page(struct tab
*t
, struct karg
*args
)
5519 WebKitWebFrame
*frame
;
5521 GtkPrintOperation
*op
;
5522 GtkPrintOperationAction action
;
5523 GtkPrintOperationResult print_res
;
5524 GError
*g_err
= NULL
;
5525 int marg_l
, marg_r
, marg_t
, marg_b
;
5527 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5529 ps
= gtk_page_setup_new();
5530 op
= gtk_print_operation_new();
5531 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5532 frame
= webkit_web_view_get_main_frame(t
->wv
);
5534 /* the default margins are too small, so we will bump them */
5535 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5536 XT_PRINT_EXTRA_MARGIN
;
5537 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5538 XT_PRINT_EXTRA_MARGIN
;
5539 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5540 XT_PRINT_EXTRA_MARGIN
;
5541 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5542 XT_PRINT_EXTRA_MARGIN
;
5545 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5546 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5547 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5548 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5550 gtk_print_operation_set_default_page_setup(op
, ps
);
5552 /* this appears to free 'op' and 'ps' */
5553 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5555 /* check it worked */
5556 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5557 show_oops(NULL
, "can't print: %s", g_err
->message
);
5558 g_error_free (g_err
);
5566 go_home(struct tab
*t
, struct karg
*args
)
5573 set_encoding(struct tab
*t
, struct karg
*args
)
5577 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5578 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5580 e
= webkit_web_view_get_encoding(t
->wv
);
5581 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5583 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5589 restart(struct tab
*t
, struct karg
*args
)
5593 a
.s
= XT_RESTART_TABS_FILE
;
5595 execvp(start_argv
[0], start_argv
);
5601 #define CTRL GDK_CONTROL_MASK
5602 #define MOD1 GDK_MOD1_MASK
5603 #define SHFT GDK_SHIFT_MASK
5605 /* inherent to GTK not all keys will be caught at all times */
5606 /* XXX sort key bindings */
5607 struct key_binding
{
5612 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5614 { "command_mode", 0, 0, GDK_Escape
},
5615 { "insert_mode", 0, 0, GDK_i
},
5616 { "cookiejar", MOD1
, 0, GDK_j
},
5617 { "downloadmgr", MOD1
, 0, GDK_d
},
5618 { "history", MOD1
, 0, GDK_h
},
5619 { "print", CTRL
, 0, GDK_p
},
5620 { "search", 0, 0, GDK_slash
},
5621 { "searchb", 0, 0, GDK_question
},
5622 { "statustoggle", CTRL
, 0, GDK_n
},
5623 { "command", 0, 0, GDK_colon
},
5624 { "qa", CTRL
, 0, GDK_q
},
5625 { "restart", MOD1
, 0, GDK_q
},
5626 { "js toggle", CTRL
, 0, GDK_j
},
5627 { "cookie toggle", MOD1
, 0, GDK_c
},
5628 { "togglesrc", CTRL
, 0, GDK_s
},
5629 { "yankuri", 0, 0, GDK_y
},
5630 { "pasteuricur", 0, 0, GDK_p
},
5631 { "pasteurinew", 0, 0, GDK_P
},
5632 { "toplevel toggle", 0, 0, GDK_F4
},
5633 { "help", 0, 0, GDK_F1
},
5634 { "run_script", MOD1
, 0, GDK_r
},
5637 { "searchnext", 0, 0, GDK_n
},
5638 { "searchprevious", 0, 0, GDK_N
},
5641 { "focusaddress", 0, 0, GDK_F6
},
5642 { "focussearch", 0, 0, GDK_F7
},
5645 { "hinting", 0, 0, GDK_f
},
5646 { "hinting", 0, 0, GDK_period
},
5647 { "hinting_newtab", SHFT
, 0, GDK_F
},
5648 { "hinting_newtab", 0, 0, GDK_comma
},
5650 /* custom stylesheet */
5651 { "userstyle", 0, 0, GDK_s
},
5654 { "goback", 0, 0, GDK_BackSpace
},
5655 { "goback", MOD1
, 0, GDK_Left
},
5656 { "goforward", SHFT
, 0, GDK_BackSpace
},
5657 { "goforward", MOD1
, 0, GDK_Right
},
5658 { "reload", 0, 0, GDK_F5
},
5659 { "reload", CTRL
, 0, GDK_r
},
5660 { "reload", CTRL
, 0, GDK_l
},
5661 { "favorites", MOD1
, 1, GDK_f
},
5663 /* vertical movement */
5664 { "scrolldown", 0, 0, GDK_j
},
5665 { "scrolldown", 0, 0, GDK_Down
},
5666 { "scrollup", 0, 0, GDK_Up
},
5667 { "scrollup", 0, 0, GDK_k
},
5668 { "scrollbottom", 0, 0, GDK_G
},
5669 { "scrollbottom", 0, 0, GDK_End
},
5670 { "scrolltop", 0, 0, GDK_Home
},
5671 { "scrollpagedown", 0, 0, GDK_space
},
5672 { "scrollpagedown", CTRL
, 0, GDK_f
},
5673 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5674 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5675 { "scrollpageup", 0, 0, GDK_Page_Up
},
5676 { "scrollpageup", CTRL
, 0, GDK_b
},
5677 { "scrollhalfup", CTRL
, 0, GDK_u
},
5678 /* horizontal movement */
5679 { "scrollright", 0, 0, GDK_l
},
5680 { "scrollright", 0, 0, GDK_Right
},
5681 { "scrollleft", 0, 0, GDK_Left
},
5682 { "scrollleft", 0, 0, GDK_h
},
5683 { "scrollfarright", 0, 0, GDK_dollar
},
5684 { "scrollfarleft", 0, 0, GDK_0
},
5687 { "tabnew", CTRL
, 0, GDK_t
},
5688 { "999tabnew", CTRL
, 0, GDK_T
},
5689 { "tabclose", CTRL
, 1, GDK_w
},
5690 { "tabundoclose", 0, 0, GDK_U
},
5691 { "tabnext 1", CTRL
, 0, GDK_1
},
5692 { "tabnext 2", CTRL
, 0, GDK_2
},
5693 { "tabnext 3", CTRL
, 0, GDK_3
},
5694 { "tabnext 4", CTRL
, 0, GDK_4
},
5695 { "tabnext 5", CTRL
, 0, GDK_5
},
5696 { "tabnext 6", CTRL
, 0, GDK_6
},
5697 { "tabnext 7", CTRL
, 0, GDK_7
},
5698 { "tabnext 8", CTRL
, 0, GDK_8
},
5699 { "tabnext 9", CTRL
, 0, GDK_9
},
5700 { "tabfirst", CTRL
, 0, GDK_less
},
5701 { "tablast", CTRL
, 0, GDK_greater
},
5702 { "tabprevious", CTRL
, 0, GDK_Left
},
5703 { "tabnext", CTRL
, 0, GDK_Right
},
5704 { "focusout", CTRL
, 0, GDK_minus
},
5705 { "focusin", CTRL
, 0, GDK_plus
},
5706 { "focusin", CTRL
, 0, GDK_equal
},
5707 { "focusreset", CTRL
, 0, GDK_0
},
5709 /* command aliases (handy when -S flag is used) */
5710 { "promptopen", 0, 0, GDK_F9
},
5711 { "promptopencurrent", 0, 0, GDK_F10
},
5712 { "prompttabnew", 0, 0, GDK_F11
},
5713 { "prompttabnewcurrent",0, 0, GDK_F12
},
5715 TAILQ_HEAD(keybinding_list
, key_binding
);
5718 walk_kb(struct settings
*s
,
5719 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5721 struct key_binding
*k
;
5724 if (s
== NULL
|| cb
== NULL
) {
5725 show_oops(NULL
, "walk_kb invalid parameters");
5729 TAILQ_FOREACH(k
, &kbl
, entry
) {
5735 if (gdk_keyval_name(k
->key
) == NULL
)
5738 strlcat(str
, k
->cmd
, sizeof str
);
5739 strlcat(str
, ",", sizeof str
);
5741 if (k
->mask
& GDK_SHIFT_MASK
)
5742 strlcat(str
, "S-", sizeof str
);
5743 if (k
->mask
& GDK_CONTROL_MASK
)
5744 strlcat(str
, "C-", sizeof str
);
5745 if (k
->mask
& GDK_MOD1_MASK
)
5746 strlcat(str
, "M1-", sizeof str
);
5747 if (k
->mask
& GDK_MOD2_MASK
)
5748 strlcat(str
, "M2-", sizeof str
);
5749 if (k
->mask
& GDK_MOD3_MASK
)
5750 strlcat(str
, "M3-", sizeof str
);
5751 if (k
->mask
& GDK_MOD4_MASK
)
5752 strlcat(str
, "M4-", sizeof str
);
5753 if (k
->mask
& GDK_MOD5_MASK
)
5754 strlcat(str
, "M5-", sizeof str
);
5756 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5757 cb(s
, str
, cb_args
);
5762 init_keybindings(void)
5765 struct key_binding
*k
;
5767 for (i
= 0; i
< LENGTH(keys
); i
++) {
5768 k
= g_malloc0(sizeof *k
);
5769 k
->cmd
= keys
[i
].cmd
;
5770 k
->mask
= keys
[i
].mask
;
5771 k
->use_in_entry
= keys
[i
].use_in_entry
;
5772 k
->key
= keys
[i
].key
;
5773 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5775 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5776 k
->cmd
? k
->cmd
: "unnamed key");
5781 keybinding_clearall(void)
5783 struct key_binding
*k
, *next
;
5785 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5786 next
= TAILQ_NEXT(k
, entry
);
5790 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5791 k
->cmd
? k
->cmd
: "unnamed key");
5792 TAILQ_REMOVE(&kbl
, k
, entry
);
5798 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5800 struct key_binding
*k
;
5801 guint keyval
, mask
= 0;
5804 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5806 /* Keys which are to be used in entry have been prefixed with an
5807 * exclamation mark. */
5811 /* find modifier keys */
5812 if (strstr(key
, "S-"))
5813 mask
|= GDK_SHIFT_MASK
;
5814 if (strstr(key
, "C-"))
5815 mask
|= GDK_CONTROL_MASK
;
5816 if (strstr(key
, "M1-"))
5817 mask
|= GDK_MOD1_MASK
;
5818 if (strstr(key
, "M2-"))
5819 mask
|= GDK_MOD2_MASK
;
5820 if (strstr(key
, "M3-"))
5821 mask
|= GDK_MOD3_MASK
;
5822 if (strstr(key
, "M4-"))
5823 mask
|= GDK_MOD4_MASK
;
5824 if (strstr(key
, "M5-"))
5825 mask
|= GDK_MOD5_MASK
;
5828 for (i
= strlen(key
) - 1; i
> 0; i
--)
5832 /* validate keyname */
5833 keyval
= gdk_keyval_from_name(key
);
5834 if (keyval
== GDK_VoidSymbol
) {
5835 warnx("invalid keybinding name %s", key
);
5838 /* must run this test too, gtk+ doesn't handle 10 for example */
5839 if (gdk_keyval_name(keyval
) == NULL
) {
5840 warnx("invalid keybinding name %s", key
);
5844 /* Remove eventual dupes. */
5845 TAILQ_FOREACH(k
, &kbl
, entry
)
5846 if (k
->key
== keyval
&& k
->mask
== mask
) {
5847 TAILQ_REMOVE(&kbl
, k
, entry
);
5853 k
= g_malloc0(sizeof *k
);
5854 k
->cmd
= g_strdup(cmd
);
5856 k
->use_in_entry
= use_in_entry
;
5859 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5864 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5865 k
->cmd
, gdk_keyval_name(keyval
));
5867 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5873 add_kb(struct settings
*s
, char *entry
)
5877 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5879 /* clearall is special */
5880 if (!strcmp(entry
, "clearall")) {
5881 keybinding_clearall();
5885 kb
= strstr(entry
, ",");
5891 return (keybinding_add(entry
, key
, key
[0] == '!'));
5897 int (*func
)(struct tab
*, struct karg
*);
5901 { "command_mode", 0, command_mode
, XT_MODE_COMMAND
, 0 },
5902 { "insert_mode", 0, command_mode
, XT_MODE_INSERT
, 0 },
5903 { "command", 0, command
, ':', 0 },
5904 { "search", 0, command
, '/', 0 },
5905 { "searchb", 0, command
, '?', 0 },
5906 { "hinting", 0, command
, '.', 0 },
5907 { "hinting_newtab", 0, command
, ',', 0 },
5908 { "togglesrc", 0, toggle_src
, 0, 0 },
5910 /* yanking and pasting */
5911 { "yankuri", 0, yank_uri
, 0, 0 },
5912 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5913 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5914 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5917 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5918 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5921 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5922 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5925 { "hinting", 0, hint
, 0, 0 },
5926 { "hinting_newtab", 0, hint
, XT_HINT_NEWTAB
, 0 },
5928 /* custom stylesheet */
5929 { "userstyle", 0, userstyle
, 0, 0 },
5932 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5933 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5934 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5936 /* vertical movement */
5937 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5938 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5939 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5940 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5941 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5942 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5943 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5944 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5945 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5946 /* horizontal movement */
5947 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5948 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5949 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5950 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5952 { "favorites", 0, xtp_page_fl
, 0, 0 },
5953 { "fav", 0, xtp_page_fl
, 0, 0 },
5954 { "favadd", 0, add_favorite
, 0, 0 },
5956 { "qall", 0, quit
, 0, 0 },
5957 { "quitall", 0, quit
, 0, 0 },
5958 { "w", 0, save_tabs
, 0, 0 },
5959 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5960 { "help", 0, help
, 0, 0 },
5961 { "about", 0, about
, 0, 0 },
5962 { "stats", 0, stats
, 0, 0 },
5963 { "version", 0, about
, 0, 0 },
5966 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5967 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5968 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5969 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5970 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5971 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5972 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5973 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5974 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5975 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5976 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5978 /* cookie command */
5979 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5980 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5981 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5982 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5983 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5984 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5985 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5986 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5987 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5988 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5989 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5991 /* plugin command */
5992 { "plugin", 0, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5993 { "save", 1, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5994 { "domain", 2, pl_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5995 { "fqdn", 2, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5996 { "show", 1, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5997 { "all", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5998 { "persistent", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5999 { "session", 2, pl_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6000 { "toggle", 1, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6001 { "domain", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6002 { "fqdn", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6004 /* toplevel (domain) command */
6005 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6006 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6009 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
6012 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
6013 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
6014 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
6016 { "ca", 0, ca_cmd
, 0, 0 },
6017 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
6018 { "dl", 0, xtp_page_dl
, 0, 0 },
6019 { "h", 0, xtp_page_hl
, 0, 0 },
6020 { "history", 0, xtp_page_hl
, 0, 0 },
6021 { "home", 0, go_home
, 0, 0 },
6022 { "restart", 0, restart
, 0, 0 },
6023 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
6024 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
6025 { "statustoggle", 0, statustoggle
, 0, 0 },
6026 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
6028 { "print", 0, print_page
, 0, 0 },
6031 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
6032 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
6033 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
6034 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6035 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6036 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
6037 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
6038 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6039 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
6040 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
6041 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
6042 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6043 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
6044 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
6045 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
6046 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
6047 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
6048 { "tabs", 0, buffers
, 0, 0 },
6049 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
6050 { "buffers", 0, buffers
, 0, 0 },
6051 { "ls", 0, buffers
, 0, 0 },
6052 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
6054 /* command aliases (handy when -S flag is used) */
6055 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
6056 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
6057 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
6058 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
6061 { "set", 0, set
, 0, XT_SETARG
},
6063 { "fullscreen", 0, fullscreen
, 0, 0 },
6064 { "f", 0, fullscreen
, 0, 0 },
6067 { "session", 0, session_cmd
, XT_SHOW
, 0 },
6068 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
6069 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
6070 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
6071 { "show", 1, session_cmd
, XT_SHOW
, 0 },
6073 /* external javascript */
6074 { "script", 0, script_cmd
, XT_EJS_SHOW
, XT_USERARG
},
6077 { "inspector", 0, inspector_cmd
, XT_INS_SHOW
, 0 },
6078 { "show", 1, inspector_cmd
, XT_INS_SHOW
, 0 },
6079 { "hide", 1, inspector_cmd
, XT_INS_HIDE
, 0 },
6086 } cmd_status
= {-1, 0};
6089 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6092 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
6099 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6106 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6108 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
6114 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
6116 a
.i
= XT_NAV_FORWARD
;
6126 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6128 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6130 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6137 * cancel, remove, etc. downloads
6140 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6142 struct download find
, *d
= NULL
;
6144 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6146 /* some commands require a valid download id */
6147 if (cmd
!= XT_XTP_DL_LIST
) {
6148 /* lookup download in question */
6150 d
= RB_FIND(download_list
, &downloads
, &find
);
6153 show_oops(t
, "%s: no such download", __func__
);
6158 /* decide what to do */
6160 case XT_XTP_DL_CANCEL
:
6161 webkit_download_cancel(d
->download
);
6163 case XT_XTP_DL_REMOVE
:
6164 webkit_download_cancel(d
->download
); /* just incase */
6165 g_object_unref(d
->download
);
6166 RB_REMOVE(download_list
, &downloads
, d
);
6168 case XT_XTP_DL_LIST
:
6172 show_oops(t
, "%s: unknown command", __func__
);
6175 xtp_page_dl(t
, NULL
);
6179 * Actions on history, only does one thing for now, but
6180 * we provide the function for future actions
6183 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6185 struct history
*h
, *next
;
6189 case XT_XTP_HL_REMOVE
:
6190 /* walk backwards, as listed in reverse */
6191 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6192 next
= RB_PREV(history_list
, &hl
, h
);
6194 RB_REMOVE(history_list
, &hl
, h
);
6195 g_free((gpointer
) h
->title
);
6196 g_free((gpointer
) h
->uri
);
6203 case XT_XTP_HL_LIST
:
6204 /* Nothing - just xtp_page_hl() below */
6207 show_oops(t
, "%s: unknown command", __func__
);
6211 xtp_page_hl(t
, NULL
);
6214 /* remove a favorite */
6216 remove_favorite(struct tab
*t
, int index
)
6218 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6219 char *new_favs
, *tmp
;
6224 /* open favorites */
6225 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6227 if ((f
= fopen(file
, "r")) == NULL
) {
6228 show_oops(t
, "%s: can't open favorites: %s",
6229 __func__
, strerror(errno
));
6233 /* build a string which will become the new favroites file */
6234 new_favs
= g_strdup("");
6237 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6238 if (feof(f
) || ferror(f
))
6240 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6247 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6248 if (feof(f
) || ferror(f
)) {
6249 show_oops(t
, "%s: can't parse favorites %s",
6250 __func__
, strerror(errno
));
6255 /* as long as this isn't the one we are deleting add to file */
6258 new_favs
= g_strdup_printf("%s%s\n%s\n",
6259 new_favs
, title
, uri
);
6271 /* write back new favorites file */
6272 if ((f
= fopen(file
, "w")) == NULL
) {
6273 show_oops(t
, "%s: can't open favorites: %s",
6274 __func__
, strerror(errno
));
6278 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
6279 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
6292 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6295 case XT_XTP_FL_LIST
:
6296 /* nothing, just the below call to xtp_page_fl() */
6298 case XT_XTP_FL_REMOVE
:
6299 remove_favorite(t
, arg
);
6302 show_oops(t
, "%s: invalid favorites command", __func__
);
6306 xtp_page_fl(t
, NULL
);
6310 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6313 case XT_XTP_CL_LIST
:
6314 /* nothing, just xtp_page_cl() */
6316 case XT_XTP_CL_REMOVE
:
6320 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6324 xtp_page_cl(t
, NULL
);
6327 /* link an XTP class to it's session key and handler function */
6328 struct xtp_despatch
{
6331 void (*handle_func
)(struct tab
*, uint8_t, int);
6334 struct xtp_despatch xtp_despatches
[] = {
6335 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6336 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6337 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6338 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6339 { XT_XTP_INVALID
, NULL
, NULL
}
6343 * is the url xtp protocol? (xxxt://)
6344 * if so, parse and despatch correct bahvior
6347 parse_xtp_url(struct tab
*t
, const char *url
)
6349 char *dup
= NULL
, *p
, *last
= NULL
;
6350 uint8_t n_tokens
= 0;
6351 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6352 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6357 * tokens array meaning:
6359 * tokens[1] = session key
6360 * tokens[2] = action
6361 * tokens[3] = optional argument
6364 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6366 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6369 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6371 /* split out the url */
6372 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6373 (p
= strtok_r(NULL
, "/", &last
))) {
6375 tokens
[n_tokens
++] = p
;
6378 /* should be atleast three fields 'class/seskey/command/arg' */
6382 dsp
= xtp_despatches
;
6383 req_class
= atoi(tokens
[0]);
6384 while (dsp
->xtp_class
) {
6385 if (dsp
->xtp_class
== req_class
) {
6392 /* did we find one atall? */
6393 if (dsp_match
== NULL
) {
6394 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6398 /* check session key and call despatch function */
6399 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6400 ret
= TRUE
; /* all is well, this was a valid xtp request */
6401 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6414 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6416 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6418 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6421 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6426 show_oops(t
, "activate_uri_entry_cb no uri");
6430 uri
+= strspn(uri
, "\t ");
6432 /* if xxxt:// treat specially */
6433 if (parse_xtp_url(t
, uri
))
6436 /* otherwise continue to load page normally */
6437 load_uri(t
, (gchar
*)uri
);
6442 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6444 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6445 char *newuri
= NULL
;
6448 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6451 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6455 if (search_string
== NULL
) {
6456 show_oops(t
, "no search_string");
6460 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6462 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6463 newuri
= g_strdup_printf(search_string
, enc_search
);
6467 webkit_web_view_load_uri(t
->wv
, newuri
);
6475 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6477 struct domain
*d
= NULL
;
6480 if (uri
== NULL
|| t
== NULL
)
6483 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6488 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6489 es
? "enable" : "disable", uri
);
6491 g_object_set(G_OBJECT(t
->settings
),
6492 "enable-html5-local-storage", es
, (char *)NULL
);
6493 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6497 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6499 struct domain
*d
= NULL
;
6502 if (uri
== NULL
|| t
== NULL
)
6505 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6510 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6511 es
? "enable" : "disable", uri
);
6513 g_object_set(G_OBJECT(t
->settings
),
6514 "enable-scripts", es
, (char *)NULL
);
6515 g_object_set(G_OBJECT(t
->settings
),
6516 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6517 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6519 button_set_stockid(t
->js_toggle
,
6520 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6524 check_and_set_pl(const gchar
*uri
, struct tab
*t
)
6526 struct domain
*d
= NULL
;
6529 if (uri
== NULL
|| t
== NULL
)
6532 if ((d
= wl_find_uri(uri
, &pl_wl
)) == NULL
)
6537 DNPRINTF(XT_D_JS
, "check_and_set_pl: %s %s\n",
6538 es
? "enable" : "disable", uri
);
6540 g_object_set(G_OBJECT(t
->settings
),
6541 "enable-plugins", es
, (char *)NULL
);
6542 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6546 color_address_bar(gpointer p
)
6549 struct tab
*tt
, *t
= p
;
6550 gchar
*col_str
= XT_COLOR_WHITE
;
6551 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
6554 gdk_threads_enter();
6556 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6558 /* make sure t still exists */
6561 TAILQ_FOREACH(tt
, &tabs
, entry
)
6567 if ((uri
= get_uri(t
)) == NULL
)
6572 gdk_threads_leave();
6575 col_str
= XT_COLOR_YELLOW
;
6576 switch (load_compare_cert(u
, &error_str
)) {
6578 col_str
= XT_COLOR_BLUE
;
6581 col_str
= XT_COLOR_GREEN
;
6583 case CERT_UNTRUSTED
:
6584 col_str
= XT_COLOR_YELLOW
;
6587 col_str
= XT_COLOR_RED
;
6592 gdk_threads_enter();
6594 /* make sure t isn't deleted */
6595 TAILQ_FOREACH(tt
, &tabs
, entry
)
6602 /* test to see if the user navigated away and canceled the thread */
6603 if (t
->thread
!= g_thread_self())
6605 if ((uri
= get_uri(t
)) == NULL
) {
6609 if (strcmp(uri
, u
)) {
6610 /* make sure we are still the same url */
6616 gdk_color_parse(col_str
, &color
);
6617 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6619 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6620 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6622 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6624 if (error_str
&& error_str
[0] != '\0')
6625 show_oops(t
, "%s", error_str
);
6630 /* t is invalid at this point */
6632 g_free((gpointer
)u
);
6634 gdk_threads_leave();
6639 show_ca_status(struct tab
*t
, const char *uri
)
6642 gchar
*col_str
= XT_COLOR_WHITE
;
6644 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6645 ssl_strict_certs
, ssl_ca_file
, uri
);
6652 if (ssl_ca_file
== NULL
) {
6653 if (g_str_has_prefix(uri
, "http://"))
6655 if (g_str_has_prefix(uri
, "https://")) {
6656 col_str
= XT_COLOR_RED
;
6661 if (g_str_has_prefix(uri
, "http://") ||
6662 !g_str_has_prefix(uri
, "https://"))
6666 * It is not necessary to see if the thread is already running.
6667 * If the thread is in progress setting it to something else aborts it
6671 /* thread the coloring of the address bar */
6672 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
6674 color_address_bar(t
);
6680 gdk_color_parse(col_str
, &color
);
6681 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6683 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6684 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6686 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6691 free_favicon(struct tab
*t
)
6693 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6694 __func__
, t
->icon_download
, t
->icon_request
);
6696 if (t
->icon_request
)
6697 g_object_unref(t
->icon_request
);
6698 if (t
->icon_dest_uri
)
6699 g_free(t
->icon_dest_uri
);
6701 t
->icon_request
= NULL
;
6702 t
->icon_dest_uri
= NULL
;
6706 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6708 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6709 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6711 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6712 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6714 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6715 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6719 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6721 GdkPixbuf
*pb_scaled
;
6723 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6724 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6725 GDK_INTERP_BILINEAR
);
6729 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6730 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6732 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6733 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6735 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6736 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6738 if (pb_scaled
!= pb
)
6739 g_object_unref(pb_scaled
);
6743 xt_icon_from_file(struct tab
*t
, char *file
)
6747 if (g_str_has_prefix(file
, "file://"))
6748 file
+= strlen("file://");
6750 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6752 xt_icon_from_pixbuf(t
, pb
);
6755 xt_icon_from_name(t
, "text-html");
6759 is_valid_icon(char *file
)
6762 const char *mime_type
;
6766 gf
= g_file_new_for_path(file
);
6767 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6769 mime_type
= g_file_info_get_content_type(fi
);
6770 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6771 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6772 g_strcmp0(mime_type
, "image/png") == 0 ||
6773 g_strcmp0(mime_type
, "image/gif") == 0 ||
6774 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6782 set_favicon_from_file(struct tab
*t
, char *file
)
6786 if (t
== NULL
|| file
== NULL
)
6789 if (g_str_has_prefix(file
, "file://"))
6790 file
+= strlen("file://");
6791 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6793 if (!stat(file
, &sb
)) {
6794 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6795 /* corrupt icon so trash it */
6796 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6799 /* no need to set icon to default here */
6803 xt_icon_from_file(t
, file
);
6807 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6810 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6811 struct tab
*tt
= NULL
, *t
= NULL
;
6814 * find the webview instead of passing in the tab as it could have been
6815 * deleted from underneath us.
6817 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6826 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6827 __func__
, t
->tab_id
, status
);
6830 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6832 t
->icon_download
= NULL
;
6835 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6838 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6841 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6843 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6844 __func__
, t
->tab_id
);
6845 t
->icon_download
= NULL
;
6848 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6851 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6852 __func__
, t
->icon_dest_uri
);
6853 set_favicon_from_file(t
, t
->icon_dest_uri
);
6854 /* these will be freed post callback */
6855 t
->icon_request
= NULL
;
6856 t
->icon_download
= NULL
;
6864 abort_favicon_download(struct tab
*t
)
6866 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6868 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6869 if (t
->icon_download
) {
6870 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6871 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6872 webkit_download_cancel(t
->icon_download
);
6873 t
->icon_download
= NULL
;
6878 xt_icon_from_name(t
, "text-html");
6882 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6884 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6886 if (uri
== NULL
|| t
== NULL
)
6889 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6890 /* take icon from WebKitIconDatabase */
6893 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6895 xt_icon_from_pixbuf(t
, pb
);
6898 xt_icon_from_name(t
, "text-html");
6899 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6900 /* download icon to cache dir */
6901 gchar
*name_hash
, file
[PATH_MAX
];
6904 if (t
->icon_request
) {
6905 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6909 /* check to see if we got the icon in cache */
6910 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6911 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6914 if (!stat(file
, &sb
)) {
6915 if (sb
.st_size
> 0) {
6916 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6918 set_favicon_from_file(t
, file
);
6922 /* corrupt icon so trash it */
6923 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6928 /* create download for icon */
6929 t
->icon_request
= webkit_network_request_new(uri
);
6930 if (t
->icon_request
== NULL
) {
6931 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6936 t
->icon_download
= webkit_download_new(t
->icon_request
);
6937 if (t
->icon_download
== NULL
)
6940 /* we have to free icon_dest_uri later */
6941 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6942 webkit_download_set_destination_uri(t
->icon_download
,
6945 if (webkit_download_get_status(t
->icon_download
) ==
6946 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6947 g_object_unref(t
->icon_request
);
6948 g_free(t
->icon_dest_uri
);
6949 t
->icon_request
= NULL
;
6950 t
->icon_dest_uri
= NULL
;
6954 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6955 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6957 webkit_download_start(t
->icon_download
);
6962 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6964 const gchar
*uri
= NULL
, *title
= NULL
;
6965 struct history
*h
, find
;
6969 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6970 webkit_web_view_get_load_status(wview
),
6971 get_uri(t
) ? get_uri(t
) : "NOTHING");
6974 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6978 switch (webkit_web_view_get_load_status(wview
)) {
6979 case WEBKIT_LOAD_PROVISIONAL
:
6981 abort_favicon_download(t
);
6982 #if GTK_CHECK_VERSION(2, 20, 0)
6983 gtk_widget_show(t
->spinner
);
6984 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6986 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6988 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6990 /* assume we are a new address */
6991 gdk_color_parse("white", &color
);
6992 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6993 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6995 /* take focus if we are visible */
7001 /* kill color thread */
7006 case WEBKIT_LOAD_COMMITTED
:
7011 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
7017 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
7019 /* check if js white listing is enabled */
7020 if (enable_plugin_whitelist
)
7021 check_and_set_pl(uri
, t
);
7022 if (enable_cookie_whitelist
)
7023 check_and_set_cookie(uri
, t
);
7024 if (enable_js_whitelist
)
7025 check_and_set_js(uri
, t
);
7031 /* we know enough to autosave the session */
7032 if (session_autosave
) {
7037 show_ca_status(t
, uri
);
7040 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
7044 case WEBKIT_LOAD_FINISHED
:
7050 if (!strncmp(uri
, "http://", strlen("http://")) ||
7051 !strncmp(uri
, "https://", strlen("https://")) ||
7052 !strncmp(uri
, "file://", strlen("file://"))) {
7054 h
= RB_FIND(history_list
, &hl
, &find
);
7056 title
= get_title(t
, FALSE
);
7057 h
= g_malloc(sizeof *h
);
7058 h
->uri
= g_strdup(uri
);
7059 h
->title
= g_strdup(title
);
7060 RB_INSERT(history_list
, &hl
, h
);
7061 completion_add_uri(h
->uri
);
7062 update_history_tabs(NULL
);
7066 set_status(t
, (char *)uri
, XT_STATUS_URI
);
7067 #if WEBKIT_CHECK_VERSION(1, 1, 18)
7068 case WEBKIT_LOAD_FAILED
:
7071 #if GTK_CHECK_VERSION(2, 20, 0)
7072 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7073 gtk_widget_hide(t
->spinner
);
7076 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
7081 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
7083 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
7084 can_go_back_for_real(t
));
7086 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
7087 can_go_forward_for_real(t
));
7091 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
7093 const gchar
*title
= NULL
, *win_title
= NULL
;
7095 title
= get_title(t
, FALSE
);
7096 win_title
= get_title(t
, TRUE
);
7097 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
7098 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
7099 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
7100 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
7104 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7106 run_script(t
, JS_HINTING
);
7107 if (autofocus_onload
&&
7108 t
->tab_id
== gtk_notebook_get_current_page(notebook
))
7109 run_script(t
, "hints.focusInput();");
7111 run_script(t
, "hints.clearFocus();");
7115 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
7117 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
7118 progress
== 100 ? 0 : (double)progress
/ 100);
7119 if (show_url
== 0) {
7120 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
7121 progress
== 100 ? 0 : (double)progress
/ 100);
7124 update_statusbar_position(NULL
, NULL
);
7128 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
7129 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
7130 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
7133 WebKitWebNavigationReason reason
;
7134 struct domain
*d
= NULL
;
7137 show_oops(NULL
, "webview_npd_cb invalid parameters");
7141 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
7143 webkit_network_request_get_uri(request
));
7145 uri
= (char *)webkit_network_request_get_uri(request
);
7147 /* if this is an xtp url, we don't load anything else */
7148 if (parse_xtp_url(t
, uri
))
7151 if ((t
->hints_on
&& t
->new_tab
) || t
->ctrl_click
) {
7153 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
7154 webkit_web_policy_decision_ignore(pd
);
7155 return (TRUE
); /* we made the decission */
7159 * This is a little hairy but it comes down to this:
7160 * when we run in whitelist mode we have to assist the browser in
7161 * opening the URL that it would have opened in a new tab.
7163 reason
= webkit_web_navigation_action_get_reason(na
);
7164 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
7165 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7166 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
7167 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7169 webkit_web_policy_decision_use(pd
);
7170 return (TRUE
); /* we made the decision */
7177 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7180 struct domain
*d
= NULL
;
7182 WebKitWebView
*webview
= NULL
;
7184 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
7185 webkit_web_view_get_uri(wv
));
7188 /* open in current tab */
7190 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7191 uri
= webkit_web_view_get_uri(wv
);
7192 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7195 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7197 } else if (enable_scripts
== 1) {
7198 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7206 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
7209 struct domain
*d
= NULL
;
7211 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7213 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7214 uri
= webkit_web_view_get_uri(wv
);
7215 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7219 } else if (enable_scripts
== 1)
7226 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7228 /* we can not eat the event without throwing gtk off so defer it */
7230 /* catch middle click */
7231 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7236 /* catch ctrl click */
7237 if (e
->type
== GDK_BUTTON_RELEASE
&&
7238 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7243 return (XT_CB_PASSTHROUGH
);
7247 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7249 struct mime_type
*m
;
7251 m
= find_mime_type(mime_type
);
7259 show_oops(t
, "can't fork mime handler");
7269 execlp(m
->mt_action
, m
->mt_action
,
7270 webkit_network_request_get_uri(request
), (void *)NULL
);
7279 get_mime_type(const char *file
)
7282 char *mime_type
= NULL
;
7286 if (g_str_has_prefix(file
, "file://"))
7287 file
+= strlen("file://");
7289 gf
= g_file_new_for_path(file
);
7290 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7292 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
7293 mime_type
= g_strdup(m
);
7301 run_download_mimehandler(char *mime_type
, char *file
)
7303 struct mime_type
*m
;
7305 m
= find_mime_type(mime_type
);
7311 show_oops(NULL
, "can't fork download mime handler");
7321 if (g_str_has_prefix(file
, "file://"))
7322 file
+= strlen("file://");
7323 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7332 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7335 WebKitDownloadStatus status
;
7336 const char *file
= NULL
;
7339 if (download
== NULL
)
7341 status
= webkit_download_get_status(download
);
7342 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7345 file
= webkit_download_get_destination_uri(download
);
7348 mime
= get_mime_type(file
);
7352 run_download_mimehandler((char *)mime
, (char *)file
);
7357 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7358 WebKitNetworkRequest
*request
, char *mime_type
,
7359 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7362 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7366 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7367 t
->tab_id
, mime_type
);
7369 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7370 webkit_web_policy_decision_ignore(decision
);
7375 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7376 webkit_web_policy_decision_download(decision
);
7384 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7388 const gchar
*suggested_name
;
7389 gchar
*filename
= NULL
;
7391 struct download
*download_entry
;
7394 if (wk_download
== NULL
|| t
== NULL
) {
7395 show_oops(NULL
, "%s invalid parameters", __func__
);
7399 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7400 if (suggested_name
== NULL
)
7401 return (FALSE
); /* abort download */
7412 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7414 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7415 filename
: suggested_name
);
7417 } while (!stat(uri
+ strlen("file://"), &sb
));
7419 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7420 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7422 webkit_download_set_destination_uri(wk_download
, uri
);
7424 if (webkit_download_get_status(wk_download
) ==
7425 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7426 show_oops(t
, "%s: download failed to start", __func__
);
7428 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7430 /* connect "download first" mime handler */
7431 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7432 G_CALLBACK(download_status_changed_cb
), NULL
);
7434 download_entry
= g_malloc(sizeof(struct download
));
7435 download_entry
->download
= wk_download
;
7436 download_entry
->tab
= t
;
7437 download_entry
->id
= next_download_id
++;
7438 RB_INSERT(download_list
, &downloads
, download_entry
);
7439 /* get from history */
7440 g_object_ref(wk_download
);
7441 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7442 show_oops(t
, "Download of '%s' started...",
7443 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7452 /* sync other download manager tabs */
7453 update_download_tabs(NULL
);
7456 * NOTE: never redirect/render the current tab before this
7457 * function returns. This will cause the download to never start.
7459 return (ret
); /* start download */
7463 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7465 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7468 show_oops(NULL
, "webview_hover_cb");
7473 set_status(t
, uri
, XT_STATUS_LINK
);
7476 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7481 mark(struct tab
*t
, struct karg
*arg
)
7487 if ((index
= marktoindex(mark
)) == -1)
7490 if (arg
->i
== XT_MARK_SET
)
7491 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7492 else if (arg
->i
== XT_MARK_GOTO
) {
7493 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7494 show_oops(t
, "mark '%c' does not exist", mark
);
7497 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7498 something changes the document size */
7499 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7506 marks_clear(struct tab
*t
)
7510 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7511 t
->mark
[i
] = XT_INVALID_MARK
;
7517 char file
[PATH_MAX
];
7518 char *line
= NULL
, *p
;
7523 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7524 if ((f
= fopen(file
, "r+")) == NULL
) {
7525 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7529 for (i
= 1; ; i
++) {
7530 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7532 if (strlen(line
) == 0 || line
[0] == '#') {
7538 p
= strtok(line
, " \t");
7540 if (p
== NULL
|| strlen(p
) != 1 ||
7541 (index
= marktoindex(*p
)) == -1) {
7542 warnx("corrupt quickmarks file, line %d", i
);
7546 p
= strtok(NULL
, " \t");
7547 if (qmarks
[index
] != NULL
)
7548 g_free(qmarks
[index
]);
7549 qmarks
[index
] = g_strdup(p
);
7560 char file
[PATH_MAX
];
7564 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7565 if ((f
= fopen(file
, "r+")) == NULL
) {
7566 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7570 for (i
= 0; i
< XT_NOMARKS
; i
++)
7571 if (qmarks
[i
] != NULL
)
7572 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7580 qmark(struct tab
*t
, struct karg
*arg
)
7585 mark
= arg
->s
[strlen(arg
->s
)-1];
7586 index
= marktoindex(mark
);
7592 if (qmarks
[index
] != NULL
)
7593 g_free(qmarks
[index
]);
7595 qmarks_load(); /* sync if multiple instances */
7596 qmarks
[index
] = g_strdup(get_uri(t
));
7600 if (qmarks
[index
] != NULL
)
7601 load_uri(t
, qmarks
[index
]);
7603 show_oops(t
, "quickmark \"%c\" does not exist",
7609 if (qmarks
[index
] != NULL
)
7610 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7612 show_oops(t
, "quickmark \"%c\" does not exist",
7623 go_up(struct tab
*t
, struct karg
*args
)
7629 levels
= atoi(args
->s
);
7633 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7634 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7636 tmp
+= strlen(XT_PROTO_DELIM
);
7638 /* if an uri starts with a slash, leave it alone (for file:///) */
7645 p
= strrchr(tmp
, '/');
7659 gototab(struct tab
*t
, struct karg
*args
)
7662 struct karg arg
= {0, NULL
, -1};
7664 tab
= atoi(args
->s
);
7666 arg
.i
= XT_TAB_NEXT
;
7675 zoom_amount(struct tab
*t
, struct karg
*arg
)
7677 struct karg narg
= {0, NULL
, -1};
7679 narg
.i
= atoi(arg
->s
);
7680 resizetab(t
, &narg
);
7686 flip_colon(struct tab
*t
, struct karg
*arg
)
7688 struct karg narg
= {0, NULL
, -1};
7691 if (t
== NULL
|| arg
== NULL
)
7694 p
= strstr(arg
->s
, ":");
7706 /* buffer commands receive the regex that triggered them in arg.s */
7707 char bcmd
[XT_BUFCMD_SZ
];
7711 #define XT_PRE_NO (0)
7712 #define XT_PRE_YES (1)
7713 #define XT_PRE_MAYBE (2)
7715 int (*func
)(struct tab
*, struct karg
*);
7719 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7720 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7721 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7722 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7723 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7724 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7725 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7726 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7727 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7728 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7729 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7730 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7731 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7732 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7733 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7734 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7735 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7736 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7740 buffercmd_init(void)
7744 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7745 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7746 REG_EXTENDED
| REG_NOSUB
))
7747 startpage_add("invalid buffercmd regex %s",
7748 buffercmds
[i
].regex
);
7752 buffercmd_abort(struct tab
*t
)
7756 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7757 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7760 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7761 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7765 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7767 struct karg arg
= {0, NULL
, -1};
7770 arg
.s
= g_strdup(bcmd
);
7772 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7773 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7783 buffercmd_addkey(struct tab
*t
, guint keyval
)
7786 char s
[XT_BUFCMD_SZ
];
7788 if (keyval
== GDK_Escape
) {
7790 return (XT_CB_HANDLED
);
7793 /* key with modifier or non-ascii character */
7794 if (!isascii(keyval
))
7795 return (XT_CB_PASSTHROUGH
);
7797 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7798 "to buffer \"%s\"\n", keyval
, bcmd
);
7800 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7801 if (bcmd
[i
] == '\0') {
7806 /* buffer full, ignore input */
7807 if (i
>= LENGTH(bcmd
) -1) {
7808 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7810 return (XT_CB_HANDLED
);
7813 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7815 /* find exact match */
7816 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7817 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7818 (size_t) 0, NULL
, 0) == 0) {
7819 buffercmd_execute(t
, &buffercmds
[i
]);
7823 /* find non exact matches to see if we need to abort ot not */
7824 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7825 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7828 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7829 if (isdigit(bcmd
[0])) {
7830 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7834 if (sscanf(bcmd
, "%s", s
) == 0)
7837 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7838 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7841 if (sscanf(bcmd
, "%s", s
) == 0)
7844 if (c
== -1 && buffercmds
[i
].precount
)
7846 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7849 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7850 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7853 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7858 return (XT_CB_HANDLED
);
7862 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7864 struct key_binding
*k
;
7866 /* handle keybindings if buffercmd is empty.
7867 if not empty, allow commands like C-n */
7868 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7869 TAILQ_FOREACH(k
, &kbl
, entry
)
7870 if (e
->keyval
== k
->key
7871 && (entry
? k
->use_in_entry
: 1)) {
7873 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7874 return (cmd_execute(t
, k
->cmd
));
7875 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7876 return (cmd_execute(t
, k
->cmd
));
7880 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7881 return buffercmd_addkey(t
, e
->keyval
);
7883 return (XT_CB_PASSTHROUGH
);
7887 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7891 /* don't use w directly; use t->whatever instead */
7894 show_oops(NULL
, "wv_keypress_after_cb");
7895 return (XT_CB_PASSTHROUGH
);
7898 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7899 e
->keyval
, e
->state
, t
);
7902 /* XXX make sure cmd entry is enabled */
7903 return (XT_CB_HANDLED
);
7906 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7907 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7908 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7911 return (handle_keypress(t
, e
, 0));
7915 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7919 /* Hide buffers, if they are visible, with escape. */
7920 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7921 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7922 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7924 return (XT_CB_HANDLED
);
7927 return (XT_CB_PASSTHROUGH
);
7931 hint_continue(struct tab
*t
)
7933 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7935 const gchar
*errstr
= NULL
;
7939 if (!(c
[0] == '.' || c
[0] == ','))
7941 if (strlen(c
) == 1) {
7942 /* XXX should not happen */
7947 if (isdigit(c
[1])) {
7949 i
= strtonum(&c
[1], 1, 4096, &errstr
);
7951 show_oops(t
, "invalid numerical hint %s", &c
[1]);
7954 s
= g_strdup_printf("hints.updateHints(%lld);", i
);
7958 /* alphanumeric input */
7959 s
= g_strdup_printf("hints.createHints('%s', '%c');",
7960 &c
[1], c
[0] == '.' ? 'f' : 'F');
7971 search_continue(struct tab
*t
)
7973 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7974 gboolean rv
= FALSE
;
7976 if (c
[0] == ':' || c
[0] == '.' || c
[0] == ',')
7978 if (strlen(c
) == 1) {
7979 webkit_web_view_unmark_text_matches(t
->wv
);
7984 t
->search_forward
= TRUE
;
7985 else if (c
[0] == '?')
7986 t
->search_forward
= FALSE
;
7996 search_cb(struct tab
*t
)
7998 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
8001 if (search_continue(t
) == FALSE
)
8005 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
8007 /* not found, mark red */
8008 gdk_color_parse(XT_COLOR_RED
, &color
);
8009 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8010 /* unmark and remove selection */
8011 webkit_web_view_unmark_text_matches(t
->wv
);
8012 /* my kingdom for a way to unselect text in webview */
8014 /* found, highlight all */
8015 webkit_web_view_unmark_text_matches(t
->wv
);
8016 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
8017 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
8018 gdk_color_parse(XT_COLOR_WHITE
, &color
);
8019 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8027 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8029 const gchar
*c
= gtk_entry_get_text(w
);
8032 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
8033 return (XT_CB_PASSTHROUGH
);
8036 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
8037 e
->keyval
, e
->state
, t
);
8040 if (!(e
->keyval
== GDK_Tab
|| e
->keyval
== GDK_ISO_Left_Tab
)) {
8041 if (hint_continue(t
) == FALSE
)
8046 if (search_continue(t
) == FALSE
)
8049 /* if search length is > 4 then no longer play timeout games */
8050 if (strlen(c
) > 4) {
8052 g_source_remove(t
->search_id
);
8059 /* reestablish a new timer if the user types fast */
8061 g_source_remove(t
->search_id
);
8062 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
8065 return (XT_CB_PASSTHROUGH
);
8069 match_uri(const gchar
*uri
, const gchar
*key
) {
8072 gboolean match
= FALSE
;
8076 if (!strncmp(key
, uri
, len
))
8079 voffset
= strstr(uri
, "/") + 2;
8080 if (!strncmp(key
, voffset
, len
))
8082 else if (g_str_has_prefix(voffset
, "www.")) {
8083 voffset
= voffset
+ strlen("www.");
8084 if (!strncmp(key
, voffset
, len
))
8093 match_session(const gchar
*name
, const gchar
*key
) {
8096 sub
= strcasestr(name
, key
);
8102 cmd_getlist(int id
, char *key
)
8109 if (cmds
[id
].type
& XT_URLARG
) {
8110 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
8111 if (match_uri(h
->uri
, key
)) {
8112 cmd_status
.list
[c
] = (char *)h
->uri
;
8118 } else if (cmds
[id
].type
& XT_SESSARG
) {
8119 TAILQ_FOREACH(s
, &sessions
, entry
)
8120 if (match_session(s
->name
, key
)) {
8121 cmd_status
.list
[c
] = (char *)s
->name
;
8127 } else if (cmds
[id
].type
& XT_SETARG
) {
8128 for (i
= 0; i
< LENGTH(rs
); i
++)
8129 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
8130 cmd_status
.list
[c
++] = rs
[i
].name
;
8136 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
8138 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
8139 if (cmds
[i
].level
< dep
)
8141 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
8142 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
8143 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
8151 cmd_getnext(int dir
)
8153 cmd_status
.index
+= dir
;
8155 if (cmd_status
.index
< 0)
8156 cmd_status
.index
= cmd_status
.len
- 1;
8157 else if (cmd_status
.index
>= cmd_status
.len
)
8158 cmd_status
.index
= 0;
8160 return cmd_status
.list
[cmd_status
.index
];
8164 cmd_tokenize(char *s
, char *tokens
[])
8167 char *tok
, *last
= NULL
;
8168 size_t len
= strlen(s
);
8171 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8172 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8173 tok
= strtok_r(NULL
, " ", &last
), i
++)
8183 cmd_complete(struct tab
*t
, char *str
, int dir
)
8185 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8186 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8188 char *tok
, *match
, *s
= g_strdup(str
);
8190 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8193 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8196 for (i
= 0; isdigit(s
[i
]); i
++)
8199 for (; isspace(s
[i
]); i
++)
8204 levels
= cmd_tokenize(s
, tokens
);
8206 for (i
= 0; i
< levels
- 1; i
++) {
8209 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8210 if (cmds
[j
].level
< dep
)
8212 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8216 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8223 if (matchcount
== 1) {
8224 strlcat(res
, tok
, sizeof res
);
8225 strlcat(res
, " ", sizeof res
);
8235 if (cmd_status
.index
== -1)
8236 cmd_getlist(parent
, tokens
[i
]);
8238 if (cmd_status
.len
> 0) {
8239 match
= cmd_getnext(dir
);
8240 strlcat(res
, match
, sizeof res
);
8241 gtk_entry_set_text(w
, res
);
8242 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8249 cmd_execute(struct tab
*t
, char *str
)
8251 struct cmd
*cmd
= NULL
;
8252 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
8254 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8255 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8256 struct karg arg
= {0, NULL
, -1};
8261 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8267 while (isspace(s
[0]))
8270 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8271 prefix
= atoi(prefixstr
);
8275 for (tok
= strtok_r(s
, " ", &last
); tok
;
8276 tok
= strtok_r(NULL
, " ", &last
)) {
8278 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8279 if (cmds
[j
].level
< dep
)
8281 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8283 if (cmds
[j
].level
== dep
&&
8284 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8288 if (len
== strlen(cmds
[j
].cmd
)) {
8294 if (matchcount
== 1) {
8299 show_oops(t
, "Invalid command: %s", str
);
8305 show_oops(t
, "Empty command");
8311 arg
.precount
= prefix
;
8312 else if (cmd_prefix
> 0)
8313 arg
.precount
= cmd_prefix
;
8315 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8316 show_oops(t
, "No prefix allowed: %s", str
);
8320 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8321 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8322 if (arg
.s
== NULL
) {
8323 show_oops(t
, "Invalid command");
8326 arg
.precount
= atoi(arg
.s
);
8327 if (arg
.precount
<= 0) {
8328 if (arg
.s
[0] == '0')
8329 show_oops(t
, "Zero count");
8331 show_oops(t
, "Trailing characters");
8336 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8337 __func__
, arg
.precount
, arg
.s
);
8353 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8356 show_oops(NULL
, "entry_key_cb invalid parameters");
8357 return (XT_CB_PASSTHROUGH
);
8360 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8361 e
->keyval
, e
->state
, t
);
8365 if (e
->keyval
== GDK_Escape
) {
8366 /* don't use focus_webview(t) because we want to type :cmds */
8367 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8370 return (handle_keypress(t
, e
, 1));
8373 struct command_entry
*
8374 history_prev(struct command_list
*l
, struct command_entry
*at
)
8377 at
= TAILQ_LAST(l
, command_list
);
8379 at
= TAILQ_PREV(at
, command_list
, entry
);
8381 at
= TAILQ_LAST(l
, command_list
);
8387 struct command_entry
*
8388 history_next(struct command_list
*l
, struct command_entry
*at
)
8391 at
= TAILQ_FIRST(l
);
8393 at
= TAILQ_NEXT(at
, entry
);
8395 at
= TAILQ_FIRST(l
);
8402 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8404 int rv
= XT_CB_HANDLED
;
8405 const gchar
*c
= gtk_entry_get_text(w
);
8409 show_oops(NULL
, "cmd_keypress_cb parameters");
8410 return (XT_CB_PASSTHROUGH
);
8413 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8414 e
->keyval
, e
->state
, t
);
8418 e
->keyval
= GDK_Escape
;
8419 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?' ||
8420 c
[0] == '.' || c
[0] == ','))
8421 e
->keyval
= GDK_Escape
;
8423 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8424 e
->keyval
!= GDK_ISO_Left_Tab
)
8425 cmd_status
.index
= -1;
8427 switch (e
->keyval
) {
8430 cmd_complete(t
, (char *)&c
[1], 1);
8431 else if (c
[0] == '.' || c
[0] == ',')
8432 run_script(t
, "hints.focusNextHint();");
8434 case GDK_ISO_Left_Tab
:
8436 cmd_complete(t
, (char *)&c
[1], -1);
8437 else if (c
[0] == '.' || c
[0] == ',')
8438 run_script(t
, "hints.focusPreviousHint();");
8442 if ((search_at
= history_next(&shl
, search_at
))) {
8443 search_at
->line
[0] = c
[0];
8444 gtk_entry_set_text(w
, search_at
->line
);
8445 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8447 } else if (c
[0] == '/') {
8448 if ((search_at
= history_prev(&chl
, search_at
))) {
8449 search_at
->line
[0] = c
[0];
8450 gtk_entry_set_text(w
, search_at
->line
);
8451 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8453 } if (c
[0] == ':') {
8454 if ((history_at
= history_prev(&chl
, history_at
))) {
8455 history_at
->line
[0] = c
[0];
8456 gtk_entry_set_text(w
, history_at
->line
);
8457 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8463 if ((search_at
= history_next(&shl
, search_at
))) {
8464 search_at
->line
[0] = c
[0];
8465 gtk_entry_set_text(w
, search_at
->line
);
8466 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8468 } else if (c
[0] == '?') {
8469 if ((search_at
= history_prev(&chl
, search_at
))) {
8470 search_at
->line
[0] = c
[0];
8471 gtk_entry_set_text(w
, search_at
->line
);
8472 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8474 } if (c
[0] == ':') {
8475 if ((history_at
= history_next(&chl
, history_at
))) {
8476 history_at
->line
[0] = c
[0];
8477 gtk_entry_set_text(w
, history_at
->line
);
8478 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8483 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?") ||
8484 !strcmp(c
, ".") || !strcmp(c
, ","))) {
8485 /* see if we are doing hinting and reset it */
8486 if (c
[0] == '.' || c
[0] == ',') {
8487 /* recreate hints */
8488 s
= g_strdup_printf("hints.createHints('', "
8489 "'%c');", c
[0] == '.' ? 'f' : 'F');
8502 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8503 webkit_web_view_unmark_text_matches(t
->wv
);
8505 /* no need to cancel hints */
8509 rv
= XT_CB_PASSTHROUGH
;
8515 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8517 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8521 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8523 /* popup menu enabled */
8528 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8531 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8532 return (XT_CB_PASSTHROUGH
);
8535 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8536 t
->tab_id
, t
->popup
);
8538 /* if popup is enabled don't lose focus */
8541 return (XT_CB_PASSTHROUGH
);
8548 if (show_url
== 0 || t
->focus_wv
)
8551 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8553 return (XT_CB_PASSTHROUGH
);
8557 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8560 const gchar
*c
= gtk_entry_get_text(entry
);
8563 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8567 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8573 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?' ||
8574 c
[0] == '.' || c
[0] == ','))
8580 if (c
[0] == '/' || c
[0] == '?') {
8581 /* see if there is a timer pending */
8583 g_source_remove(t
->search_id
);
8588 if (t
->search_text
) {
8589 g_free(t
->search_text
);
8590 t
->search_text
= NULL
;
8593 t
->search_text
= g_strdup(s
);
8595 g_free(global_search
);
8596 global_search
= g_strdup(s
);
8597 t
->search_forward
= c
[0] == '/';
8599 history_add(&shl
, search_file
, s
, &search_history_count
);
8601 } else if (c
[0] == '.' || c
[0] == ',') {
8602 run_script(t
, "hints.fire();");
8603 /* XXX history for link following? */
8607 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8614 backward_cb(GtkWidget
*w
, struct tab
*t
)
8619 show_oops(NULL
, "backward_cb invalid parameters");
8623 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8630 forward_cb(GtkWidget
*w
, struct tab
*t
)
8635 show_oops(NULL
, "forward_cb invalid parameters");
8639 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8641 a
.i
= XT_NAV_FORWARD
;
8646 home_cb(GtkWidget
*w
, struct tab
*t
)
8649 show_oops(NULL
, "home_cb invalid parameters");
8653 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8659 stop_cb(GtkWidget
*w
, struct tab
*t
)
8661 WebKitWebFrame
*frame
;
8664 show_oops(NULL
, "stop_cb invalid parameters");
8668 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8670 frame
= webkit_web_view_get_main_frame(t
->wv
);
8671 if (frame
== NULL
) {
8672 show_oops(t
, "stop_cb: no frame");
8676 webkit_web_frame_stop_loading(frame
);
8677 abort_favicon_download(t
);
8681 setup_webkit(struct tab
*t
)
8683 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8684 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8685 FALSE
, (char *)NULL
);
8687 warnx("webkit does not have \"enable-dns-prefetching\" property");
8688 g_object_set(G_OBJECT(t
->settings
),
8689 "user-agent", t
->user_agent
, (char *)NULL
);
8690 g_object_set(G_OBJECT(t
->settings
),
8691 "enable-scripts", enable_scripts
, (char *)NULL
);
8692 g_object_set(G_OBJECT(t
->settings
),
8693 "enable-plugins", enable_plugins
, (char *)NULL
);
8694 g_object_set(G_OBJECT(t
->settings
),
8695 "javascript-can-open-windows-automatically", enable_scripts
,
8697 g_object_set(G_OBJECT(t
->settings
),
8698 "enable-html5-database", FALSE
, (char *)NULL
);
8699 g_object_set(G_OBJECT(t
->settings
),
8700 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8701 g_object_set(G_OBJECT(t
->settings
),
8702 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8703 g_object_set(G_OBJECT(t
->settings
),
8704 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8705 g_object_set(G_OBJECT(t
->settings
),
8706 "enable-developer-extras", TRUE
, (char *)NULL
);
8707 g_object_set(G_OBJECT(t
->wv
),
8708 "full-content-zoom", TRUE
, (char *)NULL
);
8710 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8714 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8716 struct tab
*ti
, *t
= NULL
;
8717 gdouble view_size
, value
, max
;
8720 TAILQ_FOREACH(ti
, &tabs
, entry
)
8721 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8729 if (adjustment
== NULL
)
8730 adjustment
= gtk_scrolled_window_get_vadjustment(
8731 GTK_SCROLLED_WINDOW(t
->browser_win
));
8733 view_size
= gtk_adjustment_get_page_size(adjustment
);
8734 value
= gtk_adjustment_get_value(adjustment
);
8735 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8738 position
= g_strdup("All");
8739 else if (value
== max
)
8740 position
= g_strdup("Bot");
8741 else if (value
== 0)
8742 position
= g_strdup("Top");
8744 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8746 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8753 create_window(const gchar
*name
)
8757 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8758 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8759 gtk_widget_set_name(w
, name
);
8760 gtk_window_set_wmclass(GTK_WINDOW(w
), name
, "XXXTerm");
8766 create_browser(struct tab
*t
)
8770 GtkAdjustment
*adjustment
;
8773 show_oops(NULL
, "create_browser invalid parameters");
8777 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8778 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8779 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8780 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8782 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8783 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8784 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8786 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8787 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8790 t
->settings
= webkit_web_settings_new();
8792 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
8794 if (user_agent
== NULL
) {
8795 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8797 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8800 t
->user_agent
= g_strdup(user_agent
);
8802 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8805 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8806 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8807 G_CALLBACK(update_statusbar_position
), NULL
);
8816 create_kiosk_toolbar(struct tab
*t
)
8818 GtkWidget
*toolbar
= NULL
, *b
;
8820 b
= gtk_hbox_new(FALSE
, 0);
8822 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8824 /* backward button */
8825 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8826 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8827 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8828 G_CALLBACK(backward_cb
), t
);
8829 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8831 /* forward button */
8832 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8833 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8834 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8835 G_CALLBACK(forward_cb
), t
);
8836 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8839 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8840 gtk_widget_set_sensitive(t
->gohome
, true);
8841 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8842 G_CALLBACK(home_cb
), t
);
8843 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8845 /* create widgets but don't use them */
8846 t
->uri_entry
= gtk_entry_new();
8847 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8848 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8849 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8855 create_toolbar(struct tab
*t
)
8857 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8859 b
= gtk_hbox_new(FALSE
, 0);
8861 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8863 /* backward button */
8864 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8865 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8866 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8867 G_CALLBACK(backward_cb
), t
);
8868 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8870 /* forward button */
8871 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8872 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8873 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8874 G_CALLBACK(forward_cb
), t
);
8875 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8879 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8880 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8881 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8882 G_CALLBACK(stop_cb
), t
);
8883 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8887 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8888 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8889 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8890 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8891 G_CALLBACK(js_toggle_cb
), t
);
8892 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8894 t
->uri_entry
= gtk_entry_new();
8895 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8896 G_CALLBACK(activate_uri_entry_cb
), t
);
8897 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8898 G_CALLBACK(entry_key_cb
), t
);
8900 eb1
= gtk_hbox_new(FALSE
, 0);
8901 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8902 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8903 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8906 if (search_string
) {
8908 t
->search_entry
= gtk_entry_new();
8909 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8910 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8911 G_CALLBACK(activate_search_entry_cb
), t
);
8912 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8913 G_CALLBACK(entry_key_cb
), t
);
8914 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8915 eb2
= gtk_hbox_new(FALSE
, 0);
8916 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8917 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8919 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8926 create_buffers(struct tab
*t
)
8928 GtkCellRenderer
*renderer
;
8931 view
= gtk_tree_view_new();
8933 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8935 renderer
= gtk_cell_renderer_text_new();
8936 gtk_tree_view_insert_column_with_attributes
8937 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8939 renderer
= gtk_cell_renderer_text_new();
8940 gtk_tree_view_insert_column_with_attributes
8941 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8944 gtk_tree_view_set_model
8945 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8951 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8952 GtkTreeViewColumn
*col
, struct tab
*t
)
8957 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8959 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8962 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8963 set_current_tab(id
- 1);
8969 /* after tab reordering/creation/removal */
8976 TAILQ_FOREACH(t
, &tabs
, entry
) {
8977 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8978 if (t
->tab_id
> maxid
)
8981 gtk_widget_show(t
->tab_elems
.sep
);
8984 TAILQ_FOREACH(t
, &tabs
, entry
) {
8985 if (t
->tab_id
== maxid
) {
8986 gtk_widget_hide(t
->tab_elems
.sep
);
8992 /* after active tab change */
8994 recolor_compact_tabs(void)
9000 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9001 TAILQ_FOREACH(t
, &tabs
, entry
)
9002 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
9005 curid
= gtk_notebook_get_current_page(notebook
);
9006 TAILQ_FOREACH(t
, &tabs
, entry
)
9007 if (t
->tab_id
== curid
) {
9008 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
9009 gtk_widget_modify_fg(t
->tab_elems
.label
,
9010 GTK_STATE_NORMAL
, &color
);
9016 set_current_tab(int page_num
)
9018 buffercmd_abort(get_current_tab());
9019 gtk_notebook_set_current_page(notebook
, page_num
);
9020 recolor_compact_tabs();
9024 undo_close_tab_save(struct tab
*t
)
9028 struct undo
*u1
, *u2
;
9030 WebKitWebHistoryItem
*item
;
9032 if ((uri
= get_uri(t
)) == NULL
)
9035 u1
= g_malloc0(sizeof(struct undo
));
9036 u1
->uri
= g_strdup(uri
);
9038 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9040 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
9041 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
9044 /* forward history */
9045 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
9049 u1
->history
= g_list_prepend(u1
->history
,
9050 webkit_web_history_item_copy(item
));
9051 items
= g_list_next(items
);
9056 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
9057 u1
->history
= g_list_prepend(u1
->history
,
9058 webkit_web_history_item_copy(item
));
9062 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
9066 u1
->history
= g_list_prepend(u1
->history
,
9067 webkit_web_history_item_copy(item
));
9068 items
= g_list_next(items
);
9071 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
9073 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
9074 u2
= TAILQ_LAST(&undos
, undo_tailq
);
9075 TAILQ_REMOVE(&undos
, u2
, entry
);
9077 g_list_free(u2
->history
);
9086 delete_tab(struct tab
*t
)
9090 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
9096 * no need to join thread here because it won't access t on completion
9099 TAILQ_REMOVE(&tabs
, t
, entry
);
9102 /* Halt all webkit activity. */
9103 abort_favicon_download(t
);
9104 webkit_web_view_stop_loading(t
->wv
);
9106 /* Save the tab, so we can undo the close. */
9107 undo_close_tab_save(t
);
9111 g_source_remove(t
->search_id
);
9114 bzero(&a
, sizeof a
);
9116 inspector_cmd(t
, &a
);
9118 if (browser_mode
== XT_BM_KIOSK
) {
9119 gtk_widget_destroy(t
->uri_entry
);
9120 gtk_widget_destroy(t
->stop
);
9121 gtk_widget_destroy(t
->js_toggle
);
9124 gtk_widget_destroy(t
->tab_elems
.eventbox
);
9125 gtk_widget_destroy(t
->vbox
);
9127 g_free(t
->user_agent
);
9128 g_free(t
->stylesheet
);
9133 if (TAILQ_EMPTY(&tabs
)) {
9134 if (browser_mode
== XT_BM_KIOSK
)
9135 create_new_tab(home
, NULL
, 1, -1);
9137 create_new_tab(NULL
, NULL
, 1, -1);
9140 /* recreate session */
9141 if (session_autosave
) {
9142 bzero(&a
, sizeof a
);
9144 save_tabs(NULL
, &a
);
9148 recolor_compact_tabs();
9152 update_statusbar_zoom(struct tab
*t
)
9155 char s
[16] = { '\0' };
9157 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9158 if ((zoom
<= 0.99 || zoom
>= 1.01))
9159 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
9160 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
9164 setzoom_webkit(struct tab
*t
, int adjust
)
9166 #define XT_ZOOMPERCENT 0.04
9171 show_oops(NULL
, "setzoom_webkit invalid parameters");
9175 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9176 if (adjust
== XT_ZOOM_IN
)
9177 zoom
+= XT_ZOOMPERCENT
;
9178 else if (adjust
== XT_ZOOM_OUT
)
9179 zoom
-= XT_ZOOMPERCENT
;
9180 else if (adjust
> 0)
9181 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
9183 show_oops(t
, "setzoom_webkit invalid zoom value");
9187 if (zoom
< XT_ZOOMPERCENT
)
9188 zoom
= XT_ZOOMPERCENT
;
9189 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
9190 update_statusbar_zoom(t
);
9194 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
9196 struct tab
*t
= (struct tab
*) data
;
9198 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
9200 switch (event
->button
) {
9202 set_current_tab(t
->tab_id
);
9213 append_tab(struct tab
*t
)
9218 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9219 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9223 create_sbe(int width
)
9227 sbe
= gtk_entry_new();
9228 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9229 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9230 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9231 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9232 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9233 gtk_widget_set_size_request(sbe
, width
, -1);
9239 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9244 WebKitWebHistoryItem
*item
;
9248 int sbe_p
= 0, sbe_b
= 0,
9251 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9253 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9254 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9258 t
= g_malloc0(sizeof *t
);
9260 if (title
== NULL
) {
9261 title
= "(untitled)";
9265 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9267 /* label + button for tab */
9268 b
= gtk_hbox_new(FALSE
, 0);
9271 #if GTK_CHECK_VERSION(2, 20, 0)
9272 t
->spinner
= gtk_spinner_new();
9274 t
->label
= gtk_label_new(title
);
9275 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9276 gtk_widget_set_size_request(t
->label
, 100, 0);
9277 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9278 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9279 gtk_widget_set_size_request(b
, 130, 0);
9281 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9282 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9283 #if GTK_CHECK_VERSION(2, 20, 0)
9284 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9288 if (browser_mode
== XT_BM_KIOSK
) {
9289 t
->toolbar
= create_kiosk_toolbar(t
);
9290 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9293 t
->toolbar
= create_toolbar(t
);
9295 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9303 t
->browser_win
= create_browser(t
);
9304 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9306 /* oops message for user feedback */
9307 t
->oops
= gtk_entry_new();
9308 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9309 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9310 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9311 gdk_color_parse(XT_COLOR_RED
, &color
);
9312 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9313 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9314 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9317 t
->cmd
= gtk_entry_new();
9318 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9319 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9320 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9321 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9324 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9326 t
->sbe
.statusbar
= gtk_entry_new();
9327 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9328 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9329 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9330 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9332 /* create these widgets only if specified in statusbar_elems */
9334 t
->sbe
.position
= create_sbe(40);
9335 t
->sbe
.zoom
= create_sbe(40);
9336 t
->sbe
.buffercmd
= create_sbe(60);
9338 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9340 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9343 /* gtk widgets cannot be added to a box twice. sbe_* variables
9344 make sure of this */
9345 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9349 GtkWidget
*sep
= gtk_vseparator_new();
9351 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9352 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9353 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9354 FALSE
, FALSE
, FALSE
);
9359 warnx("flag \"%c\" specified more than "
9360 "once in statusbar_elems\n", *p
);
9364 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9365 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9369 warnx("flag \"%c\" specified more than "
9370 "once in statusbar_elems\n", *p
);
9374 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9375 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9379 warnx("flag \"%c\" specified more than "
9380 "once in statusbar_elems\n", *p
);
9384 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9385 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9388 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9393 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9396 t
->buffers
= create_buffers(t
);
9397 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9399 /* xtp meaning is normal by default */
9400 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9402 /* set empty favicon */
9403 xt_icon_from_name(t
, "text-html");
9405 /* and show it all */
9406 gtk_widget_show_all(b
);
9407 gtk_widget_show_all(t
->vbox
);
9409 /* compact tab bar */
9410 t
->tab_elems
.label
= gtk_label_new(title
);
9411 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9412 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9413 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9414 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9416 t
->tab_elems
.eventbox
= gtk_event_box_new();
9417 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9418 t
->tab_elems
.sep
= gtk_vseparator_new();
9420 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9421 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9422 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9423 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9424 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9425 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9427 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9429 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9431 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9434 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9436 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9438 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9441 id
= position
>= 0 ? position
:
9442 gtk_notebook_get_current_page(notebook
) + 1;
9443 if (id
> gtk_notebook_get_n_pages(notebook
))
9446 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9447 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9448 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9449 t
->tab_elems
.eventbox
, id
);
9454 #if GTK_CHECK_VERSION(2, 20, 0)
9455 /* turn spinner off if we are a new tab without uri */
9457 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9458 gtk_widget_hide(t
->spinner
);
9461 /* make notebook tabs reorderable */
9462 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9464 /* compact tabs clickable */
9465 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9466 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9468 g_object_connect(G_OBJECT(t
->cmd
),
9469 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9470 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9471 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9472 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9473 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9476 /* reuse wv_button_cb to hide oops */
9477 g_object_connect(G_OBJECT(t
->oops
),
9478 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9481 g_signal_connect(t
->buffers
,
9482 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9483 g_object_connect(G_OBJECT(t
->buffers
),
9484 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9486 g_object_connect(G_OBJECT(t
->wv
),
9487 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9488 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9489 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9490 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9491 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9492 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9493 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9494 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9495 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9496 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9497 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9498 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9499 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9500 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9501 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9502 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9504 g_signal_connect(t
->wv
,
9505 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9506 g_signal_connect(t
->wv
,
9507 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9509 /* hijack the unused keys as if we were the browser */
9510 g_object_connect(G_OBJECT(t
->toolbar
),
9511 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9514 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9515 G_CALLBACK(tab_close_cb
), t
);
9518 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9519 /* restore the tab's history */
9520 if (u
&& u
->history
) {
9524 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9525 items
= g_list_next(items
);
9528 item
= g_list_nth_data(u
->history
, u
->back
);
9530 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9533 g_list_free(u
->history
);
9535 webkit_web_back_forward_list_clear(t
->bfl
);
9541 url_set_visibility();
9542 statusbar_set_visibility();
9545 set_current_tab(t
->tab_id
);
9546 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9550 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9554 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9561 recolor_compact_tabs();
9562 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9567 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9573 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9575 if (gtk_notebook_get_current_page(notebook
) == -1)
9578 TAILQ_FOREACH(t
, &tabs
, entry
) {
9579 if (t
->tab_id
== pn
) {
9580 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9583 uri
= get_title(t
, TRUE
);
9584 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9590 /* can't use focus_webview here */
9591 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9598 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9601 struct tab
*t
= NULL
, *tt
;
9605 TAILQ_FOREACH(tt
, &tabs
, entry
)
9606 if (tt
->tab_id
== pn
) {
9612 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9614 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9619 menuitem_response(struct tab
*t
)
9621 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9625 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9627 GtkWidget
*menu
, *menu_items
;
9628 GdkEventButton
*bevent
;
9632 if (event
->type
== GDK_BUTTON_PRESS
) {
9633 bevent
= (GdkEventButton
*) event
;
9634 menu
= gtk_menu_new();
9636 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9637 if ((uri
= get_uri(ti
)) == NULL
)
9638 /* XXX make sure there is something to print */
9639 /* XXX add gui pages in here to look purdy */
9641 menu_items
= gtk_menu_item_new_with_label(uri
);
9642 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9643 gtk_widget_show(menu_items
);
9645 g_signal_connect_swapped((menu_items
),
9646 "activate", G_CALLBACK(menuitem_response
),
9650 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9651 bevent
->button
, bevent
->time
);
9653 /* unref object so it'll free itself when popped down */
9654 #if !GTK_CHECK_VERSION(3, 0, 0)
9655 /* XXX does not need unref with gtk+3? */
9656 g_object_ref_sink(menu
);
9657 g_object_unref(menu
);
9660 return (TRUE
/* eat event */);
9663 return (FALSE
/* propagate */);
9667 icon_size_map(int icon_size
)
9669 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9670 icon_size
> GTK_ICON_SIZE_DIALOG
)
9671 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9677 create_button(char *name
, char *stockid
, int size
)
9679 GtkWidget
*button
, *image
;
9683 rcstring
= g_strdup_printf(
9684 "style \"%s-style\"\n"
9686 " GtkWidget::focus-padding = 0\n"
9687 " GtkWidget::focus-line-width = 0\n"
9691 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9692 gtk_rc_parse_string(rcstring
);
9694 button
= gtk_button_new();
9695 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9696 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9698 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9699 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9700 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9701 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9702 gtk_widget_set_name(button
, name
);
9703 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9709 button_set_stockid(GtkWidget
*button
, char *stockid
)
9713 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9714 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9715 gtk_button_set_image(GTK_BUTTON(button
), image
);
9719 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9722 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9725 if (xterm_workaround
== 0)
9729 * xterm doesn't play nice with clipboards because it clears the
9730 * primary when clicked. We rely on primary being set to properly
9731 * handle middle mouse button clicks (paste). So when someone clears
9732 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9733 * other application behavior (as in DON'T clear primary).
9736 p
= gtk_clipboard_wait_for_text(primary
);
9738 if (gdk_property_get(gdk_get_default_root_window(),
9740 gdk_atom_intern("STRING", FALSE
),
9742 1024 * 1024 /* picked out of my butt */,
9748 /* yes sir, we need to NUL the string */
9750 gtk_clipboard_set_text(primary
, p
, -1);
9764 char file
[PATH_MAX
];
9767 vbox
= gtk_vbox_new(FALSE
, 0);
9768 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9769 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9770 #if !GTK_CHECK_VERSION(3, 0, 0)
9771 /* XXX seems to be needed with gtk+2 */
9772 gtk_notebook_set_tab_hborder(notebook
, 0);
9773 gtk_notebook_set_tab_vborder(notebook
, 0);
9775 gtk_notebook_set_scrollable(notebook
, TRUE
);
9776 gtk_notebook_set_show_border(notebook
, FALSE
);
9777 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9779 abtn
= gtk_button_new();
9780 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9781 gtk_widget_set_size_request(arrow
, -1, -1);
9782 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9783 gtk_widget_set_size_request(abtn
, -1, 20);
9785 #if GTK_CHECK_VERSION(2, 20, 0)
9786 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9788 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9790 /* compact tab bar */
9791 tab_bar
= gtk_hbox_new(TRUE
, 0);
9793 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9794 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9795 gtk_widget_set_size_request(vbox
, -1, -1);
9797 g_object_connect(G_OBJECT(notebook
),
9798 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9800 g_object_connect(G_OBJECT(notebook
),
9801 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9802 NULL
, (char *)NULL
);
9803 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9804 G_CALLBACK(arrow_cb
), NULL
);
9806 main_window
= create_window("xxxterm");
9807 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9808 g_signal_connect(G_OBJECT(main_window
), "delete_event",
9809 G_CALLBACK(gtk_main_quit
), NULL
);
9812 for (i
= 0; i
< LENGTH(icons
); i
++) {
9813 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9814 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9815 l
= g_list_append(l
, pb
);
9817 gtk_window_set_default_icon_list(l
);
9819 /* clipboard work around */
9820 if (xterm_workaround
)
9822 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9823 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9825 gtk_widget_show_all(abtn
);
9826 gtk_widget_show_all(main_window
);
9827 notebook_tab_set_visibility();
9831 set_hook(void **hook
, char *name
)
9834 errx(1, "set_hook");
9836 if (*hook
== NULL
) {
9837 *hook
= dlsym(RTLD_NEXT
, name
);
9839 errx(1, "can't hook %s", name
);
9843 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9845 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9847 g_return_val_if_fail(cookie1
, FALSE
);
9848 g_return_val_if_fail(cookie2
, FALSE
);
9850 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9851 !strcmp (cookie1
->value
, cookie2
->value
) &&
9852 !strcmp (cookie1
->path
, cookie2
->path
) &&
9853 !strcmp (cookie1
->domain
, cookie2
->domain
));
9857 transfer_cookies(void)
9860 SoupCookie
*sc
, *pc
;
9862 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9864 for (;cf
; cf
= cf
->next
) {
9866 sc
= soup_cookie_copy(pc
);
9867 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9870 soup_cookies_free(cf
);
9874 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9879 print_cookie("soup_cookie_jar_delete_cookie", c
);
9881 if (cookies_enabled
== 0)
9884 if (jar
== NULL
|| c
== NULL
)
9887 /* find and remove from persistent jar */
9888 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9890 for (;cf
; cf
= cf
->next
) {
9892 if (soup_cookie_equal(ci
, c
)) {
9893 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9898 soup_cookies_free(cf
);
9900 /* delete from session jar */
9901 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9905 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9907 struct domain
*d
= NULL
;
9911 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9912 jar
, p_cookiejar
, s_cookiejar
);
9914 if (cookies_enabled
== 0)
9917 /* see if we are up and running */
9918 if (p_cookiejar
== NULL
) {
9919 _soup_cookie_jar_add_cookie(jar
, cookie
);
9922 /* disallow p_cookiejar adds, shouldn't happen */
9923 if (jar
== p_cookiejar
)
9927 if (jar
== NULL
|| cookie
== NULL
)
9930 if (enable_cookie_whitelist
&&
9931 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9933 DNPRINTF(XT_D_COOKIE
,
9934 "soup_cookie_jar_add_cookie: reject %s\n",
9936 if (save_rejected_cookies
) {
9937 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9938 show_oops(NULL
, "can't open reject cookie file");
9941 fseek(r_cookie_f
, 0, SEEK_END
);
9942 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9943 cookie
->http_only
? "#HttpOnly_" : "",
9945 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9947 cookie
->secure
? "TRUE" : "FALSE",
9949 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9956 if (!allow_volatile_cookies
)
9960 if (cookie
->expires
== NULL
&& session_timeout
) {
9961 soup_cookie_set_expires(cookie
,
9962 soup_date_new_from_now(session_timeout
));
9963 print_cookie("modified add cookie", cookie
);
9966 /* see if we are white listed for persistence */
9967 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9968 /* add to persistent jar */
9969 c
= soup_cookie_copy(cookie
);
9970 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9971 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9974 /* add to session jar */
9975 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9976 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9982 char file
[PATH_MAX
];
9984 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9985 "soup_cookie_jar_add_cookie");
9986 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9987 "soup_cookie_jar_delete_cookie");
9989 if (cookies_enabled
== 0)
9993 * the following code is intricate due to overriding several libsoup
9995 * do not alter order of these operations.
9998 /* rejected cookies */
9999 if (save_rejected_cookies
)
10000 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
10003 /* persistent cookies */
10004 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
10005 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
10007 /* session cookies */
10008 s_cookiejar
= soup_cookie_jar_new();
10009 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
10010 cookie_policy
, (void *)NULL
);
10011 transfer_cookies();
10013 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
10017 setup_proxy(char *uri
)
10020 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
10021 soup_uri_free(proxy_uri
);
10025 if (http_proxy
!= uri
) {
10026 g_free(http_proxy
);
10032 http_proxy
= g_strdup(uri
);
10033 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
10034 proxy_uri
= soup_uri_new(http_proxy
);
10035 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
10036 g_object_set(session
, "proxy-uri", proxy_uri
,
10042 set_http_proxy(char *proxy
)
10049 /* see if we need to clear it instead */
10050 if (strlen(proxy
) == 0) {
10055 uri
= soup_uri_new(proxy
);
10056 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
10059 setup_proxy(proxy
);
10061 soup_uri_free(uri
);
10067 send_cmd_to_socket(char *cmd
)
10069 int s
, len
, rv
= 1;
10070 struct sockaddr_un sa
;
10072 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10073 warnx("%s: socket", __func__
);
10077 sa
.sun_family
= AF_UNIX
;
10078 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10079 work_dir
, XT_SOCKET_FILE
);
10080 len
= SUN_LEN(&sa
);
10082 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10083 warnx("%s: connect", __func__
);
10087 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
10088 warnx("%s: send", __func__
);
10099 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
10102 char str
[XT_MAX_URL_LENGTH
];
10103 socklen_t t
= sizeof(struct sockaddr_un
);
10104 struct sockaddr_un sa
;
10109 gint fd
= g_io_channel_unix_get_fd(source
);
10111 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
10116 if (getpeereid(s
, &uid
, &gid
) == -1) {
10117 warn("getpeereid");
10120 if (uid
!= getuid() || gid
!= getgid()) {
10121 warnx("unauthorized user");
10127 warnx("not a valid user");
10131 n
= recv(s
, str
, sizeof(str
), 0);
10135 tt
= TAILQ_LAST(&tabs
, tab_list
);
10136 cmd_execute(tt
, str
);
10143 int s
, len
, rv
= 1;
10144 struct sockaddr_un sa
;
10146 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10147 warn("is_running: socket");
10151 sa
.sun_family
= AF_UNIX
;
10152 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10153 work_dir
, XT_SOCKET_FILE
);
10154 len
= SUN_LEN(&sa
);
10156 /* connect to see if there is a listener */
10157 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
10158 rv
= 0; /* not running */
10160 rv
= 1; /* already running */
10171 struct sockaddr_un sa
;
10173 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10174 warn("build_socket: socket");
10178 sa
.sun_family
= AF_UNIX
;
10179 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10180 work_dir
, XT_SOCKET_FILE
);
10181 len
= SUN_LEN(&sa
);
10183 /* connect to see if there is a listener */
10184 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10185 /* no listener so we will */
10186 unlink(sa
.sun_path
);
10188 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10189 warn("build_socket: bind");
10193 if (listen(s
, 1) == -1) {
10194 warn("build_socket: listen");
10207 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10208 GtkTreeIter
*iter
, struct tab
*t
)
10212 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10213 load_uri(t
, value
);
10220 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10221 GtkTreeIter
*iter
, struct tab
*t
)
10225 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10226 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10227 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10234 completion_add_uri(const gchar
*uri
)
10238 /* add uri to list_store */
10239 gtk_list_store_append(completion_model
, &iter
);
10240 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10244 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10245 GtkTreeIter
*iter
, gpointer user_data
)
10248 gboolean match
= FALSE
;
10250 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10256 match
= match_uri(value
, key
);
10263 completion_add(struct tab
*t
)
10265 /* enable completion for tab */
10266 t
->completion
= gtk_entry_completion_new();
10267 gtk_entry_completion_set_text_column(t
->completion
, 0);
10268 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10269 gtk_entry_completion_set_model(t
->completion
,
10270 GTK_TREE_MODEL(completion_model
));
10271 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10273 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10274 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10275 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10276 G_CALLBACK(completion_select_cb
), t
);
10277 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10278 G_CALLBACK(completion_hover_cb
), t
);
10286 if (stat(dir
, &sb
)) {
10287 if (mkdir(dir
, S_IRWXU
) == -1)
10288 err(1, "mkdir %s", dir
);
10289 if (stat(dir
, &sb
))
10290 err(1, "stat %s", dir
);
10292 if (S_ISDIR(sb
.st_mode
) == 0)
10293 errx(1, "%s not a dir", dir
);
10294 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10295 warnx("fixing invalid permissions on %s", dir
);
10296 if (chmod(dir
, S_IRWXU
) == -1)
10297 err(1, "chmod %s", dir
);
10305 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10309 GStaticRecMutex my_gdk_mtx
= G_STATIC_REC_MUTEX_INIT
;
10310 volatile int mtx_depth
;
10314 * The linux flash plugin violates the gdk locking mechanism.
10315 * Work around the issue by using a recursive mutex with some match applied
10316 * to see if we hit a buggy condition.
10318 * The following code is painful so just don't read it. It really doesn't
10319 * make much sense but seems to work.
10324 g_static_rec_mutex_lock(&my_gdk_mtx
);
10327 if (mtx_depth
<= 0) {
10328 /* should not happen */
10329 show_oops(NULL
, "negative mutex locking bug, trying to "
10331 fprintf(stderr
, "negative mutex locking bug, trying to "
10333 g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10334 g_static_rec_mutex_lock(&my_gdk_mtx
);
10339 if (mtx_depth
!= 1) {
10340 /* decrease mutext depth to 1 */
10342 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10344 } while (mtx_depth
> 1);
10353 /* if mutex depth isn't 1 then something went bad */
10354 if (mtx_depth
!= 1) {
10355 x
= g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10357 /* should not happen */
10358 show_oops(NULL
, "mutex unlocking bug, trying to "
10360 fprintf(stderr
, "mutex unlocking bug, trying to "
10364 if (mtx_complain
== 0) {
10365 show_oops(NULL
, "buggy mutex implementation detected, "
10366 "work around implemented");
10367 fprintf(stderr
, "buggy mutex implementation detected, "
10368 "work around implemented");
10375 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10379 main(int argc
, char *argv
[])
10382 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10383 char conf
[PATH_MAX
] = { '\0' };
10384 char file
[PATH_MAX
];
10385 char *env_proxy
= NULL
;
10389 struct sigaction sact
;
10390 GIOChannel
*channel
;
10397 g_thread_init(NULL
);
10398 gdk_threads_set_lock_functions(mtx_lock
, mtx_unlock
);
10399 gdk_threads_init();
10400 gdk_threads_enter();
10402 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pthread
);
10404 gtk_init(&argc
, &argv
);
10406 gnutls_global_init();
10408 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10413 RB_INIT(&downloads
);
10415 TAILQ_INIT(&sessions
);
10418 TAILQ_INIT(&aliases
);
10419 TAILQ_INIT(&undos
);
10425 /* fiddle with ulimits */
10426 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10429 /* just use them all */
10430 rlp
.rlim_cur
= rlp
.rlim_max
;
10431 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10433 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10435 else if (rlp
.rlim_cur
<= 256)
10436 startpage_add("%s requires at least 256 file "
10437 "descriptors, currently it has up to %d available",
10438 __progname
, rlp
.rlim_cur
);
10441 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10450 errx(0 , "Version: %s", version
);
10453 strlcpy(conf
, optarg
, sizeof(conf
));
10456 strlcpy(named_session
, optarg
, sizeof(named_session
));
10475 init_keybindings();
10477 /* generate session keys for xtp pages */
10478 generate_xtp_session_key(&dl_session_key
);
10479 generate_xtp_session_key(&hl_session_key
);
10480 generate_xtp_session_key(&cl_session_key
);
10481 generate_xtp_session_key(&fl_session_key
);
10484 bzero(&sact
, sizeof(sact
));
10485 sigemptyset(&sact
.sa_mask
);
10486 sact
.sa_handler
= sigchild
;
10487 sact
.sa_flags
= SA_NOCLDSTOP
;
10488 sigaction(SIGCHLD
, &sact
, NULL
);
10490 /* set download dir */
10491 pwd
= getpwuid(getuid());
10493 errx(1, "invalid user %d", getuid());
10494 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10496 /* compile buffer command regexes */
10499 /* set default string settings */
10500 home
= g_strdup("https://www.cyphertite.com");
10501 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10502 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10503 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10504 cmd_font_name
= g_strdup("monospace normal 9");
10505 oops_font_name
= g_strdup("monospace normal 9");
10506 statusbar_font_name
= g_strdup("monospace normal 9");
10507 tabbar_font_name
= g_strdup("monospace normal 9");
10508 statusbar_elems
= g_strdup("BP");
10509 encoding
= g_strdup("ISO-8859-1");
10511 /* read config file */
10512 if (strlen(conf
) == 0)
10513 snprintf(conf
, sizeof conf
, "%s/.%s",
10514 pwd
->pw_dir
, XT_CONF_FILE
);
10515 config_parse(conf
, 0);
10518 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10519 oops_font
= pango_font_description_from_string(oops_font_name
);
10520 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10521 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10523 /* working directory */
10524 if (strlen(work_dir
) == 0)
10525 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10526 pwd
->pw_dir
, XT_DIR
);
10529 /* icon cache dir */
10530 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10531 xxx_dir(cache_dir
);
10534 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10535 xxx_dir(certs_dir
);
10538 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10539 work_dir
, XT_SESSIONS_DIR
);
10540 xxx_dir(sessions_dir
);
10542 /* runtime settings that can override config file */
10543 if (runtime_settings
[0] != '\0')
10544 config_parse(runtime_settings
, 1);
10547 if (!strcmp(download_dir
, pwd
->pw_dir
))
10548 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10549 xxx_dir(download_dir
);
10551 /* favorites file */
10552 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10553 if (stat(file
, &sb
)) {
10554 warnx("favorites file doesn't exist, creating it");
10555 if ((f
= fopen(file
, "w")) == NULL
)
10556 err(1, "favorites");
10560 /* quickmarks file */
10561 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10562 if (stat(file
, &sb
)) {
10563 warnx("quickmarks file doesn't exist, creating it");
10564 if ((f
= fopen(file
, "w")) == NULL
)
10565 err(1, "quickmarks");
10569 /* search history */
10570 if (history_autosave
) {
10571 snprintf(search_file
, sizeof search_file
, "%s/%s",
10572 work_dir
, XT_SEARCH_FILE
);
10573 if (stat(search_file
, &sb
)) {
10574 warnx("search history file doesn't exist, creating it");
10575 if ((f
= fopen(search_file
, "w")) == NULL
)
10576 err(1, "search_history");
10579 history_read(&shl
, search_file
, &search_history_count
);
10582 /* command history */
10583 if (history_autosave
) {
10584 snprintf(command_file
, sizeof command_file
, "%s/%s",
10585 work_dir
, XT_COMMAND_FILE
);
10586 if (stat(command_file
, &sb
)) {
10587 warnx("command history file doesn't exist, creating it");
10588 if ((f
= fopen(command_file
, "w")) == NULL
)
10589 err(1, "command_history");
10592 history_read(&chl
, command_file
, &cmd_history_count
);
10596 session
= webkit_get_default_session();
10601 if (stat(ssl_ca_file
, &sb
)) {
10602 warnx("no CA file: %s", ssl_ca_file
);
10603 g_free(ssl_ca_file
);
10604 ssl_ca_file
= NULL
;
10606 g_object_set(session
,
10607 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10608 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10612 /* guess_search regex */
10613 if (url_regex
== NULL
)
10614 url_regex
= g_strdup(XT_URL_REGEX
);
10616 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10617 startpage_add("invalid url regex %s", url_regex
);
10620 env_proxy
= getenv("http_proxy");
10622 setup_proxy(env_proxy
);
10624 setup_proxy(http_proxy
);
10627 send_cmd_to_socket(argv
[0]);
10631 /* set some connection parameters */
10632 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10633 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10636 /* see if there is already an xxxterm running */
10637 if (single_instance
&& is_running()) {
10639 warnx("already running");
10644 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10645 send_cmd_to_socket(cmd
);
10655 /* uri completion */
10656 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10659 buffers_store
= gtk_list_store_new
10660 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10666 notebook_tab_set_visibility();
10668 if (save_global_history
)
10669 restore_global_history();
10671 /* restore session list */
10672 restore_sessions_list();
10674 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10675 restore_saved_tabs();
10677 a
.s
= named_session
;
10678 a
.i
= XT_SES_DONOTHING
;
10679 open_tabs(NULL
, &a
);
10682 /* see if we have an exception */
10683 if (!TAILQ_EMPTY(&spl
)) {
10684 create_new_tab("about:startpage", NULL
, focus
, -1);
10689 create_new_tab(argv
[0], NULL
, focus
, -1);
10696 if (TAILQ_EMPTY(&tabs
))
10697 create_new_tab(home
, NULL
, 1, -1);
10700 if ((s
= build_socket()) != -1) {
10701 channel
= g_io_channel_unix_new(s
);
10702 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10708 gdk_threads_leave();
10709 g_static_rec_mutex_unlock_full(&my_gdk_mtx
); /* just in case */
10712 gnutls_global_deinit();