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;
123 #define XT_NAME ("XXXTerm")
124 #define XT_DIR (".xxxterm")
125 #define XT_CACHE_DIR ("cache")
126 #define XT_CERT_DIR ("certs/")
127 #define XT_SESSIONS_DIR ("sessions/")
128 #define XT_CONF_FILE ("xxxterm.conf")
129 #define XT_FAVS_FILE ("favorites")
130 #define XT_QMARKS_FILE ("quickmarks")
131 #define XT_SAVED_TABS_FILE ("main_session")
132 #define XT_RESTART_TABS_FILE ("restart_tabs")
133 #define XT_SOCKET_FILE ("socket")
134 #define XT_HISTORY_FILE ("history")
135 #define XT_REJECT_FILE ("rejected.txt")
136 #define XT_COOKIE_FILE ("cookies.txt")
137 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
138 #define XT_SEARCH_FILE ("search_history")
139 #define XT_COMMAND_FILE ("command_history")
140 #define XT_CB_HANDLED (TRUE)
141 #define XT_CB_PASSTHROUGH (FALSE)
142 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
143 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
144 #define XT_DLMAN_REFRESH "10"
145 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
146 "td{overflow: hidden;" \
147 " padding: 2px 2px 2px 2px;" \
148 " border: 1px solid black;" \
149 " vertical-align:top;" \
150 " word-wrap: break-word}\n" \
151 "tr:hover{background: #ffff99}\n" \
152 "th{background-color: #cccccc;" \
153 " border: 1px solid black}\n" \
154 "table{width: 100%%;" \
155 " border: 1px black solid;" \
156 " border-collapse:collapse}\n" \
158 "border: 1px solid black;" \
161 ".progress-inner{float: left;" \
163 " background: green}\n" \
164 ".dlstatus{font-size: small;" \
165 " text-align: center}\n" \
167 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
168 #define XT_MAX_UNDO_CLOSE_TAB (32)
169 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
170 #define XT_PRINT_EXTRA_MARGIN 10
171 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
172 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
175 #define XT_COLOR_RED "#cc0000"
176 #define XT_COLOR_YELLOW "#ffff66"
177 #define XT_COLOR_BLUE "lightblue"
178 #define XT_COLOR_GREEN "#99ff66"
179 #define XT_COLOR_WHITE "white"
180 #define XT_COLOR_BLACK "black"
182 #define XT_COLOR_CT_BACKGROUND "#000000"
183 #define XT_COLOR_CT_INACTIVE "#dddddd"
184 #define XT_COLOR_CT_ACTIVE "#bbbb00"
185 #define XT_COLOR_CT_SEPARATOR "#555555"
187 #define XT_COLOR_SB_SEPARATOR "#555555"
189 #define XT_PROTO_DELIM "://"
192 * xxxterm "protocol" (xtp)
193 * We use this for managing stuff like downloads and favorites. They
194 * make magical HTML pages in memory which have xxxt:// links in order
195 * to communicate with xxxterm's internals. These links take the format:
196 * xxxt://class/session_key/action/arg
198 * Don't begin xtp class/actions as 0. atoi returns that on error.
200 * Typically we have not put addition of items in this framework, as
201 * adding items is either done via an ex-command or via a keybinding instead.
204 #define XT_XTP_STR "xxxt://"
206 /* XTP classes (xxxt://<class>) */
207 #define XT_XTP_INVALID 0 /* invalid */
208 #define XT_XTP_DL 1 /* downloads */
209 #define XT_XTP_HL 2 /* history */
210 #define XT_XTP_CL 3 /* cookies */
211 #define XT_XTP_FL 4 /* favorites */
213 /* XTP download actions */
214 #define XT_XTP_DL_LIST 1
215 #define XT_XTP_DL_CANCEL 2
216 #define XT_XTP_DL_REMOVE 3
218 /* XTP history actions */
219 #define XT_XTP_HL_LIST 1
220 #define XT_XTP_HL_REMOVE 2
222 /* XTP cookie actions */
223 #define XT_XTP_CL_LIST 1
224 #define XT_XTP_CL_REMOVE 2
226 /* XTP cookie actions */
227 #define XT_XTP_FL_LIST 1
228 #define XT_XTP_FL_REMOVE 2
231 #define XT_MOVE_INVALID (0)
232 #define XT_MOVE_DOWN (1)
233 #define XT_MOVE_UP (2)
234 #define XT_MOVE_BOTTOM (3)
235 #define XT_MOVE_TOP (4)
236 #define XT_MOVE_PAGEDOWN (5)
237 #define XT_MOVE_PAGEUP (6)
238 #define XT_MOVE_HALFDOWN (7)
239 #define XT_MOVE_HALFUP (8)
240 #define XT_MOVE_LEFT (9)
241 #define XT_MOVE_FARLEFT (10)
242 #define XT_MOVE_RIGHT (11)
243 #define XT_MOVE_FARRIGHT (12)
244 #define XT_MOVE_PERCENT (13)
246 #define XT_QMARK_SET (0)
247 #define XT_QMARK_OPEN (1)
248 #define XT_QMARK_TAB (2)
250 #define XT_MARK_SET (0)
251 #define XT_MARK_GOTO (1)
253 #define XT_TAB_LAST (-4)
254 #define XT_TAB_FIRST (-3)
255 #define XT_TAB_PREV (-2)
256 #define XT_TAB_NEXT (-1)
257 #define XT_TAB_INVALID (0)
258 #define XT_TAB_NEW (1)
259 #define XT_TAB_DELETE (2)
260 #define XT_TAB_DELQUIT (3)
261 #define XT_TAB_OPEN (4)
262 #define XT_TAB_UNDO_CLOSE (5)
263 #define XT_TAB_SHOW (6)
264 #define XT_TAB_HIDE (7)
265 #define XT_TAB_NEXTSTYLE (8)
267 #define XT_NAV_INVALID (0)
268 #define XT_NAV_BACK (1)
269 #define XT_NAV_FORWARD (2)
270 #define XT_NAV_RELOAD (3)
272 #define XT_FOCUS_INVALID (0)
273 #define XT_FOCUS_URI (1)
274 #define XT_FOCUS_SEARCH (2)
276 #define XT_SEARCH_INVALID (0)
277 #define XT_SEARCH_NEXT (1)
278 #define XT_SEARCH_PREV (2)
280 #define XT_PASTE_CURRENT_TAB (0)
281 #define XT_PASTE_NEW_TAB (1)
283 #define XT_ZOOM_IN (-1)
284 #define XT_ZOOM_OUT (-2)
285 #define XT_ZOOM_NORMAL (100)
287 #define XT_URL_SHOW (1)
288 #define XT_URL_HIDE (2)
290 #define XT_WL_TOGGLE (1<<0)
291 #define XT_WL_ENABLE (1<<1)
292 #define XT_WL_DISABLE (1<<2)
293 #define XT_WL_FQDN (1<<3) /* default */
294 #define XT_WL_TOPLEVEL (1<<4)
295 #define XT_WL_PERSISTENT (1<<5)
296 #define XT_WL_SESSION (1<<6)
297 #define XT_WL_RELOAD (1<<7)
299 #define XT_SHOW (1<<7)
300 #define XT_DELETE (1<<8)
301 #define XT_SAVE (1<<9)
302 #define XT_OPEN (1<<10)
304 #define XT_CMD_OPEN (0)
305 #define XT_CMD_OPEN_CURRENT (1)
306 #define XT_CMD_TABNEW (2)
307 #define XT_CMD_TABNEW_CURRENT (3)
309 #define XT_STATUS_NOTHING (0)
310 #define XT_STATUS_LINK (1)
311 #define XT_STATUS_URI (2)
312 #define XT_STATUS_LOADING (3)
314 #define XT_SES_DONOTHING (0)
315 #define XT_SES_CLOSETABS (1)
317 #define XT_BM_NORMAL (0)
318 #define XT_BM_WHITELIST (1)
319 #define XT_BM_KIOSK (2)
321 #define XT_PREFIX (1<<0)
322 #define XT_USERARG (1<<1)
323 #define XT_URLARG (1<<2)
324 #define XT_INTARG (1<<3)
325 #define XT_SESSARG (1<<4)
326 #define XT_SETARG (1<<5)
328 #define XT_HINT_NEWTAB (1<<0)
330 #define XT_MODE_INSERT (0)
331 #define XT_MODE_COMMAND (1)
333 #define XT_TABS_NORMAL 0
334 #define XT_TABS_COMPACT 1
336 #define XT_BUFCMD_SZ (8)
338 #define XT_EJS_SHOW (1<<0)
346 TAILQ_ENTRY(mime_type
) entry
;
348 TAILQ_HEAD(mime_type_list
, mime_type
);
354 TAILQ_ENTRY(alias
) entry
;
356 TAILQ_HEAD(alias_list
, alias
);
358 /* settings that require restart */
359 int tabless
= 0; /* allow only 1 tab */
360 int enable_socket
= 0;
361 int single_instance
= 0; /* only allow one xxxterm to run */
362 int fancy_bar
= 1; /* fancy toolbar */
363 int browser_mode
= XT_BM_NORMAL
;
364 int enable_localstorage
= 1;
365 char *statusbar_elems
= NULL
;
367 /* runtime settings */
368 int show_tabs
= 1; /* show tabs on notebook */
369 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
370 int show_url
= 1; /* show url toolbar on notebook */
371 int show_statusbar
= 0; /* vimperator style status bar */
372 int ctrl_click_focus
= 0; /* ctrl click gets focus */
373 int cookies_enabled
= 1; /* enable cookies */
374 int read_only_cookies
= 0; /* enable to not write cookies */
375 int enable_scripts
= 1;
376 int enable_plugins
= 1;
377 gfloat default_zoom_level
= 1.0;
378 char default_script
[PATH_MAX
];
379 int window_height
= 768;
380 int window_width
= 1024;
381 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
382 int refresh_interval
= 10; /* download refresh interval */
383 int enable_plugin_whitelist
= 0;
384 int enable_cookie_whitelist
= 0;
385 int enable_js_whitelist
= 0;
386 int session_timeout
= 3600; /* cookie session timeout */
387 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
388 char *ssl_ca_file
= NULL
;
389 char *resource_dir
= NULL
;
390 gboolean ssl_strict_certs
= FALSE
;
391 int append_next
= 1; /* append tab after current tab */
393 char *search_string
= NULL
;
394 char *http_proxy
= NULL
;
395 char download_dir
[PATH_MAX
];
396 char runtime_settings
[PATH_MAX
]; /* override of settings */
397 int allow_volatile_cookies
= 0;
398 int save_global_history
= 0; /* save global history to disk */
399 char *user_agent
= NULL
;
400 int save_rejected_cookies
= 0;
401 int session_autosave
= 0;
402 int guess_search
= 0;
403 int dns_prefetch
= FALSE
;
404 gint max_connections
= 25;
405 gint max_host_connections
= 5;
406 gint enable_spell_checking
= 0;
407 char *spell_check_languages
= NULL
;
408 int xterm_workaround
= 0;
409 char *url_regex
= NULL
;
410 int history_autosave
= 0;
411 char search_file
[PATH_MAX
];
412 char command_file
[PATH_MAX
];
413 char *encoding
= NULL
;
414 int autofocus_onload
= 0;
416 char *cmd_font_name
= NULL
;
417 char *oops_font_name
= NULL
;
418 char *statusbar_font_name
= NULL
;
419 char *tabbar_font_name
= NULL
;
420 PangoFontDescription
*cmd_font
;
421 PangoFontDescription
*oops_font
;
422 PangoFontDescription
*statusbar_font
;
423 PangoFontDescription
*tabbar_font
;
424 char *qmarks
[XT_NOMARKS
];
426 int btn_down
; /* M1 down in any wv */
427 regex_t url_re
; /* guess_search regex */
431 int set_browser_mode(struct settings
*, char *);
432 int set_cookie_policy(struct settings
*, char *);
433 int set_download_dir(struct settings
*, char *);
434 int set_default_script(struct settings
*, char *);
435 int set_runtime_dir(struct settings
*, char *);
436 int set_tab_style(struct settings
*, char *);
437 int set_work_dir(struct settings
*, char *);
438 int add_alias(struct settings
*, char *);
439 int add_mime_type(struct settings
*, char *);
440 int add_cookie_wl(struct settings
*, char *);
441 int add_js_wl(struct settings
*, char *);
442 int add_pl_wl(struct settings
*, char *);
443 int add_kb(struct settings
*, char *);
444 void button_set_stockid(GtkWidget
*, char *);
445 GtkWidget
* create_button(char *, char *, int);
447 char *get_browser_mode(struct settings
*);
448 char *get_cookie_policy(struct settings
*);
449 char *get_download_dir(struct settings
*);
450 char *get_default_script(struct settings
*);
451 char *get_runtime_dir(struct settings
*);
452 char *get_tab_style(struct settings
*);
453 char *get_work_dir(struct settings
*);
454 void startpage_add(const char *, ...);
456 void walk_alias(struct settings
*, void (*)(struct settings
*,
457 char *, void *), void *);
458 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
459 char *, void *), void *);
460 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
461 char *, void *), void *);
462 void walk_pl_wl(struct settings
*, void (*)(struct settings
*,
463 char *, void *), void *);
464 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
466 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
467 char *, void *), void *);
469 void recalc_tabs(void);
470 void recolor_compact_tabs(void);
471 void set_current_tab(int page_num
);
472 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
473 void marks_clear(struct tab
*t
);
475 int set_http_proxy(char *);
478 int (*set
)(struct settings
*, char *);
479 char *(*get
)(struct settings
*);
480 void (*walk
)(struct settings
*,
481 void (*cb
)(struct settings
*, char *, void *),
485 struct special s_browser_mode
= {
491 struct special s_cookie
= {
497 struct special s_alias
= {
503 struct special s_mime
= {
509 struct special s_js
= {
515 struct special s_pl
= {
521 struct special s_kb
= {
527 struct special s_cookie_wl
= {
533 struct special s_default_script
= {
539 struct special s_download_dir
= {
545 struct special s_work_dir
= {
551 struct special s_tab_style
= {
560 #define XT_S_INVALID (0)
563 #define XT_S_FLOAT (3)
565 #define XT_SF_RESTART (1<<0)
566 #define XT_SF_RUNTIME (1<<1)
571 int (*activate
)(char *);
573 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
574 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
575 { "autofocus_onload", XT_S_INT
, 0, &autofocus_onload
, NULL
, NULL
},
576 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
577 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
578 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
579 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
580 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
581 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
582 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
583 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
584 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
585 { "enable_plugin_whitelist", XT_S_INT
, 0, &enable_plugin_whitelist
, NULL
, NULL
},
586 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
587 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
588 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
589 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
590 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
591 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
592 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
593 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
594 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
595 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
596 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
597 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
598 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
599 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
600 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
601 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
602 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
603 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
604 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
605 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
606 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
607 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
608 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
609 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
610 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
611 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
612 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
613 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
614 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
615 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
616 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
617 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
618 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
619 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
620 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
621 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
622 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
625 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
626 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
627 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
628 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
630 /* runtime settings */
631 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
632 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
633 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
634 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
635 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
636 { "pl_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_pl
},
639 int about(struct tab
*, struct karg
*);
640 int blank(struct tab
*, struct karg
*);
641 int ca_cmd(struct tab
*, struct karg
*);
642 int cookie_show_wl(struct tab
*, struct karg
*);
643 int js_show_wl(struct tab
*, struct karg
*);
644 int pl_show_wl(struct tab
*, struct karg
*);
645 int help(struct tab
*, struct karg
*);
646 int set(struct tab
*, struct karg
*);
647 int stats(struct tab
*, struct karg
*);
648 int marco(struct tab
*, struct karg
*);
649 int startpage(struct tab
*, struct karg
*);
650 const char * marco_message(int *);
651 int xtp_page_cl(struct tab
*, struct karg
*);
652 int xtp_page_dl(struct tab
*, struct karg
*);
653 int xtp_page_fl(struct tab
*, struct karg
*);
654 int xtp_page_hl(struct tab
*, struct karg
*);
655 void xt_icon_from_file(struct tab
*, char *);
656 const gchar
*get_uri(struct tab
*);
657 const gchar
*get_title(struct tab
*, bool);
659 #define XT_URI_ABOUT ("about:")
660 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
661 #define XT_URI_ABOUT_ABOUT ("about")
662 #define XT_URI_ABOUT_BLANK ("blank")
663 #define XT_URI_ABOUT_CERTS ("certs")
664 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
665 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
666 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
667 #define XT_URI_ABOUT_FAVORITES ("favorites")
668 #define XT_URI_ABOUT_HELP ("help")
669 #define XT_URI_ABOUT_HISTORY ("history")
670 #define XT_URI_ABOUT_JSWL ("jswl")
671 #define XT_URI_ABOUT_PLUGINWL ("plwl")
672 #define XT_URI_ABOUT_SET ("set")
673 #define XT_URI_ABOUT_STATS ("stats")
674 #define XT_URI_ABOUT_MARCO ("marco")
675 #define XT_URI_ABOUT_STARTPAGE ("startpage")
679 int (*func
)(struct tab
*, struct karg
*);
681 { XT_URI_ABOUT_ABOUT
, about
},
682 { XT_URI_ABOUT_BLANK
, blank
},
683 { XT_URI_ABOUT_CERTS
, ca_cmd
},
684 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
685 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
686 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
687 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
688 { XT_URI_ABOUT_HELP
, help
},
689 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
690 { XT_URI_ABOUT_JSWL
, js_show_wl
},
691 { XT_URI_ABOUT_SET
, set
},
692 { XT_URI_ABOUT_STATS
, stats
},
693 { XT_URI_ABOUT_MARCO
, marco
},
694 { XT_URI_ABOUT_STARTPAGE
, startpage
},
695 { XT_URI_ABOUT_PLUGINWL
, pl_show_wl
},
698 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
699 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
700 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
701 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
702 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
703 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
704 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
707 extern char *__progname
;
710 GtkWidget
*main_window
;
711 GtkNotebook
*notebook
;
713 GtkWidget
*arrow
, *abtn
;
714 struct tab_list tabs
;
715 struct history_list hl
;
716 struct session_list sessions
;
717 struct download_list downloads
;
718 struct domain_list c_wl
;
719 struct domain_list js_wl
;
720 struct domain_list pl_wl
;
721 struct undo_tailq undos
;
722 struct keybinding_list kbl
;
724 struct command_list chl
;
725 struct command_list shl
;
726 struct command_entry
*history_at
;
727 struct command_entry
*search_at
;
729 int updating_dl_tabs
= 0;
730 int updating_hl_tabs
= 0;
731 int updating_cl_tabs
= 0;
732 int updating_fl_tabs
= 0;
733 int cmd_history_count
= 0;
734 int search_history_count
= 0;
736 long long unsigned int blocked_cookies
= 0;
737 char named_session
[PATH_MAX
];
738 GtkListStore
*completion_model
;
739 GtkListStore
*buffers_store
;
741 void xxx_dir(char *);
742 int icon_size_map(int);
743 void completion_add(struct tab
*);
744 void completion_add_uri(const gchar
*);
745 void show_oops(struct tab
*, const char *, ...);
748 history_delete(struct command_list
*l
, int *counter
)
750 struct command_entry
*c
;
752 if (l
== NULL
|| counter
== NULL
)
755 c
= TAILQ_LAST(l
, command_list
);
759 TAILQ_REMOVE(l
, c
, entry
);
766 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
768 struct command_entry
*c
;
771 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
774 /* don't add the same line */
775 c
= TAILQ_FIRST(list
);
777 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
780 c
= g_malloc0(sizeof *c
);
781 c
->line
= g_strdup_printf(" %s", l
);
784 TAILQ_INSERT_HEAD(list
, c
, entry
);
787 history_delete(list
, counter
);
789 if (history_autosave
&& file
) {
790 f
= fopen(file
, "w");
792 show_oops(NULL
, "couldn't write history %s", file
);
796 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
798 fprintf(f
, "%s\n", c
->line
);
806 history_read(struct command_list
*list
, char *file
, int *counter
)
809 char *s
, line
[65536];
811 if (list
== NULL
|| file
== NULL
)
814 f
= fopen(file
, "r");
816 startpage_add("couldn't open history file %s", file
);
821 s
= fgets(line
, sizeof line
, f
);
822 if (s
== NULL
|| feof(f
) || ferror(f
))
824 if ((s
= strchr(line
, '\n')) == NULL
) {
825 startpage_add("invalid history file %s", file
);
831 history_add(list
, NULL
, line
+ 1, counter
);
839 /* marks and quickmarks array storage.
840 * first a-z, then A-Z, then 0-9 */
847 if (i
>= 0 && i
<= 'z' - 'a')
851 if (i
>= 0 && i
<= 'Z' - 'A')
866 if (m
>= 'a' && m
<= 'z')
867 return ret
+ m
- 'a';
869 ret
+= 'z' - 'a' + 1;
870 if (m
>= 'A' && m
<= 'Z')
871 return ret
+ m
- 'A';
873 ret
+= 'Z' - 'A' + 1;
874 if (m
>= '0' && m
<= '9')
875 return ret
+ m
- '0';
884 int saved_errno
, status
;
889 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
893 if (errno
!= ECHILD
) {
895 clog_warn("sigchild: waitpid:");
901 if (WIFEXITED(status
)) {
902 if (WEXITSTATUS(status
) != 0) {
904 clog_warnx("sigchild: child exit status: %d",
905 WEXITSTATUS(status));
910 clog_warnx("sigchild: child is terminated abnormally");
919 is_g_object_setting(GObject
*o
, char *str
)
921 guint n_props
= 0, i
;
922 GParamSpec
**proplist
;
924 if (! G_IS_OBJECT(o
))
927 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
930 for (i
=0; i
< n_props
; i
++) {
931 if (! strcmp(proplist
[i
]->name
, str
))
938 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
942 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
944 "<title>%s</title>\n"
953 addstyles
? XT_PAGE_STYLE
: "",
962 * Display a web page from a HTML string in memory, rather than from a URL
965 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
970 /* we set this to indicate we want to manually do navaction */
972 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
974 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
976 /* set t->xtp_meaning */
977 for (i
= 0; i
< LENGTH(about_list
); i
++)
978 if (!strcmp(title
, about_list
[i
].name
)) {
983 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
985 #if GTK_CHECK_VERSION(2, 20, 0)
986 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
987 gtk_widget_hide(t
->spinner
);
989 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
990 xt_icon_from_file(t
, file
);
995 get_current_tab(void)
999 TAILQ_FOREACH(t
, &tabs
, entry
) {
1000 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1004 warnx("%s: no current tab", __func__
);
1010 set_status(struct tab
*t
, gchar
*s
, int status
)
1018 case XT_STATUS_LOADING
:
1019 type
= g_strdup_printf("Loading: %s", s
);
1022 case XT_STATUS_LINK
:
1023 type
= g_strdup_printf("Link: %s", s
);
1025 t
->status
= g_strdup(gtk_entry_get_text(
1026 GTK_ENTRY(t
->sbe
.statusbar
)));
1030 type
= g_strdup_printf("%s", s
);
1032 t
->status
= g_strdup(type
);
1036 t
->status
= g_strdup(s
);
1038 case XT_STATUS_NOTHING
:
1043 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1049 hide_cmd(struct tab
*t
)
1051 history_at
= NULL
; /* just in case */
1052 search_at
= NULL
; /* just in case */
1053 gtk_widget_hide(t
->cmd
);
1057 show_cmd(struct tab
*t
)
1061 gtk_widget_hide(t
->oops
);
1062 gtk_widget_show(t
->cmd
);
1066 hide_buffers(struct tab
*t
)
1068 gtk_widget_hide(t
->buffers
);
1069 gtk_list_store_clear(buffers_store
);
1079 sort_tabs_by_page_num(struct tab
***stabs
)
1084 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1086 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1088 TAILQ_FOREACH(t
, &tabs
, entry
)
1089 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1095 buffers_make_list(void)
1098 const gchar
*title
= NULL
;
1100 struct tab
**stabs
= NULL
;
1102 num_tabs
= sort_tabs_by_page_num(&stabs
);
1104 for (i
= 0; i
< num_tabs
; i
++)
1106 gtk_list_store_append(buffers_store
, &iter
);
1107 title
= get_title(stabs
[i
], FALSE
);
1108 gtk_list_store_set(buffers_store
, &iter
,
1109 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1119 show_buffers(struct tab
*t
)
1121 buffers_make_list();
1122 gtk_widget_show(t
->buffers
);
1123 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1127 toggle_buffers(struct tab
*t
)
1129 if (gtk_widget_get_visible(t
->buffers
))
1136 buffers(struct tab
*t
, struct karg
*args
)
1144 hide_oops(struct tab
*t
)
1146 gtk_widget_hide(t
->oops
);
1150 show_oops(struct tab
*at
, const char *fmt
, ...)
1154 struct tab
*t
= NULL
;
1160 if ((t
= get_current_tab()) == NULL
)
1166 if (vasprintf(&msg
, fmt
, ap
) == -1)
1167 errx(1, "show_oops failed");
1170 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1171 gtk_widget_hide(t
->cmd
);
1172 gtk_widget_show(t
->oops
);
1179 get_as_string(struct settings
*s
)
1190 warnx("get_as_string skip %s\n", s
->name
);
1191 } else if (s
->type
== XT_S_INT
)
1192 r
= g_strdup_printf("%d", *s
->ival
);
1193 else if (s
->type
== XT_S_STR
)
1194 r
= g_strdup(*s
->sval
);
1195 else if (s
->type
== XT_S_FLOAT
)
1196 r
= g_strdup_printf("%f", *s
->fval
);
1198 r
= g_strdup_printf("INVALID TYPE");
1204 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1209 for (i
= 0; i
< LENGTH(rs
); i
++) {
1210 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1211 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1213 s
= get_as_string(&rs
[i
]);
1214 cb(&rs
[i
], s
, cb_args
);
1221 set_browser_mode(struct settings
*s
, char *val
)
1223 if (!strcmp(val
, "whitelist")) {
1224 browser_mode
= XT_BM_WHITELIST
;
1225 allow_volatile_cookies
= 0;
1226 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1227 cookies_enabled
= 1;
1228 enable_cookie_whitelist
= 1;
1229 enable_plugin_whitelist
= 1;
1231 read_only_cookies
= 0;
1232 save_rejected_cookies
= 0;
1233 session_timeout
= 3600;
1235 enable_js_whitelist
= 1;
1236 enable_localstorage
= 0;
1237 } else if (!strcmp(val
, "normal")) {
1238 browser_mode
= XT_BM_NORMAL
;
1239 allow_volatile_cookies
= 0;
1240 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1241 cookies_enabled
= 1;
1242 enable_cookie_whitelist
= 0;
1243 enable_plugin_whitelist
= 0;
1245 read_only_cookies
= 0;
1246 save_rejected_cookies
= 0;
1247 session_timeout
= 3600;
1249 enable_js_whitelist
= 0;
1250 enable_localstorage
= 1;
1251 } else if (!strcmp(val
, "kiosk")) {
1252 browser_mode
= XT_BM_KIOSK
;
1253 allow_volatile_cookies
= 0;
1254 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1255 cookies_enabled
= 1;
1256 enable_cookie_whitelist
= 0;
1257 enable_plugin_whitelist
= 0;
1259 read_only_cookies
= 0;
1260 save_rejected_cookies
= 0;
1261 session_timeout
= 3600;
1263 enable_js_whitelist
= 0;
1264 enable_localstorage
= 1;
1274 get_browser_mode(struct settings
*s
)
1278 if (browser_mode
== XT_BM_WHITELIST
)
1279 r
= g_strdup("whitelist");
1280 else if (browser_mode
== XT_BM_NORMAL
)
1281 r
= g_strdup("normal");
1282 else if (browser_mode
== XT_BM_KIOSK
)
1283 r
= g_strdup("kiosk");
1291 set_cookie_policy(struct settings
*s
, char *val
)
1293 if (!strcmp(val
, "no3rdparty"))
1294 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1295 else if (!strcmp(val
, "accept"))
1296 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1297 else if (!strcmp(val
, "reject"))
1298 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1306 get_cookie_policy(struct settings
*s
)
1310 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1311 r
= g_strdup("no3rdparty");
1312 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1313 r
= g_strdup("accept");
1314 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1315 r
= g_strdup("reject");
1323 get_default_script(struct settings
*s
)
1325 if (default_script
[0] == '\0')
1327 return (g_strdup(default_script
));
1331 set_default_script(struct settings
*s
, char *val
)
1334 snprintf(default_script
, sizeof default_script
, "%s/%s",
1335 pwd
->pw_dir
, &val
[1]);
1337 strlcpy(default_script
, val
, sizeof default_script
);
1343 get_download_dir(struct settings
*s
)
1345 if (download_dir
[0] == '\0')
1347 return (g_strdup(download_dir
));
1351 set_download_dir(struct settings
*s
, char *val
)
1354 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1355 pwd
->pw_dir
, &val
[1]);
1357 strlcpy(download_dir
, val
, sizeof download_dir
);
1364 * We use these to prevent people putting xxxt:// URLs on
1365 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1367 #define XT_XTP_SES_KEY_SZ 8
1368 #define XT_XTP_SES_KEY_HEX_FMT \
1369 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1370 char *dl_session_key
; /* downloads */
1371 char *hl_session_key
; /* history list */
1372 char *cl_session_key
; /* cookie list */
1373 char *fl_session_key
; /* favorites list */
1375 char work_dir
[PATH_MAX
];
1376 char certs_dir
[PATH_MAX
];
1377 char cache_dir
[PATH_MAX
];
1378 char sessions_dir
[PATH_MAX
];
1379 char cookie_file
[PATH_MAX
];
1380 SoupURI
*proxy_uri
= NULL
;
1381 SoupSession
*session
;
1382 SoupCookieJar
*s_cookiejar
;
1383 SoupCookieJar
*p_cookiejar
;
1384 char rc_fname
[PATH_MAX
];
1386 struct mime_type_list mtl
;
1387 struct alias_list aliases
;
1390 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1391 void delete_tab(struct tab
*);
1392 void setzoom_webkit(struct tab
*, int);
1393 int run_script(struct tab
*, char *);
1394 int download_rb_cmp(struct download
*, struct download
*);
1395 gboolean
cmd_execute(struct tab
*t
, char *str
);
1398 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1400 return (strcmp(h1
->uri
, h2
->uri
));
1402 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1405 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1407 return (strcmp(d1
->d
, d2
->d
));
1409 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1412 get_work_dir(struct settings
*s
)
1414 if (work_dir
[0] == '\0')
1416 return (g_strdup(work_dir
));
1420 set_work_dir(struct settings
*s
, char *val
)
1423 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1424 pwd
->pw_dir
, &val
[1]);
1426 strlcpy(work_dir
, val
, sizeof work_dir
);
1432 get_tab_style(struct settings
*s
)
1434 if (tab_style
== XT_TABS_NORMAL
)
1435 return (g_strdup("normal"));
1437 return (g_strdup("compact"));
1441 set_tab_style(struct settings
*s
, char *val
)
1443 if (!strcmp(val
, "normal"))
1444 tab_style
= XT_TABS_NORMAL
;
1445 else if (!strcmp(val
, "compact"))
1446 tab_style
= XT_TABS_COMPACT
;
1454 * generate a session key to secure xtp commands.
1455 * pass in a ptr to the key in question and it will
1456 * be modified in place.
1459 generate_xtp_session_key(char **key
)
1461 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1467 /* make a new one */
1468 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1469 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1470 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1471 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1473 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1477 * validate a xtp session key.
1481 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1483 if (strcmp(trusted
, untrusted
) != 0) {
1484 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1493 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1495 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1497 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1499 struct valid_url_types
{
1510 valid_url_type(char *url
)
1514 for (i
= 0; i
< LENGTH(vut
); i
++)
1515 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1522 print_cookie(char *msg
, SoupCookie
*c
)
1528 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1529 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1530 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1531 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1532 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1533 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1534 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1535 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1536 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1537 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1541 walk_alias(struct settings
*s
,
1542 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1547 if (s
== NULL
|| cb
== NULL
) {
1548 show_oops(NULL
, "walk_alias invalid parameters");
1552 TAILQ_FOREACH(a
, &aliases
, entry
) {
1553 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1554 cb(s
, str
, cb_args
);
1560 match_alias(char *url_in
)
1564 char *url_out
= NULL
, *search
, *enc_arg
;
1566 search
= g_strdup(url_in
);
1568 if (strsep(&arg
, " \t") == NULL
) {
1569 show_oops(NULL
, "match_alias: NULL URL");
1573 TAILQ_FOREACH(a
, &aliases
, entry
) {
1574 if (!strcmp(search
, a
->a_name
))
1579 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1582 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1583 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1586 url_out
= g_strdup_printf(a
->a_uri
, "");
1594 guess_url_type(char *url_in
)
1597 char *url_out
= NULL
, *enc_search
= NULL
;
1600 /* substitute aliases */
1601 url_out
= match_alias(url_in
);
1602 if (url_out
!= NULL
)
1605 /* see if we are an about page */
1606 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1607 for (i
= 0; i
< LENGTH(about_list
); i
++)
1608 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1609 about_list
[i
].name
)) {
1610 url_out
= g_strdup(url_in
);
1614 if (guess_search
&& url_regex
&&
1615 !(g_str_has_prefix(url_in
, "http://") ||
1616 g_str_has_prefix(url_in
, "https://"))) {
1617 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1618 /* invalid URI so search instead */
1619 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1620 url_out
= g_strdup_printf(search_string
, enc_search
);
1626 /* XXX not sure about this heuristic */
1627 if (stat(url_in
, &sb
) == 0)
1628 url_out
= g_strdup_printf("file://%s", url_in
);
1630 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1632 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1638 load_uri(struct tab
*t
, gchar
*uri
)
1641 gchar
*newuri
= NULL
;
1647 /* Strip leading spaces. */
1648 while (*uri
&& isspace(*uri
))
1651 if (strlen(uri
) == 0) {
1656 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1658 if (valid_url_type(uri
)) {
1659 newuri
= guess_url_type(uri
);
1663 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1664 for (i
= 0; i
< LENGTH(about_list
); i
++)
1665 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1666 bzero(&args
, sizeof args
);
1667 about_list
[i
].func(t
, &args
);
1668 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1672 show_oops(t
, "invalid about page");
1676 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1678 webkit_web_view_load_uri(t
->wv
, uri
);
1685 get_uri(struct tab
*t
)
1687 const gchar
*uri
= NULL
;
1689 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1691 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1692 uri
= webkit_web_view_get_uri(t
->wv
);
1694 /* use tmp_uri to make sure it is g_freed */
1697 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1698 about_list
[t
->xtp_meaning
].name
);
1705 get_title(struct tab
*t
, bool window
)
1707 const gchar
*set
= NULL
, *title
= NULL
;
1708 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1710 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1711 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1714 title
= webkit_web_view_get_title(t
->wv
);
1715 if ((set
= title
? title
: get_uri(t
)))
1719 set
= window
? XT_NAME
: "(untitled)";
1725 add_alias(struct settings
*s
, char *line
)
1728 struct alias
*a
= NULL
;
1730 if (s
== NULL
|| line
== NULL
) {
1731 show_oops(NULL
, "add_alias invalid parameters");
1736 a
= g_malloc(sizeof(*a
));
1738 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1739 show_oops(NULL
, "add_alias: incomplete alias definition");
1742 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1743 show_oops(NULL
, "add_alias: invalid alias definition");
1747 a
->a_name
= g_strdup(alias
);
1748 a
->a_uri
= g_strdup(l
);
1750 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1752 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1762 add_mime_type(struct settings
*s
, char *line
)
1766 struct mime_type
*m
= NULL
;
1767 int downloadfirst
= 0;
1769 /* XXX this could be smarter */
1771 if (line
== NULL
|| strlen(line
) == 0) {
1772 show_oops(NULL
, "add_mime_type invalid parameters");
1781 m
= g_malloc(sizeof(*m
));
1783 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1784 show_oops(NULL
, "add_mime_type: invalid mime_type");
1787 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1788 mime_type
[strlen(mime_type
) - 1] = '\0';
1793 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1794 show_oops(NULL
, "add_mime_type: invalid mime_type");
1798 m
->mt_type
= g_strdup(mime_type
);
1799 m
->mt_action
= g_strdup(l
);
1800 m
->mt_download
= downloadfirst
;
1802 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1803 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1805 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1815 find_mime_type(char *mime_type
)
1817 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1819 TAILQ_FOREACH(m
, &mtl
, entry
) {
1820 if (m
->mt_default
&&
1821 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1824 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1837 walk_mime_type(struct settings
*s
,
1838 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1840 struct mime_type
*m
;
1843 if (s
== NULL
|| cb
== NULL
) {
1844 show_oops(NULL
, "walk_mime_type invalid parameters");
1848 TAILQ_FOREACH(m
, &mtl
, entry
) {
1849 str
= g_strdup_printf("%s%s --> %s",
1851 m
->mt_default
? "*" : "",
1853 cb(s
, str
, cb_args
);
1859 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1865 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1868 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1870 /* treat *.moo.com the same as .moo.com */
1871 if (str
[0] == '*' && str
[1] == '.')
1873 else if (str
[0] == '.')
1878 /* slice off port number */
1879 p
= g_strrstr(str
, ":");
1883 d
= g_malloc(sizeof *d
);
1885 d
->d
= g_strdup_printf(".%s", str
);
1887 d
->d
= g_strdup(str
);
1890 if (RB_INSERT(domain_list
, wl
, d
))
1893 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1904 add_cookie_wl(struct settings
*s
, char *entry
)
1906 wl_add(entry
, &c_wl
, 1);
1911 walk_cookie_wl(struct settings
*s
,
1912 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1916 if (s
== NULL
|| cb
== NULL
) {
1917 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1921 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1922 cb(s
, d
->d
, cb_args
);
1926 walk_js_wl(struct settings
*s
,
1927 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1931 if (s
== NULL
|| cb
== NULL
) {
1932 show_oops(NULL
, "walk_js_wl invalid parameters");
1936 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1937 cb(s
, d
->d
, cb_args
);
1941 add_js_wl(struct settings
*s
, char *entry
)
1943 wl_add(entry
, &js_wl
, 1 /* persistent */);
1948 walk_pl_wl(struct settings
*s
,
1949 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1953 if (s
== NULL
|| cb
== NULL
) {
1954 show_oops(NULL
, "walk_pl_wl invalid parameters");
1958 RB_FOREACH_REVERSE(d
, domain_list
, &pl_wl
)
1959 cb(s
, d
->d
, cb_args
);
1963 add_pl_wl(struct settings
*s
, char *entry
)
1965 wl_add(entry
, &pl_wl
, 1 /* persistent */);
1970 wl_find(const gchar
*search
, struct domain_list
*wl
)
1973 struct domain
*d
= NULL
, dfind
;
1976 if (search
== NULL
|| wl
== NULL
)
1978 if (strlen(search
) < 2)
1981 if (search
[0] != '.')
1982 s
= g_strdup_printf(".%s", search
);
1984 s
= g_strdup(search
);
1986 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1989 d
= RB_FIND(domain_list
, wl
, &dfind
);
2003 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2009 if (s
== NULL
|| wl
== NULL
)
2012 if (!strncmp(s
, "http://", strlen("http://")))
2013 s
= &s
[strlen("http://")];
2014 else if (!strncmp(s
, "https://", strlen("https://")))
2015 s
= &s
[strlen("https://")];
2020 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2021 /* chop string at first slash */
2022 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2025 r
= wl_find(ss
, wl
);
2034 settings_add(char *var
, char *val
)
2041 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2042 if (strcmp(var
, rs
[i
].name
))
2046 if (rs
[i
].s
->set(&rs
[i
], val
))
2047 errx(1, "invalid value for %s: %s", var
, val
);
2051 switch (rs
[i
].type
) {
2060 errx(1, "invalid sval for %s",
2074 errx(1, "invalid type for %s", var
);
2083 config_parse(char *filename
, int runtime
)
2086 char *line
, *cp
, *var
, *val
;
2087 size_t len
, lineno
= 0;
2089 char file
[PATH_MAX
];
2092 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2094 if (filename
== NULL
)
2097 if (runtime
&& runtime_settings
[0] != '\0') {
2098 snprintf(file
, sizeof file
, "%s/%s",
2099 work_dir
, runtime_settings
);
2100 if (stat(file
, &sb
)) {
2101 warnx("runtime file doesn't exist, creating it");
2102 if ((f
= fopen(file
, "w")) == NULL
)
2104 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2108 strlcpy(file
, filename
, sizeof file
);
2110 if ((config
= fopen(file
, "r")) == NULL
) {
2111 warn("config_parse: cannot open %s", filename
);
2116 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2117 if (feof(config
) || ferror(config
))
2121 cp
+= (long)strspn(cp
, WS
);
2122 if (cp
[0] == '\0') {
2128 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2129 startpage_add("invalid configuration file entry: %s",
2132 cp
+= (long)strspn(cp
, WS
);
2134 if ((val
= strsep(&cp
, "\0")) == NULL
)
2137 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",
2139 handled
= settings_add(var
, val
);
2142 startpage_add("invalid configuration file entry"
2143 ": %s=%s", var
, val
);
2153 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2159 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2163 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2166 JSStringGetUTF8CString(jsref
, s
, l
);
2167 JSStringRelease(jsref
);
2173 disable_hints(struct tab
*t
)
2175 DNPRINTF(XT_D_JS
, "%s\n", __func__
);
2177 run_script(t
, "hints.clearHints();");
2183 enable_hints(struct tab
*t
)
2185 DNPRINTF(XT_D_JS
, "%s\n", __func__
);
2187 run_script(t
, JS_HINTING
);
2190 run_script(t
, "hints.createHints('', 'F');");
2192 run_script(t
, "hints.createHints('', 'f');");
2196 #define XT_JS_DONE ("done;")
2197 #define XT_JS_DONE_LEN (strlen(XT_JS_DONE))
2198 #define XT_JS_INSERT ("insert;")
2199 #define XT_JS_INSERT_LEN (strlen(XT_JS_INSERT))
2202 run_script(struct tab
*t
, char *s
)
2204 JSGlobalContextRef ctx
;
2205 WebKitWebFrame
*frame
;
2207 JSValueRef val
, exception
;
2210 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2211 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2213 /* a bit silly but it prevents some heartburn */
2214 if (t
->script_init
== 0) {
2216 run_script(t
, JS_HINTING
);
2219 frame
= webkit_web_view_get_main_frame(t
->wv
);
2220 ctx
= webkit_web_frame_get_global_context(frame
);
2222 str
= JSStringCreateWithUTF8CString(s
);
2223 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2224 NULL
, 0, &exception
);
2225 JSStringRelease(str
);
2227 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2229 es
= js_ref_to_string(ctx
, exception
);
2231 /* show_oops(t, "script exception: %s", es); */
2232 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2237 es
= js_ref_to_string(ctx
, val
);
2240 if (!strncmp(es
, XT_JS_DONE
, XT_JS_DONE_LEN
))
2242 if (!strncmp(es
, XT_JS_INSERT
, XT_JS_INSERT_LEN
))
2246 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2255 hint(struct tab
*t
, struct karg
*args
)
2258 DNPRINTF(XT_D_JS
, "hint: tab %d args %d\n", t
->tab_id
, args
->i
);
2260 if (t
->hints_on
== 0) {
2261 if (args
->i
== XT_HINT_NEWTAB
)
2271 apply_style(struct tab
*t
)
2273 g_object_set(G_OBJECT(t
->settings
),
2274 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2278 userstyle(struct tab
*t
, struct karg
*args
)
2280 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2284 g_object_set(G_OBJECT(t
->settings
),
2285 "user-stylesheet-uri", NULL
, (char *)NULL
);
2294 * Doesn't work fully, due to the following bug:
2295 * https://bugs.webkit.org/show_bug.cgi?id=51747
2298 restore_global_history(void)
2300 char file
[PATH_MAX
];
2305 const char delim
[3] = {'\\', '\\', '\0'};
2307 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2309 if ((f
= fopen(file
, "r")) == NULL
) {
2310 warnx("%s: fopen", __func__
);
2315 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2316 if (feof(f
) || ferror(f
))
2319 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2320 if (feof(f
) || ferror(f
)) {
2322 warnx("%s: broken history file\n", __func__
);
2326 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2327 webkit_web_history_item_new_with_data(uri
, title
);
2328 h
= g_malloc(sizeof(struct history
));
2329 h
->uri
= g_strdup(uri
);
2330 h
->title
= g_strdup(title
);
2331 RB_INSERT(history_list
, &hl
, h
);
2332 completion_add_uri(h
->uri
);
2334 warnx("%s: failed to restore history\n", __func__
);
2350 save_global_history_to_disk(struct tab
*t
)
2352 char file
[PATH_MAX
];
2356 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2358 if ((f
= fopen(file
, "w")) == NULL
) {
2359 show_oops(t
, "%s: global history file: %s",
2360 __func__
, strerror(errno
));
2364 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2365 if (h
->uri
&& h
->title
)
2366 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2375 quit(struct tab
*t
, struct karg
*args
)
2377 if (save_global_history
)
2378 save_global_history_to_disk(t
);
2386 restore_sessions_list(void)
2389 struct dirent
*dp
= NULL
;
2392 sdir
= opendir(sessions_dir
);
2394 while ((dp
= readdir(sdir
)) != NULL
)
2395 if (dp
->d_type
== DT_REG
) {
2396 s
= g_malloc(sizeof(struct session
));
2397 s
->name
= g_strdup(dp
->d_name
);
2398 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2405 open_tabs(struct tab
*t
, struct karg
*a
)
2407 char file
[PATH_MAX
];
2411 struct tab
*ti
, *tt
;
2416 ti
= TAILQ_LAST(&tabs
, tab_list
);
2418 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2419 if ((f
= fopen(file
, "r")) == NULL
)
2423 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2424 if (feof(f
) || ferror(f
))
2427 /* retrieve session name */
2428 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2429 strlcpy(named_session
,
2430 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2431 sizeof named_session
);
2435 if (uri
&& strlen(uri
))
2436 create_new_tab(uri
, NULL
, 1, -1);
2442 /* close open tabs */
2443 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2445 tt
= TAILQ_FIRST(&tabs
);
2466 restore_saved_tabs(void)
2468 char file
[PATH_MAX
];
2469 int unlink_file
= 0;
2474 snprintf(file
, sizeof file
, "%s/%s",
2475 sessions_dir
, XT_RESTART_TABS_FILE
);
2476 if (stat(file
, &sb
) == -1)
2477 a
.s
= XT_SAVED_TABS_FILE
;
2480 a
.s
= XT_RESTART_TABS_FILE
;
2483 a
.i
= XT_SES_DONOTHING
;
2484 rv
= open_tabs(NULL
, &a
);
2493 save_tabs(struct tab
*t
, struct karg
*a
)
2495 char file
[PATH_MAX
];
2497 int num_tabs
= 0, i
;
2498 struct tab
**stabs
= NULL
;
2500 /* tab may be null here */
2505 snprintf(file
, sizeof file
, "%s/%s",
2506 sessions_dir
, named_session
);
2508 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2510 if ((f
= fopen(file
, "w")) == NULL
) {
2511 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2515 /* save session name */
2516 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2518 /* Save tabs, in the order they are arranged in the notebook. */
2519 num_tabs
= sort_tabs_by_page_num(&stabs
);
2521 for (i
= 0; i
< num_tabs
; i
++)
2523 if (get_uri(stabs
[i
]) != NULL
)
2524 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2525 else if (gtk_entry_get_text(GTK_ENTRY(
2526 stabs
[i
]->uri_entry
)))
2527 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2528 stabs
[i
]->uri_entry
)));
2533 /* try and make sure this gets to disk NOW. XXX Backup first? */
2534 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2535 show_oops(t
, "May not have managed to save session: %s",
2545 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2557 run_page_script(struct tab
*t
, struct karg
*args
)
2560 char *tmp
, script
[PATH_MAX
];
2562 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2563 if (tmp
[0] == '\0') {
2564 show_oops(t
, "no script specified");
2568 if ((uri
= get_uri(t
)) == NULL
) {
2569 show_oops(t
, "tab is empty, not running script");
2574 snprintf(script
, sizeof script
, "%s/%s",
2575 pwd
->pw_dir
, &tmp
[1]);
2577 strlcpy(script
, tmp
, sizeof script
);
2581 show_oops(t
, "can't fork to run script");
2591 execlp(script
, script
, uri
, (void *)NULL
);
2601 yank_uri(struct tab
*t
, struct karg
*args
)
2604 GtkClipboard
*clipboard
;
2606 if ((uri
= get_uri(t
)) == NULL
)
2609 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2610 gtk_clipboard_set_text(clipboard
, uri
, -1);
2616 paste_uri(struct tab
*t
, struct karg
*args
)
2618 GtkClipboard
*clipboard
;
2619 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2621 gchar
*p
= NULL
, *uri
;
2623 /* try primary clipboard first */
2624 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2625 p
= gtk_clipboard_wait_for_text(clipboard
);
2627 /* if it failed get whatever text is in cut_buffer0 */
2628 if (p
== NULL
&& xterm_workaround
)
2629 if (gdk_property_get(gdk_get_default_root_window(),
2631 gdk_atom_intern("STRING", FALSE
),
2633 1024 * 1024 /* picked out of my butt */,
2639 /* yes sir, we need to NUL the string */
2645 while (*uri
&& isspace(*uri
))
2647 if (strlen(uri
) == 0) {
2648 show_oops(t
, "empty paste buffer");
2651 if (guess_search
== 0 && valid_url_type(uri
)) {
2652 /* we can be clever and paste this in search box */
2653 show_oops(t
, "not a valid URL");
2657 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2659 else if (args
->i
== XT_PASTE_NEW_TAB
)
2660 create_new_tab(uri
, NULL
, 1, -1);
2671 find_domain(const gchar
*s
, int toplevel
)
2679 uri
= soup_uri_new(s
);
2681 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2685 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2686 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2687 while(--p
>= uri
->host
&& *p
!= '.');
2694 ret
= g_strdup_printf(".%s", p
);
2702 toggle_cwl(struct tab
*t
, struct karg
*args
)
2713 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2715 if (uri
== NULL
|| dom
== NULL
||
2716 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2717 show_oops(t
, "Can't toggle domain in cookie white list");
2720 d
= wl_find(dom
, &c_wl
);
2727 if (args
->i
& XT_WL_TOGGLE
)
2729 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2731 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2735 /* enable cookies for domain */
2736 wl_add(dom
, &c_wl
, 0);
2738 /* disable cookies for domain */
2740 RB_REMOVE(domain_list
, &c_wl
, d
);
2743 if (args
->i
& XT_WL_RELOAD
)
2744 webkit_web_view_reload(t
->wv
);
2752 toggle_js(struct tab
*t
, struct karg
*args
)
2762 g_object_get(G_OBJECT(t
->settings
),
2763 "enable-scripts", &es
, (char *)NULL
);
2764 if (args
->i
& XT_WL_TOGGLE
)
2766 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2768 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2774 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2776 if (uri
== NULL
|| dom
== NULL
||
2777 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2778 show_oops(t
, "Can't toggle domain in JavaScript white list");
2783 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2784 wl_add(dom
, &js_wl
, 0 /* session */);
2786 d
= wl_find(dom
, &js_wl
);
2788 RB_REMOVE(domain_list
, &js_wl
, d
);
2789 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2791 g_object_set(G_OBJECT(t
->settings
),
2792 "enable-scripts", es
, (char *)NULL
);
2793 g_object_set(G_OBJECT(t
->settings
),
2794 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2795 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2797 if (args
->i
& XT_WL_RELOAD
)
2798 webkit_web_view_reload(t
->wv
);
2806 toggle_pl(struct tab
*t
, struct karg
*args
)
2816 g_object_get(G_OBJECT(t
->settings
),
2817 "enable-plugins", &es
, (char *)NULL
);
2818 if (args
->i
& XT_WL_TOGGLE
)
2820 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2822 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2828 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2830 if (uri
== NULL
|| dom
== NULL
||
2831 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2832 show_oops(t
, "Can't toggle domain in plugins white list");
2837 wl_add(dom
, &pl_wl
, 0 /* session */);
2839 d
= wl_find(dom
, &pl_wl
);
2841 RB_REMOVE(domain_list
, &pl_wl
, d
);
2843 g_object_set(G_OBJECT(t
->settings
),
2844 "enable-plugins", es
, (char *)NULL
);
2845 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2847 if (args
->i
& XT_WL_RELOAD
)
2848 webkit_web_view_reload(t
->wv
);
2856 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2861 g_object_get(G_OBJECT(t
->settings
),
2862 "enable-scripts", &es
, (char *)NULL
);
2867 set
= XT_WL_DISABLE
;
2869 a
.i
= set
| XT_WL_TOPLEVEL
;
2872 a
.i
= set
| XT_WL_TOPLEVEL
;
2875 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2880 toggle_src(struct tab
*t
, struct karg
*args
)
2887 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2888 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2889 webkit_web_view_reload(t
->wv
);
2895 focus_webview(struct tab
*t
)
2900 /* only grab focus if we are visible */
2901 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2902 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2906 focus(struct tab
*t
, struct karg
*args
)
2908 if (t
== NULL
|| args
== NULL
)
2914 if (args
->i
== XT_FOCUS_URI
)
2915 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2916 else if (args
->i
== XT_FOCUS_SEARCH
)
2917 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2923 stats(struct tab
*t
, struct karg
*args
)
2925 char *page
, *body
, *s
, line
[64 * 1024];
2926 long long unsigned int line_count
= 0;
2930 show_oops(NULL
, "stats invalid parameters");
2933 if (save_rejected_cookies
) {
2934 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2936 s
= fgets(line
, sizeof line
, r_cookie_f
);
2937 if (s
== NULL
|| feof(r_cookie_f
) ||
2943 snprintf(line
, sizeof line
,
2944 "<br/>Cookies blocked(*) total: %llu", line_count
);
2946 show_oops(t
, "Can't open blocked cookies file: %s",
2950 body
= g_strdup_printf(
2951 "Cookies blocked(*) this session: %llu"
2953 "<p><small><b>*</b> results vary based on settings</small></p>",
2957 page
= get_html_page("Statistics", body
, "", 0);
2960 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2967 marco(struct tab
*t
, struct karg
*args
)
2969 char *page
, line
[64 * 1024];
2973 show_oops(NULL
, "marco invalid parameters");
2976 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2978 page
= get_html_page("Marco Sez...", line
, "", 0);
2980 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2987 blank(struct tab
*t
, struct karg
*args
)
2990 show_oops(NULL
, "blank invalid parameters");
2992 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2998 about(struct tab
*t
, struct karg
*args
)
3003 show_oops(NULL
, "about invalid parameters");
3005 body
= g_strdup_printf("<b>Version: %s</b>"
3006 #ifdef XXXTERM_BUILDSTR
3007 "<br><b>Build: %s</b>"
3012 "<li>Marco Peereboom <marco@peereboom.us></li>"
3013 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3014 "<li>Edd Barrett <vext01@gmail.com> </li>"
3015 "<li>Todd T. Fries <todd@fries.net> </li>"
3016 "<li>Raphael Graf <r@undefined.ch> </li>"
3018 "Copyrights and licenses can be found on the XXXTerm "
3019 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3021 #ifdef XXXTERM_BUILDSTR
3022 version
, XXXTERM_BUILDSTR
3028 page
= get_html_page("About", body
, "", 0);
3031 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3038 help(struct tab
*t
, struct karg
*args
)
3040 char *page
, *head
, *body
;
3043 show_oops(NULL
, "help invalid parameters");
3045 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3046 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3048 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3049 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3050 "cgi-bin/man-cgi?xxxterm</a>";
3052 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3054 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3061 startpage(struct tab
*t
, struct karg
*args
)
3063 char *page
, *body
, *b
;
3067 show_oops(NULL
, "startpage invalid parameters");
3069 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3071 TAILQ_FOREACH(s
, &spl
, entry
) {
3073 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3077 page
= get_html_page("Startup Exception", body
, "", 0);
3080 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3087 startpage_add(const char *fmt
, ...)
3097 if (vasprintf(&msg
, fmt
, ap
) == -1)
3098 errx(1, "startpage_add failed");
3101 s
= g_malloc0(sizeof *s
);
3104 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3108 * update all favorite tabs apart from one. Pass NULL if
3109 * you want to update all.
3112 update_favorite_tabs(struct tab
*apart_from
)
3115 if (!updating_fl_tabs
) {
3116 updating_fl_tabs
= 1; /* stop infinite recursion */
3117 TAILQ_FOREACH(t
, &tabs
, entry
)
3118 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3119 && (t
!= apart_from
))
3120 xtp_page_fl(t
, NULL
);
3121 updating_fl_tabs
= 0;
3125 /* show a list of favorites (bookmarks) */
3127 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3129 char file
[PATH_MAX
];
3131 char *uri
= NULL
, *title
= NULL
;
3132 size_t len
, lineno
= 0;
3134 char *body
, *tmp
, *page
= NULL
;
3135 const char delim
[3] = {'\\', '\\', '\0'};
3137 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3140 warn("%s: bad param", __func__
);
3142 /* new session key */
3143 if (!updating_fl_tabs
)
3144 generate_xtp_session_key(&fl_session_key
);
3146 /* open favorites */
3147 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3148 if ((f
= fopen(file
, "r")) == NULL
) {
3149 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3154 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3155 "<th style='width: 40px'>#</th><th>Link</th>"
3156 "<th style='width: 40px'>Rm</th></tr>\n");
3159 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3161 if (strlen(title
) == 0 || title
[0] == '#') {
3167 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3168 if (feof(f
) || ferror(f
)) {
3169 show_oops(t
, "favorites file corrupt");
3175 body
= g_strdup_printf("%s<tr>"
3177 "<td><a href='%s'>%s</a></td>"
3178 "<td style='text-align: center'>"
3179 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3181 body
, i
, uri
, title
,
3182 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3194 /* if none, say so */
3197 body
= g_strdup_printf("%s<tr>"
3198 "<td colspan='3' style='text-align: center'>"
3199 "No favorites - To add one use the 'favadd' command."
3200 "</td></tr>", body
);
3205 body
= g_strdup_printf("%s</table>", body
);
3215 page
= get_html_page("Favorites", body
, "", 1);
3216 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3220 update_favorite_tabs(t
);
3229 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3230 size_t cert_count
, char *title
)
3232 gnutls_datum_t cinfo
;
3236 body
= g_strdup("");
3238 for (i
= 0; i
< cert_count
; i
++) {
3239 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3244 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3245 body
, i
, cinfo
.data
);
3246 gnutls_free(cinfo
.data
);
3250 tmp
= get_html_page(title
, body
, "", 0);
3253 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3258 ca_cmd(struct tab
*t
, struct karg
*args
)
3261 int rv
= 1, certs
= 0, certs_read
;
3264 gnutls_x509_crt_t
*c
= NULL
;
3265 char *certs_buf
= NULL
, *s
;
3267 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3268 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3272 if (fstat(fileno(f
), &sb
) == -1) {
3273 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3277 certs_buf
= g_malloc(sb
.st_size
+ 1);
3278 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3279 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3282 certs_buf
[sb
.st_size
] = '\0';
3285 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3287 s
+= strlen("BEGIN CERTIFICATE");
3290 bzero(&dt
, sizeof dt
);
3291 dt
.data
= (unsigned char *)certs_buf
;
3292 dt
.size
= sb
.st_size
;
3293 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3294 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3295 GNUTLS_X509_FMT_PEM
, 0);
3296 if (certs_read
<= 0) {
3297 show_oops(t
, "No cert(s) available");
3300 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3313 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
3317 struct addrinfo hints
, *res
= NULL
, *ai
;
3318 int rv
= -1, s
= -1, on
, error
;
3320 static gchar myerror
[256]; /* this is not thread safe */
3323 *error_str
= myerror
;
3324 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3325 *error_str
= "invalid URI";
3329 su
= soup_uri_new(uri
);
3331 *error_str
= "invalid soup URI";
3334 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3335 *error_str
= "invalid HTTPS URI";
3339 snprintf(port
, sizeof port
, "%d", su
->port
);
3340 bzero(&hints
, sizeof(struct addrinfo
));
3341 hints
.ai_flags
= AI_CANONNAME
;
3342 hints
.ai_family
= AF_UNSPEC
;
3343 hints
.ai_socktype
= SOCK_STREAM
;
3345 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3346 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
3347 gai_strerror(errno
));
3351 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3357 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3359 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3362 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3365 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3369 snprintf(myerror
, sizeof myerror
,
3370 "could not obtain certificates from: %s",
3376 strlcpy(domain
, su
->host
, domain_sz
);
3383 if (rv
== -1 && s
!= -1)
3390 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3393 gnutls_deinit(gsession
);
3395 gnutls_certificate_free_credentials(xcred
);
3401 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
3402 gnutls_certificate_credentials_t
*xc
)
3404 gnutls_certificate_credentials_t xcred
;
3405 gnutls_session_t gsession
;
3407 static gchar myerror
[1024]; /* this is not thread safe */
3409 if (gs
== NULL
|| xc
== NULL
)
3416 gnutls_certificate_allocate_credentials(&xcred
);
3417 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3418 GNUTLS_X509_FMT_PEM
);
3420 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3421 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3422 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3423 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3424 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3425 snprintf(myerror
, sizeof myerror
,
3426 "gnutls_handshake failed %d fatal %d %s",
3428 gnutls_error_is_fatal(rv
),
3429 gnutls_strerror_name(rv
));
3430 stop_tls(gsession
, xcred
);
3434 gnutls_credentials_type_t cred
;
3435 cred
= gnutls_auth_get_type(gsession
);
3436 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3437 snprintf(myerror
, sizeof myerror
,
3438 "gnutls_auth_get_type failed %d",
3440 stop_tls(gsession
, xcred
);
3448 *error_str
= myerror
;
3453 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3457 const gnutls_datum_t
*cl
;
3458 gnutls_x509_crt_t
*all_certs
;
3461 if (certs
== NULL
|| cert_count
== NULL
)
3463 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3465 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3469 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3470 for (i
= 0; i
< len
; i
++) {
3471 gnutls_x509_crt_init(&all_certs
[i
]);
3472 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3473 GNUTLS_X509_FMT_PEM
< 0)) {
3487 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3491 for (i
= 0; i
< cert_count
; i
++)
3492 gnutls_x509_crt_deinit(certs
[i
]);
3497 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3499 GdkColor c_text
, c_base
;
3501 gdk_color_parse(text
, &c_text
);
3502 gdk_color_parse(base
, &c_base
);
3504 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3505 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3506 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3507 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3509 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3510 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3511 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3512 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3516 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3517 size_t cert_count
, char *domain
)
3520 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3525 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3528 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3529 if ((f
= fopen(file
, "w")) == NULL
) {
3530 show_oops(t
, "Can't create cert file %s %s",
3531 file
, strerror(errno
));
3535 for (i
= 0; i
< cert_count
; i
++) {
3536 cert_buf_sz
= sizeof cert_buf
;
3537 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3538 cert_buf
, &cert_buf_sz
)) {
3539 show_oops(t
, "gnutls_x509_crt_export failed");
3542 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3543 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3548 /* not the best spot but oh well */
3549 gdk_color_parse("lightblue", &color
);
3550 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3551 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3564 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
3566 char domain
[8182], file
[PATH_MAX
];
3567 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3569 unsigned int error
= 0;
3571 size_t cert_buf_sz
, cert_count
;
3572 enum cert_trust rv
= CERT_UNTRUSTED
;
3573 static gchar serr
[80]; /* this isn't thread safe */
3574 gnutls_session_t gsession
;
3575 gnutls_x509_crt_t
*certs
;
3576 gnutls_certificate_credentials_t xcred
;
3578 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3582 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
3583 sizeof domain
)) == -1)
3586 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3589 if (start_tls(error_str
, s
, &gsession
, &xcred
))
3591 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3593 /* verify certs in case cert file doesn't exist */
3594 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3596 *error_str
= "Invalid certificates";
3601 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3602 *error_str
= "Can't get connection certificates";
3606 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3607 if ((f
= fopen(file
, "r")) == NULL
) {
3613 for (i
= 0; i
< cert_count
; i
++) {
3614 cert_buf_sz
= sizeof cert_buf
;
3615 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3616 cert_buf
, &cert_buf_sz
)) {
3619 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3620 rv
= CERT_BAD
; /* critical */
3623 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3624 rv
= CERT_BAD
; /* critical */
3633 free_connection_certs(certs
, cert_count
);
3635 /* we close the socket first for speed */
3639 /* only complain if we didn't save it locally */
3640 if (error
&& rv
!= CERT_LOCAL
) {
3641 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3642 if (error
& GNUTLS_CERT_INVALID
)
3643 strlcat(serr
, "invalid, ", sizeof serr
);
3644 if (error
& GNUTLS_CERT_REVOKED
)
3645 strlcat(serr
, "revoked, ", sizeof serr
);
3646 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3647 strlcat(serr
, "signer not found, ", sizeof serr
);
3648 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3649 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3650 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3651 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3652 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3653 strlcat(serr
, "not activated, ", sizeof serr
);
3654 if (error
& GNUTLS_CERT_EXPIRED
)
3655 strlcat(serr
, "expired, ", sizeof serr
);
3656 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3657 if (serr
[i
] == ',') {
3664 stop_tls(gsession
, xcred
);
3670 cert_cmd(struct tab
*t
, struct karg
*args
)
3672 const gchar
*uri
, *error_str
= NULL
;
3676 gnutls_session_t gsession
;
3677 gnutls_x509_crt_t
*certs
;
3678 gnutls_certificate_credentials_t xcred
;
3683 if (ssl_ca_file
== NULL
) {
3684 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3688 if ((uri
= get_uri(t
)) == NULL
) {
3689 show_oops(t
, "Invalid URI");
3693 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
3694 sizeof domain
)) == -1) {
3695 show_oops(t
, "%s", error_str
);
3700 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
3704 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3705 show_oops(t
, "get_connection_certs failed");
3709 if (args
->i
& XT_SHOW
)
3710 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3711 else if (args
->i
& XT_SAVE
)
3712 save_certs(t
, certs
, cert_count
, domain
);
3714 free_connection_certs(certs
, cert_count
);
3716 /* we close the socket first for speed */
3719 stop_tls(gsession
, xcred
);
3720 if (error_str
&& strlen(error_str
))
3721 show_oops(t
, "%s", error_str
);
3726 remove_cookie(int index
)
3732 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3734 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3736 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3740 print_cookie("remove cookie", c
);
3741 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3746 soup_cookies_free(cf
);
3752 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3757 body
= g_strdup("");
3760 if (args
->i
& XT_WL_PERSISTENT
) {
3762 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3764 RB_FOREACH(d
, domain_list
, wl
) {
3768 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3774 if (args
->i
& XT_WL_SESSION
) {
3776 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3778 RB_FOREACH(d
, domain_list
, wl
) {
3782 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3787 tmp
= get_html_page(title
, body
, "", 0);
3790 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3791 else if (wl
== &c_wl
)
3792 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3794 load_webkit_string(t
, tmp
, XT_URI_ABOUT_PLUGINWL
);
3799 #define XT_WL_INVALID (0)
3800 #define XT_WL_JAVASCRIPT (1)
3801 #define XT_WL_COOKIE (2)
3802 #define XT_WL_PLUGIN (3)
3804 wl_save(struct tab
*t
, struct karg
*args
, int list
)
3806 char file
[PATH_MAX
], *lst_str
= NULL
;
3808 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3816 if (t
== NULL
|| args
== NULL
)
3819 if (runtime_settings
[0] == '\0')
3822 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3823 if ((f
= fopen(file
, "r+")) == NULL
)
3827 case XT_WL_JAVASCRIPT
:
3828 lst_str
= "JavaScript";
3829 lt
= g_strdup_printf("js_wl=%s", dom
);
3833 lt
= g_strdup_printf("cookie_wl=%s", dom
);
3837 lt
= g_strdup_printf("pl_wl=%s", dom
);
3840 show_oops(t
, "Invalid list id: %d", list
);
3845 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3846 if (uri
== NULL
|| dom
== NULL
||
3847 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3848 show_oops(t
, "Can't add domain to %s white list", lst_str
);
3853 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3856 if (!strcmp(line
, lt
))
3862 fprintf(f
, "%s\n", lt
);
3867 case XT_WL_JAVASCRIPT
:
3868 d
= wl_find(dom
, &js_wl
);
3870 settings_add("js_wl", dom
);
3871 d
= wl_find(dom
, &js_wl
);
3877 d
= wl_find(dom
, &c_wl
);
3879 settings_add("cookie_wl", dom
);
3880 d
= wl_find(dom
, &c_wl
);
3884 /* find and add to persistent jar */
3885 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3886 for (;cf
; cf
= cf
->next
) {
3888 if (!strcmp(dom
, ci
->domain
) ||
3889 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3890 c
= soup_cookie_copy(ci
);
3891 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3894 soup_cookies_free(cf
);
3898 d
= wl_find(dom
, &pl_wl
);
3900 settings_add("pl_wl", dom
);
3901 d
= wl_find(dom
, &pl_wl
);
3906 abort(); /* can't happen */
3924 js_show_wl(struct tab
*t
, struct karg
*args
)
3926 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3927 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3933 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3935 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3936 wl_show(t
, args
, "Cookie White List", &c_wl
);
3942 cookie_cmd(struct tab
*t
, struct karg
*args
)
3944 if (args
->i
& XT_SHOW
)
3945 wl_show(t
, args
, "Cookie White List", &c_wl
);
3946 else if (args
->i
& XT_WL_TOGGLE
) {
3947 args
->i
|= XT_WL_RELOAD
;
3948 toggle_cwl(t
, args
);
3949 } else if (args
->i
& XT_SAVE
) {
3950 args
->i
|= XT_WL_RELOAD
;
3951 wl_save(t
, args
, XT_WL_COOKIE
);
3952 } else if (args
->i
& XT_DELETE
)
3953 show_oops(t
, "'cookie delete' currently unimplemented");
3959 js_cmd(struct tab
*t
, struct karg
*args
)
3961 if (args
->i
& XT_SHOW
)
3962 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3963 else if (args
->i
& XT_SAVE
) {
3964 args
->i
|= XT_WL_RELOAD
;
3965 wl_save(t
, args
, XT_WL_JAVASCRIPT
);
3966 } else if (args
->i
& XT_WL_TOGGLE
) {
3967 args
->i
|= XT_WL_RELOAD
;
3969 } else if (args
->i
& XT_DELETE
)
3970 show_oops(t
, "'js delete' currently unimplemented");
3976 pl_show_wl(struct tab
*t
, struct karg
*args
)
3978 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3979 wl_show(t
, args
, "Plugin White List", &pl_wl
);
3985 pl_cmd(struct tab
*t
, struct karg
*args
)
3987 if (args
->i
& XT_SHOW
)
3988 wl_show(t
, args
, "Plugin White List", &pl_wl
);
3989 else if (args
->i
& XT_SAVE
) {
3990 args
->i
|= XT_WL_RELOAD
;
3991 wl_save(t
, args
, XT_WL_PLUGIN
);
3992 } else if (args
->i
& XT_WL_TOGGLE
) {
3993 args
->i
|= XT_WL_RELOAD
;
3995 } else if (args
->i
& XT_DELETE
)
3996 show_oops(t
, "'plugin delete' currently unimplemented");
4002 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4004 js_toggle_cb(t
->js_toggle
, t
);
4010 add_favorite(struct tab
*t
, struct karg
*args
)
4012 char file
[PATH_MAX
];
4015 size_t urilen
, linelen
;
4016 const gchar
*uri
, *title
;
4021 /* don't allow adding of xtp pages to favorites */
4022 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4023 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4027 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4028 if ((f
= fopen(file
, "r+")) == NULL
) {
4029 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4033 title
= get_title(t
, FALSE
);
4036 if (title
== NULL
|| uri
== NULL
) {
4037 show_oops(t
, "can't add page to favorites");
4041 urilen
= strlen(uri
);
4044 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4045 if (feof(f
) || ferror(f
))
4048 if (linelen
== urilen
&& !strcmp(line
, uri
))
4055 fprintf(f
, "\n%s\n%s", title
, uri
);
4061 update_favorite_tabs(NULL
);
4067 can_go_back_for_real(struct tab
*t
)
4070 WebKitWebHistoryItem
*item
;
4076 /* rely on webkit to make sure we can go backward when on an about page */
4078 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4079 return (webkit_web_view_can_go_back(t
->wv
));
4081 /* the back/forwars list is stupid so help determine if we can go back */
4082 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4084 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4085 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4093 can_go_forward_for_real(struct tab
*t
)
4096 WebKitWebHistoryItem
*item
;
4102 /* rely on webkit to make sure we can go forward when on an about page */
4104 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4105 return (webkit_web_view_can_go_forward(t
->wv
));
4107 /* the back/forwars list is stupid so help selecting a different item */
4108 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4110 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4111 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4119 go_back_for_real(struct tab
*t
)
4122 WebKitWebHistoryItem
*item
;
4130 webkit_web_view_go_back(t
->wv
);
4133 /* the back/forwars list is stupid so help selecting a different item */
4134 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4136 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4137 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4138 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4145 go_forward_for_real(struct tab
*t
)
4148 WebKitWebHistoryItem
*item
;
4156 webkit_web_view_go_forward(t
->wv
);
4159 /* the back/forwars list is stupid so help selecting a different item */
4160 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4162 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4163 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4164 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4171 navaction(struct tab
*t
, struct karg
*args
)
4173 WebKitWebHistoryItem
*item
;
4174 WebKitWebFrame
*frame
;
4176 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4177 t
->tab_id
, args
->i
);
4179 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4181 if (args
->i
== XT_NAV_BACK
)
4182 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4184 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4186 return (XT_CB_PASSTHROUGH
);
4187 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4189 return (XT_CB_PASSTHROUGH
);
4195 go_back_for_real(t
);
4197 case XT_NAV_FORWARD
:
4199 go_forward_for_real(t
);
4202 frame
= webkit_web_view_get_main_frame(t
->wv
);
4203 webkit_web_frame_reload(frame
);
4206 return (XT_CB_PASSTHROUGH
);
4210 move(struct tab
*t
, struct karg
*args
)
4212 GtkAdjustment
*adjust
;
4213 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4219 case XT_MOVE_BOTTOM
:
4221 case XT_MOVE_PAGEDOWN
:
4222 case XT_MOVE_PAGEUP
:
4223 case XT_MOVE_HALFDOWN
:
4224 case XT_MOVE_HALFUP
:
4225 case XT_MOVE_PERCENT
:
4226 adjust
= t
->adjust_v
;
4229 adjust
= t
->adjust_h
;
4233 pos
= gtk_adjustment_get_value(adjust
);
4234 ps
= gtk_adjustment_get_page_size(adjust
);
4235 upper
= gtk_adjustment_get_upper(adjust
);
4236 lower
= gtk_adjustment_get_lower(adjust
);
4237 si
= gtk_adjustment_get_step_increment(adjust
);
4238 pi
= gtk_adjustment_get_page_increment(adjust
);
4241 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4242 "max %f si %f pi %f\n",
4243 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4244 pos
, ps
, upper
, lower
, max
, si
, pi
);
4250 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4255 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4257 case XT_MOVE_BOTTOM
:
4258 case XT_MOVE_FARRIGHT
:
4259 gtk_adjustment_set_value(adjust
, max
);
4262 case XT_MOVE_FARLEFT
:
4263 gtk_adjustment_set_value(adjust
, lower
);
4265 case XT_MOVE_PAGEDOWN
:
4267 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4269 case XT_MOVE_PAGEUP
:
4271 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4273 case XT_MOVE_HALFDOWN
:
4275 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4277 case XT_MOVE_HALFUP
:
4279 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4281 case XT_MOVE_PERCENT
:
4282 percent
= atoi(args
->s
) / 100.0;
4283 pos
= max
* percent
;
4284 if (pos
< 0.0 || pos
> max
)
4286 gtk_adjustment_set_value(adjust
, pos
);
4289 return (XT_CB_PASSTHROUGH
);
4292 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4294 return (XT_CB_HANDLED
);
4298 url_set_visibility(void)
4302 TAILQ_FOREACH(t
, &tabs
, entry
)
4303 if (show_url
== 0) {
4304 gtk_widget_hide(t
->toolbar
);
4307 gtk_widget_show(t
->toolbar
);
4311 notebook_tab_set_visibility(void)
4313 if (show_tabs
== 0) {
4314 gtk_widget_hide(tab_bar
);
4315 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4317 if (tab_style
== XT_TABS_NORMAL
) {
4318 gtk_widget_hide(tab_bar
);
4319 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4320 } else if (tab_style
== XT_TABS_COMPACT
) {
4321 gtk_widget_show(tab_bar
);
4322 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4328 statusbar_set_visibility(void)
4332 TAILQ_FOREACH(t
, &tabs
, entry
)
4333 if (show_statusbar
== 0) {
4334 gtk_widget_hide(t
->statusbar_box
);
4337 gtk_widget_show(t
->statusbar_box
);
4341 url_set(struct tab
*t
, int enable_url_entry
)
4346 show_url
= enable_url_entry
;
4348 if (enable_url_entry
) {
4349 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4350 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4351 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4353 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4354 GTK_ENTRY_ICON_PRIMARY
);
4356 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4357 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4358 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4359 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4365 fullscreen(struct tab
*t
, struct karg
*args
)
4367 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4370 return (XT_CB_PASSTHROUGH
);
4372 if (show_url
== 0) {
4380 url_set_visibility();
4381 notebook_tab_set_visibility();
4383 return (XT_CB_HANDLED
);
4387 statustoggle(struct tab
*t
, struct karg
*args
)
4389 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4391 if (show_statusbar
== 1) {
4393 statusbar_set_visibility();
4394 } else if (show_statusbar
== 0) {
4396 statusbar_set_visibility();
4398 return (XT_CB_HANDLED
);
4402 urlaction(struct tab
*t
, struct karg
*args
)
4404 int rv
= XT_CB_HANDLED
;
4406 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4409 return (XT_CB_PASSTHROUGH
);
4413 if (show_url
== 0) {
4415 url_set_visibility();
4419 if (show_url
== 1) {
4421 url_set_visibility();
4429 tabaction(struct tab
*t
, struct karg
*args
)
4431 int rv
= XT_CB_HANDLED
;
4432 char *url
= args
->s
;
4436 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4439 return (XT_CB_PASSTHROUGH
);
4443 if (strlen(url
) > 0)
4444 create_new_tab(url
, NULL
, 1, args
->precount
);
4446 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4449 if (args
->precount
< 0)
4452 TAILQ_FOREACH(tt
, &tabs
, entry
)
4453 if (tt
->tab_id
== args
->precount
- 1) {
4458 case XT_TAB_DELQUIT
:
4459 if (gtk_notebook_get_n_pages(notebook
) > 1)
4465 if (strlen(url
) > 0)
4468 rv
= XT_CB_PASSTHROUGH
;
4474 if (show_tabs
== 0) {
4476 notebook_tab_set_visibility();
4480 if (show_tabs
== 1) {
4482 notebook_tab_set_visibility();
4485 case XT_TAB_NEXTSTYLE
:
4486 if (tab_style
== XT_TABS_NORMAL
) {
4487 tab_style
= XT_TABS_COMPACT
;
4488 recolor_compact_tabs();
4491 tab_style
= XT_TABS_NORMAL
;
4492 notebook_tab_set_visibility();
4494 case XT_TAB_UNDO_CLOSE
:
4495 if (undo_count
== 0) {
4496 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4501 u
= TAILQ_FIRST(&undos
);
4502 create_new_tab(u
->uri
, u
, 1, -1);
4504 TAILQ_REMOVE(&undos
, u
, entry
);
4506 /* u->history is freed in create_new_tab() */
4511 rv
= XT_CB_PASSTHROUGH
;
4525 resizetab(struct tab
*t
, struct karg
*args
)
4527 if (t
== NULL
|| args
== NULL
) {
4528 show_oops(NULL
, "resizetab invalid parameters");
4529 return (XT_CB_PASSTHROUGH
);
4532 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4533 t
->tab_id
, args
->i
);
4535 setzoom_webkit(t
, args
->i
);
4537 return (XT_CB_HANDLED
);
4541 movetab(struct tab
*t
, struct karg
*args
)
4545 if (t
== NULL
|| args
== NULL
) {
4546 show_oops(NULL
, "movetab invalid parameters");
4547 return (XT_CB_PASSTHROUGH
);
4550 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4551 t
->tab_id
, args
->i
);
4553 if (args
->i
>= XT_TAB_INVALID
)
4554 return (XT_CB_PASSTHROUGH
);
4556 if (TAILQ_EMPTY(&tabs
))
4557 return (XT_CB_PASSTHROUGH
);
4559 n
= gtk_notebook_get_n_pages(notebook
);
4560 dest
= gtk_notebook_get_current_page(notebook
);
4564 if (args
->precount
< 0)
4565 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4567 dest
= args
->precount
- 1;
4571 if (args
->precount
< 0)
4574 dest
-= args
->precount
% n
;
4587 return (XT_CB_PASSTHROUGH
);
4590 if (dest
< 0 || dest
>= n
)
4591 return (XT_CB_PASSTHROUGH
);
4592 if (t
->tab_id
== dest
) {
4593 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4594 return (XT_CB_HANDLED
);
4597 set_current_tab(dest
);
4599 return (XT_CB_HANDLED
);
4605 command_mode(struct tab
*t
, struct karg
*args
)
4607 if (args
->i
== XT_MODE_COMMAND
)
4608 run_script(t
, "hints.clearFocus();");
4610 run_script(t
, "hints.focusInput();");
4611 return (XT_CB_HANDLED
);
4615 command(struct tab
*t
, struct karg
*args
)
4617 char *s
= NULL
, *ss
= NULL
;
4622 if (t
== NULL
|| args
== NULL
) {
4623 show_oops(NULL
, "command invalid parameters");
4624 return (XT_CB_PASSTHROUGH
);
4635 if (cmd_prefix
== 0)
4638 ss
= g_strdup_printf(":%d", cmd_prefix
);
4644 bzero(&a
, sizeof a
);
4650 bzero(&a
, sizeof a
);
4651 a
.i
= XT_HINT_NEWTAB
;
4661 case XT_CMD_OPEN_CURRENT
:
4664 case XT_CMD_TABNEW_CURRENT
:
4665 if (!s
) /* FALL THROUGH? */
4667 if ((uri
= get_uri(t
)) != NULL
) {
4668 ss
= g_strdup_printf("%s%s", s
, uri
);
4673 show_oops(t
, "command: invalid opcode %d", args
->i
);
4674 return (XT_CB_PASSTHROUGH
);
4677 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4679 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4680 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4681 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4683 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4684 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4689 return (XT_CB_HANDLED
);
4693 * Return a new string with a download row (in html)
4694 * appended. Old string is freed.
4697 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4700 WebKitDownloadStatus stat
;
4701 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4703 char cur_sz
[FMT_SCALED_STRSIZE
];
4704 char tot_sz
[FMT_SCALED_STRSIZE
];
4707 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4709 /* All actions wil take this form:
4710 * xxxt://class/seskey
4712 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4713 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4715 stat
= webkit_download_get_status(dl
->download
);
4718 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4719 status_html
= g_strdup_printf("Finished");
4720 cmd_html
= g_strdup_printf(
4721 "<a href='%s%d/%d'>Remove</a>",
4722 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4724 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4725 /* gather size info */
4726 progress
= 100 * webkit_download_get_progress(dl
->download
);
4729 webkit_download_get_current_size(dl
->download
), cur_sz
);
4731 webkit_download_get_total_size(dl
->download
), tot_sz
);
4733 status_html
= g_strdup_printf(
4734 "<div style='width: 100%%' align='center'>"
4735 "<div class='progress-outer'>"
4736 "<div class='progress-inner' style='width: %.2f%%'>"
4737 "</div></div></div>"
4738 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4739 progress
, cur_sz
, tot_sz
, progress
);
4741 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4742 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4746 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4747 status_html
= g_strdup_printf("Cancelled");
4748 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4749 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4751 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4752 status_html
= g_strdup_printf("Error!");
4753 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4754 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4756 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4757 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4758 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4759 status_html
= g_strdup_printf("Starting");
4762 show_oops(t
, "%s: unknown download status", __func__
);
4765 new_html
= g_strdup_printf(
4766 "%s\n<tr><td>%s</td><td>%s</td>"
4767 "<td style='text-align:center'>%s</td></tr>\n",
4768 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4769 status_html
, cmd_html
);
4773 g_free(status_html
);
4784 * update all download tabs apart from one. Pass NULL if
4785 * you want to update all.
4788 update_download_tabs(struct tab
*apart_from
)
4791 if (!updating_dl_tabs
) {
4792 updating_dl_tabs
= 1; /* stop infinite recursion */
4793 TAILQ_FOREACH(t
, &tabs
, entry
)
4794 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4795 && (t
!= apart_from
))
4796 xtp_page_dl(t
, NULL
);
4797 updating_dl_tabs
= 0;
4802 * update all cookie tabs apart from one. Pass NULL if
4803 * you want to update all.
4806 update_cookie_tabs(struct tab
*apart_from
)
4809 if (!updating_cl_tabs
) {
4810 updating_cl_tabs
= 1; /* stop infinite recursion */
4811 TAILQ_FOREACH(t
, &tabs
, entry
)
4812 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4813 && (t
!= apart_from
))
4814 xtp_page_cl(t
, NULL
);
4815 updating_cl_tabs
= 0;
4820 * update all history tabs apart from one. Pass NULL if
4821 * you want to update all.
4824 update_history_tabs(struct tab
*apart_from
)
4828 if (!updating_hl_tabs
) {
4829 updating_hl_tabs
= 1; /* stop infinite recursion */
4830 TAILQ_FOREACH(t
, &tabs
, entry
)
4831 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4832 && (t
!= apart_from
))
4833 xtp_page_hl(t
, NULL
);
4834 updating_hl_tabs
= 0;
4838 /* cookie management XTP page */
4840 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4842 char *body
, *page
, *tmp
;
4843 int i
= 1; /* all ids start 1 */
4844 GSList
*sc
, *pc
, *pc_start
;
4846 char *type
, *table_headers
, *last_domain
;
4848 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4851 show_oops(NULL
, "%s invalid parameters", __func__
);
4855 /* Generate a new session key */
4856 if (!updating_cl_tabs
)
4857 generate_xtp_session_key(&cl_session_key
);
4860 table_headers
= g_strdup_printf("<table><tr>"
4863 "<th style='width:200px'>Value</th>"
4867 "<th>HTTP<br />only</th>"
4868 "<th style='width:40px'>Rm</th></tr>\n");
4870 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4871 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4875 last_domain
= strdup("");
4876 for (; sc
; sc
= sc
->next
) {
4879 if (strcmp(last_domain
, c
->domain
) != 0) {
4882 last_domain
= strdup(c
->domain
);
4886 body
= g_strdup_printf("%s</table>"
4888 body
, c
->domain
, table_headers
);
4892 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4893 c
->domain
, table_headers
);
4898 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4899 if (soup_cookie_equal(pc
->data
, c
)) {
4900 type
= "Session + Persistent";
4905 body
= g_strdup_printf(
4908 "<td style='word-wrap:normal'>%s</td>"
4910 " <textarea rows='4'>%s</textarea>"
4916 "<td style='text-align:center'>"
4917 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4924 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4939 soup_cookies_free(sc
);
4940 soup_cookies_free(pc
);
4942 /* small message if there are none */
4944 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4945 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4948 body
= g_strdup_printf("%s</table>", body
);
4951 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4953 g_free(table_headers
);
4954 g_free(last_domain
);
4956 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4957 update_cookie_tabs(t
);
4965 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4967 char *body
, *page
, *tmp
;
4969 int i
= 1; /* all ids start 1 */
4971 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4974 show_oops(NULL
, "%s invalid parameters", __func__
);
4978 /* Generate a new session key */
4979 if (!updating_hl_tabs
)
4980 generate_xtp_session_key(&hl_session_key
);
4983 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4984 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4986 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4988 body
= g_strdup_printf(
4990 "<td><a href='%s'>%s</a></td>"
4992 "<td style='text-align: center'>"
4993 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4994 body
, h
->uri
, h
->uri
, h
->title
,
4995 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4996 XT_XTP_HL_REMOVE
, i
);
5002 /* small message if there are none */
5005 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5006 "colspan='3'>No History</td></tr>\n", body
);
5011 body
= g_strdup_printf("%s</table>", body
);
5014 page
= get_html_page("History", body
, "", TRUE
);
5018 * update all history manager tabs as the xtp session
5019 * key has now changed. No need to update the current tab.
5020 * Already did that above.
5022 update_history_tabs(t
);
5024 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5031 * Generate a web page detailing the status of any downloads
5034 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5036 struct download
*dl
;
5037 char *body
, *page
, *tmp
;
5041 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5044 show_oops(NULL
, "%s invalid parameters", __func__
);
5049 * Generate a new session key for next page instance.
5050 * This only happens for the top level call to xtp_page_dl()
5051 * in which case updating_dl_tabs is 0.
5053 if (!updating_dl_tabs
)
5054 generate_xtp_session_key(&dl_session_key
);
5056 /* header - with refresh so as to update */
5057 if (refresh_interval
>= 1)
5058 ref
= g_strdup_printf(
5059 "<meta http-equiv='refresh' content='%u"
5060 ";url=%s%d/%s/%d' />\n",
5069 body
= g_strdup_printf("<div align='center'>"
5070 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5071 "</p><table><tr><th style='width: 60%%'>"
5072 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5073 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5075 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5076 body
= xtp_page_dl_row(t
, body
, dl
);
5080 /* message if no downloads in list */
5083 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5084 " style='text-align: center'>"
5085 "No downloads</td></tr>\n", body
);
5090 body
= g_strdup_printf("%s</table></div>", body
);
5093 page
= get_html_page("Downloads", body
, ref
, 1);
5098 * update all download manager tabs as the xtp session
5099 * key has now changed. No need to update the current tab.
5100 * Already did that above.
5102 update_download_tabs(t
);
5104 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5111 search(struct tab
*t
, struct karg
*args
)
5115 if (t
== NULL
|| args
== NULL
) {
5116 show_oops(NULL
, "search invalid parameters");
5121 case XT_SEARCH_NEXT
:
5122 d
= t
->search_forward
;
5124 case XT_SEARCH_PREV
:
5125 d
= !t
->search_forward
;
5128 return (XT_CB_PASSTHROUGH
);
5131 if (t
->search_text
== NULL
) {
5132 if (global_search
== NULL
)
5133 return (XT_CB_PASSTHROUGH
);
5135 d
= t
->search_forward
= TRUE
;
5136 t
->search_text
= g_strdup(global_search
);
5137 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5138 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5142 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5143 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5145 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5147 return (XT_CB_HANDLED
);
5150 struct settings_args
{
5156 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5159 struct settings_args
*sa
= cb_args
;
5164 if (s
->flags
& XT_SF_RUNTIME
)
5170 *sa
->body
= g_strdup_printf(
5172 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5173 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5185 set_show(struct tab
*t
, struct karg
*args
)
5187 char *body
, *page
, *tmp
;
5189 struct settings_args sa
;
5191 bzero(&sa
, sizeof sa
);
5195 body
= g_strdup_printf("<div align='center'><table><tr>"
5196 "<th align='left'>Setting</th>"
5197 "<th align='left'>Value</th></tr>\n");
5199 settings_walk(print_setting
, &sa
);
5202 /* small message if there are none */
5205 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5206 "colspan='2'>No settings</td></tr>\n", body
);
5211 body
= g_strdup_printf("%s</table></div>", body
);
5214 page
= get_html_page("Settings", body
, "", 0);
5218 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5222 return (XT_CB_PASSTHROUGH
);
5226 set(struct tab
*t
, struct karg
*args
)
5231 if (args
== NULL
|| args
->s
== NULL
)
5232 return (set_show(t
, args
));
5235 p
= g_strstrip(args
->s
);
5238 return (set_show(t
, args
));
5240 /* we got some sort of string */
5241 val
= g_strrstr(p
, "=");
5244 val
= g_strchomp(val
);
5247 for (i
= 0; i
< LENGTH(rs
); i
++) {
5248 if (strcmp(rs
[i
].name
, p
))
5251 if (rs
[i
].activate
) {
5252 if (rs
[i
].activate(val
))
5253 show_oops(t
, "%s invalid value %s",
5256 show_oops(t
, ":set %s = %s", p
, val
);
5259 show_oops(t
, "not a runtime option: %s", p
);
5263 show_oops(t
, "unknown option: %s", p
);
5267 for (i
= 0; i
< LENGTH(rs
); i
++) {
5268 if (strcmp(rs
[i
].name
, p
))
5271 /* XXX this could use some cleanup */
5272 switch (rs
[i
].type
) {
5275 show_oops(t
, "%s = %d",
5276 rs
[i
].name
, *rs
[i
].ival
);
5277 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5278 show_oops(t
, "%s = %s",
5280 rs
[i
].s
->get(&rs
[i
]));
5281 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5282 show_oops(t
, "%s = ...", rs
[i
].name
);
5284 show_oops(t
, "%s = ", rs
[i
].name
);
5288 show_oops(t
, "%s = %f",
5289 rs
[i
].name
, *rs
[i
].fval
);
5290 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5291 show_oops(t
, "%s = %s",
5293 rs
[i
].s
->get(&rs
[i
]));
5294 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5295 show_oops(t
, "%s = ...", rs
[i
].name
);
5297 show_oops(t
, "%s = ", rs
[i
].name
);
5300 if (rs
[i
].sval
&& *rs
[i
].sval
)
5301 show_oops(t
, "%s = %s",
5302 rs
[i
].name
, *rs
[i
].sval
);
5303 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5304 show_oops(t
, "%s = %s",
5306 rs
[i
].s
->get(&rs
[i
]));
5307 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5308 show_oops(t
, "%s = ...", rs
[i
].name
);
5310 show_oops(t
, "%s = ", rs
[i
].name
);
5313 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5319 show_oops(t
, "unknown option: %s", p
);
5322 return (XT_CB_PASSTHROUGH
);
5326 session_save(struct tab
*t
, char *filename
)
5332 if (strlen(filename
) == 0)
5335 if (filename
[0] == '.' || filename
[0] == '/')
5339 if (save_tabs(t
, &a
))
5341 strlcpy(named_session
, filename
, sizeof named_session
);
5343 /* add the new session to the list of sessions */
5344 s
= g_malloc(sizeof(struct session
));
5345 s
->name
= g_strdup(filename
);
5346 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5354 session_open(struct tab
*t
, char *filename
)
5359 if (strlen(filename
) == 0)
5362 if (filename
[0] == '.' || filename
[0] == '/')
5366 a
.i
= XT_SES_CLOSETABS
;
5367 if (open_tabs(t
, &a
))
5370 strlcpy(named_session
, filename
, sizeof named_session
);
5378 session_delete(struct tab
*t
, char *filename
)
5380 char file
[PATH_MAX
];
5384 if (strlen(filename
) == 0)
5387 if (filename
[0] == '.' || filename
[0] == '/')
5390 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5394 if (!strcmp(filename
, named_session
))
5395 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5396 sizeof named_session
);
5398 /* remove session from sessions list */
5399 TAILQ_FOREACH(s
, &sessions
, entry
) {
5400 if (!strcmp(s
->name
, filename
))
5405 TAILQ_REMOVE(&sessions
, s
, entry
);
5406 g_free((gpointer
) s
->name
);
5415 session_cmd(struct tab
*t
, struct karg
*args
)
5417 char *filename
= args
->s
;
5422 if (args
->i
& XT_SHOW
)
5423 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5424 XT_SAVED_TABS_FILE
: named_session
);
5425 else if (args
->i
& XT_SAVE
) {
5426 if (session_save(t
, filename
)) {
5427 show_oops(t
, "Can't save session: %s",
5428 filename
? filename
: "INVALID");
5431 } else if (args
->i
& XT_OPEN
) {
5432 if (session_open(t
, filename
)) {
5433 show_oops(t
, "Can't open session: %s",
5434 filename
? filename
: "INVALID");
5437 } else if (args
->i
& XT_DELETE
) {
5438 if (session_delete(t
, filename
)) {
5439 show_oops(t
, "Can't delete session: %s",
5440 filename
? filename
: "INVALID");
5445 return (XT_CB_PASSTHROUGH
);
5449 script_cmd(struct tab
*t
, struct karg
*args
)
5451 JSGlobalContextRef ctx
;
5452 WebKitWebFrame
*frame
;
5454 JSValueRef val
, exception
;
5463 if ((f
= fopen(args
->s
, "r")) == NULL
) {
5464 show_oops(t
, "Can't open script file: %s", args
->s
);
5468 if (fstat(fileno(f
), &sb
) == -1) {
5469 show_oops(t
, "Can't stat script file: %s", args
->s
);
5473 buf
= g_malloc0(sb
.st_size
+ 1);
5474 if (fread(buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
5475 show_oops(t
, "Can't read script file: %s", args
->s
);
5479 /* this code needs to be redone */
5480 frame
= webkit_web_view_get_main_frame(t
->wv
);
5481 ctx
= webkit_web_frame_get_global_context(frame
);
5483 str
= JSStringCreateWithUTF8CString(buf
);
5484 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
5485 NULL
, 0, &exception
);
5486 JSStringRelease(str
);
5488 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
5490 es
= js_ref_to_string(ctx
, exception
);
5492 show_oops(t
, "script exception: %s", es
);
5497 es
= js_ref_to_string(ctx
, val
);
5500 if (!strncmp(es
, XT_JS_DONE
, XT_JS_DONE_LEN
))
5502 if (!strncmp(es
, XT_JS_INSERT
, XT_JS_INSERT_LEN
))
5506 show_oops(t
, "script complete return value: '%s'", es
);
5509 show_oops(t
, "script complete: without a return value");
5518 return (XT_CB_PASSTHROUGH
);
5522 * Make a hardcopy of the page
5525 print_page(struct tab
*t
, struct karg
*args
)
5527 WebKitWebFrame
*frame
;
5529 GtkPrintOperation
*op
;
5530 GtkPrintOperationAction action
;
5531 GtkPrintOperationResult print_res
;
5532 GError
*g_err
= NULL
;
5533 int marg_l
, marg_r
, marg_t
, marg_b
;
5535 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5537 ps
= gtk_page_setup_new();
5538 op
= gtk_print_operation_new();
5539 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5540 frame
= webkit_web_view_get_main_frame(t
->wv
);
5542 /* the default margins are too small, so we will bump them */
5543 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5544 XT_PRINT_EXTRA_MARGIN
;
5545 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5546 XT_PRINT_EXTRA_MARGIN
;
5547 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5548 XT_PRINT_EXTRA_MARGIN
;
5549 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5550 XT_PRINT_EXTRA_MARGIN
;
5553 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5554 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5555 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5556 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5558 gtk_print_operation_set_default_page_setup(op
, ps
);
5560 /* this appears to free 'op' and 'ps' */
5561 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5563 /* check it worked */
5564 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5565 show_oops(NULL
, "can't print: %s", g_err
->message
);
5566 g_error_free (g_err
);
5574 go_home(struct tab
*t
, struct karg
*args
)
5581 set_encoding(struct tab
*t
, struct karg
*args
)
5585 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5586 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5588 e
= webkit_web_view_get_encoding(t
->wv
);
5589 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5591 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5597 restart(struct tab
*t
, struct karg
*args
)
5601 a
.s
= XT_RESTART_TABS_FILE
;
5603 execvp(start_argv
[0], start_argv
);
5609 #define CTRL GDK_CONTROL_MASK
5610 #define MOD1 GDK_MOD1_MASK
5611 #define SHFT GDK_SHIFT_MASK
5613 /* inherent to GTK not all keys will be caught at all times */
5614 /* XXX sort key bindings */
5615 struct key_binding
{
5620 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5622 { "command_mode", 0, 0, GDK_Escape
},
5623 { "insert_mode", 0, 0, GDK_i
},
5624 { "cookiejar", MOD1
, 0, GDK_j
},
5625 { "downloadmgr", MOD1
, 0, GDK_d
},
5626 { "history", MOD1
, 0, GDK_h
},
5627 { "print", CTRL
, 0, GDK_p
},
5628 { "search", 0, 0, GDK_slash
},
5629 { "searchb", 0, 0, GDK_question
},
5630 { "statustoggle", CTRL
, 0, GDK_n
},
5631 { "command", 0, 0, GDK_colon
},
5632 { "qa", CTRL
, 0, GDK_q
},
5633 { "restart", MOD1
, 0, GDK_q
},
5634 { "js toggle", CTRL
, 0, GDK_j
},
5635 { "cookie toggle", MOD1
, 0, GDK_c
},
5636 { "togglesrc", CTRL
, 0, GDK_s
},
5637 { "yankuri", 0, 0, GDK_y
},
5638 { "pasteuricur", 0, 0, GDK_p
},
5639 { "pasteurinew", 0, 0, GDK_P
},
5640 { "toplevel toggle", 0, 0, GDK_F4
},
5641 { "help", 0, 0, GDK_F1
},
5642 { "run_script", MOD1
, 0, GDK_r
},
5645 { "searchnext", 0, 0, GDK_n
},
5646 { "searchprevious", 0, 0, GDK_N
},
5649 { "focusaddress", 0, 0, GDK_F6
},
5650 { "focussearch", 0, 0, GDK_F7
},
5653 { "hinting", 0, 0, GDK_f
},
5654 { "hinting", 0, 0, GDK_period
},
5655 { "hinting_newtab", SHFT
, 0, GDK_F
},
5656 { "hinting_newtab", 0, 0, GDK_comma
},
5658 /* custom stylesheet */
5659 { "userstyle", 0, 0, GDK_s
},
5662 { "goback", 0, 0, GDK_BackSpace
},
5663 { "goback", MOD1
, 0, GDK_Left
},
5664 { "goforward", SHFT
, 0, GDK_BackSpace
},
5665 { "goforward", MOD1
, 0, GDK_Right
},
5666 { "reload", 0, 0, GDK_F5
},
5667 { "reload", CTRL
, 0, GDK_r
},
5668 { "reload", CTRL
, 0, GDK_l
},
5669 { "favorites", MOD1
, 1, GDK_f
},
5671 /* vertical movement */
5672 { "scrolldown", 0, 0, GDK_j
},
5673 { "scrolldown", 0, 0, GDK_Down
},
5674 { "scrollup", 0, 0, GDK_Up
},
5675 { "scrollup", 0, 0, GDK_k
},
5676 { "scrollbottom", 0, 0, GDK_G
},
5677 { "scrollbottom", 0, 0, GDK_End
},
5678 { "scrolltop", 0, 0, GDK_Home
},
5679 { "scrollpagedown", 0, 0, GDK_space
},
5680 { "scrollpagedown", CTRL
, 0, GDK_f
},
5681 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5682 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5683 { "scrollpageup", 0, 0, GDK_Page_Up
},
5684 { "scrollpageup", CTRL
, 0, GDK_b
},
5685 { "scrollhalfup", CTRL
, 0, GDK_u
},
5686 /* horizontal movement */
5687 { "scrollright", 0, 0, GDK_l
},
5688 { "scrollright", 0, 0, GDK_Right
},
5689 { "scrollleft", 0, 0, GDK_Left
},
5690 { "scrollleft", 0, 0, GDK_h
},
5691 { "scrollfarright", 0, 0, GDK_dollar
},
5692 { "scrollfarleft", 0, 0, GDK_0
},
5695 { "tabnew", CTRL
, 0, GDK_t
},
5696 { "999tabnew", CTRL
, 0, GDK_T
},
5697 { "tabclose", CTRL
, 1, GDK_w
},
5698 { "tabundoclose", 0, 0, GDK_U
},
5699 { "tabnext 1", CTRL
, 0, GDK_1
},
5700 { "tabnext 2", CTRL
, 0, GDK_2
},
5701 { "tabnext 3", CTRL
, 0, GDK_3
},
5702 { "tabnext 4", CTRL
, 0, GDK_4
},
5703 { "tabnext 5", CTRL
, 0, GDK_5
},
5704 { "tabnext 6", CTRL
, 0, GDK_6
},
5705 { "tabnext 7", CTRL
, 0, GDK_7
},
5706 { "tabnext 8", CTRL
, 0, GDK_8
},
5707 { "tabnext 9", CTRL
, 0, GDK_9
},
5708 { "tabfirst", CTRL
, 0, GDK_less
},
5709 { "tablast", CTRL
, 0, GDK_greater
},
5710 { "tabprevious", CTRL
, 0, GDK_Left
},
5711 { "tabnext", CTRL
, 0, GDK_Right
},
5712 { "focusout", CTRL
, 0, GDK_minus
},
5713 { "focusin", CTRL
, 0, GDK_plus
},
5714 { "focusin", CTRL
, 0, GDK_equal
},
5715 { "focusreset", CTRL
, 0, GDK_0
},
5717 /* command aliases (handy when -S flag is used) */
5718 { "promptopen", 0, 0, GDK_F9
},
5719 { "promptopencurrent", 0, 0, GDK_F10
},
5720 { "prompttabnew", 0, 0, GDK_F11
},
5721 { "prompttabnewcurrent",0, 0, GDK_F12
},
5723 TAILQ_HEAD(keybinding_list
, key_binding
);
5726 walk_kb(struct settings
*s
,
5727 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5729 struct key_binding
*k
;
5732 if (s
== NULL
|| cb
== NULL
) {
5733 show_oops(NULL
, "walk_kb invalid parameters");
5737 TAILQ_FOREACH(k
, &kbl
, entry
) {
5743 if (gdk_keyval_name(k
->key
) == NULL
)
5746 strlcat(str
, k
->cmd
, sizeof str
);
5747 strlcat(str
, ",", sizeof str
);
5749 if (k
->mask
& GDK_SHIFT_MASK
)
5750 strlcat(str
, "S-", sizeof str
);
5751 if (k
->mask
& GDK_CONTROL_MASK
)
5752 strlcat(str
, "C-", sizeof str
);
5753 if (k
->mask
& GDK_MOD1_MASK
)
5754 strlcat(str
, "M1-", sizeof str
);
5755 if (k
->mask
& GDK_MOD2_MASK
)
5756 strlcat(str
, "M2-", sizeof str
);
5757 if (k
->mask
& GDK_MOD3_MASK
)
5758 strlcat(str
, "M3-", sizeof str
);
5759 if (k
->mask
& GDK_MOD4_MASK
)
5760 strlcat(str
, "M4-", sizeof str
);
5761 if (k
->mask
& GDK_MOD5_MASK
)
5762 strlcat(str
, "M5-", sizeof str
);
5764 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5765 cb(s
, str
, cb_args
);
5770 init_keybindings(void)
5773 struct key_binding
*k
;
5775 for (i
= 0; i
< LENGTH(keys
); i
++) {
5776 k
= g_malloc0(sizeof *k
);
5777 k
->cmd
= keys
[i
].cmd
;
5778 k
->mask
= keys
[i
].mask
;
5779 k
->use_in_entry
= keys
[i
].use_in_entry
;
5780 k
->key
= keys
[i
].key
;
5781 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5783 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5784 k
->cmd
? k
->cmd
: "unnamed key");
5789 keybinding_clearall(void)
5791 struct key_binding
*k
, *next
;
5793 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5794 next
= TAILQ_NEXT(k
, entry
);
5798 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5799 k
->cmd
? k
->cmd
: "unnamed key");
5800 TAILQ_REMOVE(&kbl
, k
, entry
);
5806 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5808 struct key_binding
*k
;
5809 guint keyval
, mask
= 0;
5812 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5814 /* Keys which are to be used in entry have been prefixed with an
5815 * exclamation mark. */
5819 /* find modifier keys */
5820 if (strstr(key
, "S-"))
5821 mask
|= GDK_SHIFT_MASK
;
5822 if (strstr(key
, "C-"))
5823 mask
|= GDK_CONTROL_MASK
;
5824 if (strstr(key
, "M1-"))
5825 mask
|= GDK_MOD1_MASK
;
5826 if (strstr(key
, "M2-"))
5827 mask
|= GDK_MOD2_MASK
;
5828 if (strstr(key
, "M3-"))
5829 mask
|= GDK_MOD3_MASK
;
5830 if (strstr(key
, "M4-"))
5831 mask
|= GDK_MOD4_MASK
;
5832 if (strstr(key
, "M5-"))
5833 mask
|= GDK_MOD5_MASK
;
5836 for (i
= strlen(key
) - 1; i
> 0; i
--)
5840 /* validate keyname */
5841 keyval
= gdk_keyval_from_name(key
);
5842 if (keyval
== GDK_VoidSymbol
) {
5843 warnx("invalid keybinding name %s", key
);
5846 /* must run this test too, gtk+ doesn't handle 10 for example */
5847 if (gdk_keyval_name(keyval
) == NULL
) {
5848 warnx("invalid keybinding name %s", key
);
5852 /* Remove eventual dupes. */
5853 TAILQ_FOREACH(k
, &kbl
, entry
)
5854 if (k
->key
== keyval
&& k
->mask
== mask
) {
5855 TAILQ_REMOVE(&kbl
, k
, entry
);
5861 k
= g_malloc0(sizeof *k
);
5862 k
->cmd
= g_strdup(cmd
);
5864 k
->use_in_entry
= use_in_entry
;
5867 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5872 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5873 k
->cmd
, gdk_keyval_name(keyval
));
5875 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5881 add_kb(struct settings
*s
, char *entry
)
5885 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5887 /* clearall is special */
5888 if (!strcmp(entry
, "clearall")) {
5889 keybinding_clearall();
5893 kb
= strstr(entry
, ",");
5899 return (keybinding_add(entry
, key
, key
[0] == '!'));
5905 int (*func
)(struct tab
*, struct karg
*);
5909 { "command_mode", 0, command_mode
, XT_MODE_COMMAND
, 0 },
5910 { "insert_mode", 0, command_mode
, XT_MODE_INSERT
, 0 },
5911 { "command", 0, command
, ':', 0 },
5912 { "search", 0, command
, '/', 0 },
5913 { "searchb", 0, command
, '?', 0 },
5914 { "hinting", 0, command
, '.', 0 },
5915 { "hinting_newtab", 0, command
, ',', 0 },
5916 { "togglesrc", 0, toggle_src
, 0, 0 },
5918 /* yanking and pasting */
5919 { "yankuri", 0, yank_uri
, 0, 0 },
5920 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5921 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5922 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5925 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5926 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5929 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5930 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5933 { "hinting", 0, hint
, 0, 0 },
5934 { "hinting_newtab", 0, hint
, XT_HINT_NEWTAB
, 0 },
5936 /* custom stylesheet */
5937 { "userstyle", 0, userstyle
, 0, 0 },
5940 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5941 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5942 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5944 /* vertical movement */
5945 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5946 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5947 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5948 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5949 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5950 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5951 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5952 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5953 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5954 /* horizontal movement */
5955 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5956 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5957 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5958 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5960 { "favorites", 0, xtp_page_fl
, 0, 0 },
5961 { "fav", 0, xtp_page_fl
, 0, 0 },
5962 { "favadd", 0, add_favorite
, 0, 0 },
5964 { "qall", 0, quit
, 0, 0 },
5965 { "quitall", 0, quit
, 0, 0 },
5966 { "w", 0, save_tabs
, 0, 0 },
5967 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5968 { "help", 0, help
, 0, 0 },
5969 { "about", 0, about
, 0, 0 },
5970 { "stats", 0, stats
, 0, 0 },
5971 { "version", 0, about
, 0, 0 },
5974 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5975 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5976 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5977 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5978 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5979 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5980 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5981 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5982 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5983 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5984 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5986 /* cookie command */
5987 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5988 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5989 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5990 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5991 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5992 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5993 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5994 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5995 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5996 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5997 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5999 /* plugin command */
6000 { "plugin", 0, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6001 { "save", 1, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6002 { "domain", 2, pl_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
6003 { "fqdn", 2, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6004 { "show", 1, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6005 { "all", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6006 { "persistent", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
6007 { "session", 2, pl_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6008 { "toggle", 1, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6009 { "domain", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6010 { "fqdn", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6012 /* toplevel (domain) command */
6013 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6014 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6017 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
6020 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
6021 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
6022 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
6024 { "ca", 0, ca_cmd
, 0, 0 },
6025 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
6026 { "dl", 0, xtp_page_dl
, 0, 0 },
6027 { "h", 0, xtp_page_hl
, 0, 0 },
6028 { "history", 0, xtp_page_hl
, 0, 0 },
6029 { "home", 0, go_home
, 0, 0 },
6030 { "restart", 0, restart
, 0, 0 },
6031 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
6032 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
6033 { "statustoggle", 0, statustoggle
, 0, 0 },
6034 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
6036 { "print", 0, print_page
, 0, 0 },
6039 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
6040 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
6041 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
6042 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6043 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6044 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
6045 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
6046 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6047 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
6048 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
6049 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
6050 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6051 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
6052 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
6053 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
6054 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
6055 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
6056 { "tabs", 0, buffers
, 0, 0 },
6057 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
6058 { "buffers", 0, buffers
, 0, 0 },
6059 { "ls", 0, buffers
, 0, 0 },
6060 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
6062 /* command aliases (handy when -S flag is used) */
6063 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
6064 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
6065 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
6066 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
6069 { "set", 0, set
, 0, XT_SETARG
},
6071 { "fullscreen", 0, fullscreen
, 0, 0 },
6072 { "f", 0, fullscreen
, 0, 0 },
6075 { "session", 0, session_cmd
, XT_SHOW
, 0 },
6076 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
6077 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
6078 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
6079 { "show", 1, session_cmd
, XT_SHOW
, 0 },
6081 /* external javascript */
6082 { "script", 0, script_cmd
, XT_EJS_SHOW
, XT_USERARG
},
6089 } cmd_status
= {-1, 0};
6092 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6095 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
6102 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6109 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6111 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
6117 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
6119 a
.i
= XT_NAV_FORWARD
;
6129 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6131 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6133 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6140 * cancel, remove, etc. downloads
6143 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6145 struct download find
, *d
= NULL
;
6147 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6149 /* some commands require a valid download id */
6150 if (cmd
!= XT_XTP_DL_LIST
) {
6151 /* lookup download in question */
6153 d
= RB_FIND(download_list
, &downloads
, &find
);
6156 show_oops(t
, "%s: no such download", __func__
);
6161 /* decide what to do */
6163 case XT_XTP_DL_CANCEL
:
6164 webkit_download_cancel(d
->download
);
6166 case XT_XTP_DL_REMOVE
:
6167 webkit_download_cancel(d
->download
); /* just incase */
6168 g_object_unref(d
->download
);
6169 RB_REMOVE(download_list
, &downloads
, d
);
6171 case XT_XTP_DL_LIST
:
6175 show_oops(t
, "%s: unknown command", __func__
);
6178 xtp_page_dl(t
, NULL
);
6182 * Actions on history, only does one thing for now, but
6183 * we provide the function for future actions
6186 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6188 struct history
*h
, *next
;
6192 case XT_XTP_HL_REMOVE
:
6193 /* walk backwards, as listed in reverse */
6194 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6195 next
= RB_PREV(history_list
, &hl
, h
);
6197 RB_REMOVE(history_list
, &hl
, h
);
6198 g_free((gpointer
) h
->title
);
6199 g_free((gpointer
) h
->uri
);
6206 case XT_XTP_HL_LIST
:
6207 /* Nothing - just xtp_page_hl() below */
6210 show_oops(t
, "%s: unknown command", __func__
);
6214 xtp_page_hl(t
, NULL
);
6217 /* remove a favorite */
6219 remove_favorite(struct tab
*t
, int index
)
6221 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6222 char *new_favs
, *tmp
;
6227 /* open favorites */
6228 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6230 if ((f
= fopen(file
, "r")) == NULL
) {
6231 show_oops(t
, "%s: can't open favorites: %s",
6232 __func__
, strerror(errno
));
6236 /* build a string which will become the new favroites file */
6237 new_favs
= g_strdup("");
6240 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6241 if (feof(f
) || ferror(f
))
6243 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6250 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6251 if (feof(f
) || ferror(f
)) {
6252 show_oops(t
, "%s: can't parse favorites %s",
6253 __func__
, strerror(errno
));
6258 /* as long as this isn't the one we are deleting add to file */
6261 new_favs
= g_strdup_printf("%s%s\n%s\n",
6262 new_favs
, title
, uri
);
6274 /* write back new favorites file */
6275 if ((f
= fopen(file
, "w")) == NULL
) {
6276 show_oops(t
, "%s: can't open favorites: %s",
6277 __func__
, strerror(errno
));
6281 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
6282 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
6295 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6298 case XT_XTP_FL_LIST
:
6299 /* nothing, just the below call to xtp_page_fl() */
6301 case XT_XTP_FL_REMOVE
:
6302 remove_favorite(t
, arg
);
6305 show_oops(t
, "%s: invalid favorites command", __func__
);
6309 xtp_page_fl(t
, NULL
);
6313 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6316 case XT_XTP_CL_LIST
:
6317 /* nothing, just xtp_page_cl() */
6319 case XT_XTP_CL_REMOVE
:
6323 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6327 xtp_page_cl(t
, NULL
);
6330 /* link an XTP class to it's session key and handler function */
6331 struct xtp_despatch
{
6334 void (*handle_func
)(struct tab
*, uint8_t, int);
6337 struct xtp_despatch xtp_despatches
[] = {
6338 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6339 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6340 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6341 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6342 { XT_XTP_INVALID
, NULL
, NULL
}
6346 * is the url xtp protocol? (xxxt://)
6347 * if so, parse and despatch correct bahvior
6350 parse_xtp_url(struct tab
*t
, const char *url
)
6352 char *dup
= NULL
, *p
, *last
= NULL
;
6353 uint8_t n_tokens
= 0;
6354 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6355 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6360 * tokens array meaning:
6362 * tokens[1] = session key
6363 * tokens[2] = action
6364 * tokens[3] = optional argument
6367 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6369 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6372 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6374 /* split out the url */
6375 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6376 (p
= strtok_r(NULL
, "/", &last
))) {
6378 tokens
[n_tokens
++] = p
;
6381 /* should be atleast three fields 'class/seskey/command/arg' */
6385 dsp
= xtp_despatches
;
6386 req_class
= atoi(tokens
[0]);
6387 while (dsp
->xtp_class
) {
6388 if (dsp
->xtp_class
== req_class
) {
6395 /* did we find one atall? */
6396 if (dsp_match
== NULL
) {
6397 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6401 /* check session key and call despatch function */
6402 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6403 ret
= TRUE
; /* all is well, this was a valid xtp request */
6404 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6417 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6419 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6421 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6424 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6429 show_oops(t
, "activate_uri_entry_cb no uri");
6433 uri
+= strspn(uri
, "\t ");
6435 /* if xxxt:// treat specially */
6436 if (parse_xtp_url(t
, uri
))
6439 /* otherwise continue to load page normally */
6440 load_uri(t
, (gchar
*)uri
);
6445 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6447 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6448 char *newuri
= NULL
;
6451 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6454 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6458 if (search_string
== NULL
) {
6459 show_oops(t
, "no search_string");
6463 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6465 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6466 newuri
= g_strdup_printf(search_string
, enc_search
);
6470 webkit_web_view_load_uri(t
->wv
, newuri
);
6478 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6480 struct domain
*d
= NULL
;
6483 if (uri
== NULL
|| t
== NULL
)
6486 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6491 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6492 es
? "enable" : "disable", uri
);
6494 g_object_set(G_OBJECT(t
->settings
),
6495 "enable-html5-local-storage", es
, (char *)NULL
);
6496 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6500 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6502 struct domain
*d
= NULL
;
6505 if (uri
== NULL
|| t
== NULL
)
6508 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6513 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6514 es
? "enable" : "disable", uri
);
6516 g_object_set(G_OBJECT(t
->settings
),
6517 "enable-scripts", es
, (char *)NULL
);
6518 g_object_set(G_OBJECT(t
->settings
),
6519 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6520 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6522 button_set_stockid(t
->js_toggle
,
6523 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6527 check_and_set_pl(const gchar
*uri
, struct tab
*t
)
6529 struct domain
*d
= NULL
;
6532 if (uri
== NULL
|| t
== NULL
)
6535 if ((d
= wl_find_uri(uri
, &pl_wl
)) == NULL
)
6540 DNPRINTF(XT_D_JS
, "check_and_set_pl: %s %s\n",
6541 es
? "enable" : "disable", uri
);
6543 g_object_set(G_OBJECT(t
->settings
),
6544 "enable-plugins", es
, (char *)NULL
);
6545 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6549 color_address_bar(gpointer p
)
6552 struct tab
*tt
, *t
= p
;
6553 gchar
*col_str
= XT_COLOR_WHITE
;
6554 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
6557 gdk_threads_enter();
6559 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6561 /* make sure t still exists */
6564 TAILQ_FOREACH(tt
, &tabs
, entry
)
6570 if ((uri
= get_uri(t
)) == NULL
)
6575 gdk_threads_leave();
6578 col_str
= XT_COLOR_YELLOW
;
6579 switch (load_compare_cert(u
, &error_str
)) {
6581 col_str
= XT_COLOR_BLUE
;
6584 col_str
= XT_COLOR_GREEN
;
6586 case CERT_UNTRUSTED
:
6587 col_str
= XT_COLOR_YELLOW
;
6590 col_str
= XT_COLOR_RED
;
6595 gdk_threads_enter();
6597 /* make sure t isn't deleted */
6598 TAILQ_FOREACH(tt
, &tabs
, entry
)
6605 /* test to see if the user navigated away and canceled the thread */
6606 if (t
->thread
!= g_thread_self())
6608 if ((uri
= get_uri(t
)) == NULL
) {
6612 if (strcmp(uri
, u
)) {
6613 /* make sure we are still the same url */
6619 gdk_color_parse(col_str
, &color
);
6620 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6622 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6623 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6625 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6627 if (error_str
&& error_str
[0] != '\0')
6628 show_oops(t
, "%s", error_str
);
6633 /* t is invalid at this point */
6635 g_free((gpointer
)u
);
6637 gdk_threads_leave();
6642 show_ca_status(struct tab
*t
, const char *uri
)
6645 gchar
*col_str
= XT_COLOR_WHITE
;
6647 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6648 ssl_strict_certs
, ssl_ca_file
, uri
);
6655 if (ssl_ca_file
== NULL
) {
6656 if (g_str_has_prefix(uri
, "http://"))
6658 if (g_str_has_prefix(uri
, "https://")) {
6659 col_str
= XT_COLOR_RED
;
6664 if (g_str_has_prefix(uri
, "http://") ||
6665 !g_str_has_prefix(uri
, "https://"))
6669 * It is not necessary to see if the thread is already running.
6670 * If the thread is in progress setting it to something else aborts it
6674 /* thread the coloring of the address bar */
6675 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
6677 color_address_bar(t
);
6683 gdk_color_parse(col_str
, &color
);
6684 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6686 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6687 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6689 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6694 free_favicon(struct tab
*t
)
6696 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6697 __func__
, t
->icon_download
, t
->icon_request
);
6699 if (t
->icon_request
)
6700 g_object_unref(t
->icon_request
);
6701 if (t
->icon_dest_uri
)
6702 g_free(t
->icon_dest_uri
);
6704 t
->icon_request
= NULL
;
6705 t
->icon_dest_uri
= NULL
;
6709 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6711 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
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
, "text-html");
6717 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6718 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6722 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6724 GdkPixbuf
*pb_scaled
;
6726 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6727 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6728 GDK_INTERP_BILINEAR
);
6732 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6733 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6735 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6736 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6738 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6739 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6741 if (pb_scaled
!= pb
)
6742 g_object_unref(pb_scaled
);
6746 xt_icon_from_file(struct tab
*t
, char *file
)
6750 if (g_str_has_prefix(file
, "file://"))
6751 file
+= strlen("file://");
6753 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6755 xt_icon_from_pixbuf(t
, pb
);
6758 xt_icon_from_name(t
, "text-html");
6762 is_valid_icon(char *file
)
6765 const char *mime_type
;
6769 gf
= g_file_new_for_path(file
);
6770 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6772 mime_type
= g_file_info_get_content_type(fi
);
6773 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6774 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6775 g_strcmp0(mime_type
, "image/png") == 0 ||
6776 g_strcmp0(mime_type
, "image/gif") == 0 ||
6777 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6785 set_favicon_from_file(struct tab
*t
, char *file
)
6789 if (t
== NULL
|| file
== NULL
)
6792 if (g_str_has_prefix(file
, "file://"))
6793 file
+= strlen("file://");
6794 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6796 if (!stat(file
, &sb
)) {
6797 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6798 /* corrupt icon so trash it */
6799 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6802 /* no need to set icon to default here */
6806 xt_icon_from_file(t
, file
);
6810 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6813 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6814 struct tab
*tt
= NULL
, *t
= NULL
;
6817 * find the webview instead of passing in the tab as it could have been
6818 * deleted from underneath us.
6820 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6829 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6830 __func__
, t
->tab_id
, status
);
6833 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6835 t
->icon_download
= NULL
;
6838 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6841 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6844 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6846 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6847 __func__
, t
->tab_id
);
6848 t
->icon_download
= NULL
;
6851 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6854 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6855 __func__
, t
->icon_dest_uri
);
6856 set_favicon_from_file(t
, t
->icon_dest_uri
);
6857 /* these will be freed post callback */
6858 t
->icon_request
= NULL
;
6859 t
->icon_download
= NULL
;
6867 abort_favicon_download(struct tab
*t
)
6869 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6871 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6872 if (t
->icon_download
) {
6873 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6874 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6875 webkit_download_cancel(t
->icon_download
);
6876 t
->icon_download
= NULL
;
6881 xt_icon_from_name(t
, "text-html");
6885 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6887 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6889 if (uri
== NULL
|| t
== NULL
)
6892 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6893 /* take icon from WebKitIconDatabase */
6896 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6898 xt_icon_from_pixbuf(t
, pb
);
6901 xt_icon_from_name(t
, "text-html");
6902 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6903 /* download icon to cache dir */
6904 gchar
*name_hash
, file
[PATH_MAX
];
6907 if (t
->icon_request
) {
6908 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6912 /* check to see if we got the icon in cache */
6913 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6914 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6917 if (!stat(file
, &sb
)) {
6918 if (sb
.st_size
> 0) {
6919 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6921 set_favicon_from_file(t
, file
);
6925 /* corrupt icon so trash it */
6926 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6931 /* create download for icon */
6932 t
->icon_request
= webkit_network_request_new(uri
);
6933 if (t
->icon_request
== NULL
) {
6934 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6939 t
->icon_download
= webkit_download_new(t
->icon_request
);
6940 if (t
->icon_download
== NULL
)
6943 /* we have to free icon_dest_uri later */
6944 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6945 webkit_download_set_destination_uri(t
->icon_download
,
6948 if (webkit_download_get_status(t
->icon_download
) ==
6949 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6950 g_object_unref(t
->icon_request
);
6951 g_free(t
->icon_dest_uri
);
6952 t
->icon_request
= NULL
;
6953 t
->icon_dest_uri
= NULL
;
6957 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6958 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6960 webkit_download_start(t
->icon_download
);
6965 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6967 const gchar
*uri
= NULL
, *title
= NULL
;
6968 struct history
*h
, find
;
6972 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6973 webkit_web_view_get_load_status(wview
),
6974 get_uri(t
) ? get_uri(t
) : "NOTHING");
6977 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6981 switch (webkit_web_view_get_load_status(wview
)) {
6982 case WEBKIT_LOAD_PROVISIONAL
:
6984 abort_favicon_download(t
);
6985 #if GTK_CHECK_VERSION(2, 20, 0)
6986 gtk_widget_show(t
->spinner
);
6987 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6989 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6991 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6993 /* assume we are a new address */
6994 gdk_color_parse("white", &color
);
6995 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6996 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6998 /* take focus if we are visible */
7004 /* kill color thread */
7009 case WEBKIT_LOAD_COMMITTED
:
7014 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
7020 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
7022 /* check if js white listing is enabled */
7023 if (enable_plugin_whitelist
)
7024 check_and_set_pl(uri
, t
);
7025 if (enable_cookie_whitelist
)
7026 check_and_set_cookie(uri
, t
);
7027 if (enable_js_whitelist
)
7028 check_and_set_js(uri
, t
);
7034 /* we know enough to autosave the session */
7035 if (session_autosave
) {
7040 show_ca_status(t
, uri
);
7043 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
7047 case WEBKIT_LOAD_FINISHED
:
7053 if (!strncmp(uri
, "http://", strlen("http://")) ||
7054 !strncmp(uri
, "https://", strlen("https://")) ||
7055 !strncmp(uri
, "file://", strlen("file://"))) {
7057 h
= RB_FIND(history_list
, &hl
, &find
);
7059 title
= get_title(t
, FALSE
);
7060 h
= g_malloc(sizeof *h
);
7061 h
->uri
= g_strdup(uri
);
7062 h
->title
= g_strdup(title
);
7063 RB_INSERT(history_list
, &hl
, h
);
7064 completion_add_uri(h
->uri
);
7065 update_history_tabs(NULL
);
7069 set_status(t
, (char *)uri
, XT_STATUS_URI
);
7070 #if WEBKIT_CHECK_VERSION(1, 1, 18)
7071 case WEBKIT_LOAD_FAILED
:
7074 #if GTK_CHECK_VERSION(2, 20, 0)
7075 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7076 gtk_widget_hide(t
->spinner
);
7079 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
7084 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
7086 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
7087 can_go_back_for_real(t
));
7089 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
7090 can_go_forward_for_real(t
));
7094 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
7096 const gchar
*title
= NULL
, *win_title
= NULL
;
7098 title
= get_title(t
, FALSE
);
7099 win_title
= get_title(t
, TRUE
);
7100 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
7101 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
7102 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
7103 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
7107 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7109 run_script(t
, JS_HINTING
);
7110 if (autofocus_onload
&&
7111 t
->tab_id
== gtk_notebook_get_current_page(notebook
))
7112 run_script(t
, "hints.focusInput();");
7114 run_script(t
, "hints.clearFocus();");
7118 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
7120 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
7121 progress
== 100 ? 0 : (double)progress
/ 100);
7122 if (show_url
== 0) {
7123 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
7124 progress
== 100 ? 0 : (double)progress
/ 100);
7127 update_statusbar_position(NULL
, NULL
);
7131 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
7132 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
7133 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
7136 WebKitWebNavigationReason reason
;
7137 struct domain
*d
= NULL
;
7140 show_oops(NULL
, "webview_npd_cb invalid parameters");
7144 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
7146 webkit_network_request_get_uri(request
));
7148 uri
= (char *)webkit_network_request_get_uri(request
);
7150 /* if this is an xtp url, we don't load anything else */
7151 if (parse_xtp_url(t
, uri
))
7154 if ((t
->hints_on
&& t
->new_tab
) || t
->ctrl_click
) {
7156 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
7157 webkit_web_policy_decision_ignore(pd
);
7158 return (TRUE
); /* we made the decission */
7162 * This is a little hairy but it comes down to this:
7163 * when we run in whitelist mode we have to assist the browser in
7164 * opening the URL that it would have opened in a new tab.
7166 reason
= webkit_web_navigation_action_get_reason(na
);
7167 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
7168 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7169 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
7170 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7172 webkit_web_policy_decision_use(pd
);
7173 return (TRUE
); /* we made the decision */
7180 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7183 struct domain
*d
= NULL
;
7185 WebKitWebView
*webview
= NULL
;
7187 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
7188 webkit_web_view_get_uri(wv
));
7191 /* open in current tab */
7193 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7194 uri
= webkit_web_view_get_uri(wv
);
7195 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7198 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7200 } else if (enable_scripts
== 1) {
7201 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7209 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
7212 struct domain
*d
= NULL
;
7214 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7216 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7217 uri
= webkit_web_view_get_uri(wv
);
7218 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7222 } else if (enable_scripts
== 1)
7229 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7231 /* we can not eat the event without throwing gtk off so defer it */
7233 /* catch middle click */
7234 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7239 /* catch ctrl click */
7240 if (e
->type
== GDK_BUTTON_RELEASE
&&
7241 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7246 return (XT_CB_PASSTHROUGH
);
7250 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7252 struct mime_type
*m
;
7254 m
= find_mime_type(mime_type
);
7262 show_oops(t
, "can't fork mime handler");
7272 execlp(m
->mt_action
, m
->mt_action
,
7273 webkit_network_request_get_uri(request
), (void *)NULL
);
7282 get_mime_type(const char *file
)
7285 char *mime_type
= NULL
;
7289 if (g_str_has_prefix(file
, "file://"))
7290 file
+= strlen("file://");
7292 gf
= g_file_new_for_path(file
);
7293 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7295 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
7296 mime_type
= g_strdup(m
);
7304 run_download_mimehandler(char *mime_type
, char *file
)
7306 struct mime_type
*m
;
7308 m
= find_mime_type(mime_type
);
7314 show_oops(NULL
, "can't fork download mime handler");
7324 if (g_str_has_prefix(file
, "file://"))
7325 file
+= strlen("file://");
7326 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7335 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7338 WebKitDownloadStatus status
;
7339 const char *file
= NULL
;
7342 if (download
== NULL
)
7344 status
= webkit_download_get_status(download
);
7345 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7348 file
= webkit_download_get_destination_uri(download
);
7351 mime
= get_mime_type(file
);
7355 run_download_mimehandler((char *)mime
, (char *)file
);
7360 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7361 WebKitNetworkRequest
*request
, char *mime_type
,
7362 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7365 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7369 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7370 t
->tab_id
, mime_type
);
7372 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7373 webkit_web_policy_decision_ignore(decision
);
7378 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7379 webkit_web_policy_decision_download(decision
);
7387 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7391 const gchar
*suggested_name
;
7392 gchar
*filename
= NULL
;
7394 struct download
*download_entry
;
7397 if (wk_download
== NULL
|| t
== NULL
) {
7398 show_oops(NULL
, "%s invalid parameters", __func__
);
7402 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7403 if (suggested_name
== NULL
)
7404 return (FALSE
); /* abort download */
7415 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7417 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7418 filename
: suggested_name
);
7420 } while (!stat(uri
+ strlen("file://"), &sb
));
7422 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7423 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7425 webkit_download_set_destination_uri(wk_download
, uri
);
7427 if (webkit_download_get_status(wk_download
) ==
7428 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7429 show_oops(t
, "%s: download failed to start", __func__
);
7431 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7433 /* connect "download first" mime handler */
7434 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7435 G_CALLBACK(download_status_changed_cb
), NULL
);
7437 download_entry
= g_malloc(sizeof(struct download
));
7438 download_entry
->download
= wk_download
;
7439 download_entry
->tab
= t
;
7440 download_entry
->id
= next_download_id
++;
7441 RB_INSERT(download_list
, &downloads
, download_entry
);
7442 /* get from history */
7443 g_object_ref(wk_download
);
7444 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7445 show_oops(t
, "Download of '%s' started...",
7446 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7455 /* sync other download manager tabs */
7456 update_download_tabs(NULL
);
7459 * NOTE: never redirect/render the current tab before this
7460 * function returns. This will cause the download to never start.
7462 return (ret
); /* start download */
7466 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7468 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7471 show_oops(NULL
, "webview_hover_cb");
7476 set_status(t
, uri
, XT_STATUS_LINK
);
7479 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7484 mark(struct tab
*t
, struct karg
*arg
)
7490 if ((index
= marktoindex(mark
)) == -1)
7493 if (arg
->i
== XT_MARK_SET
)
7494 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7495 else if (arg
->i
== XT_MARK_GOTO
) {
7496 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7497 show_oops(t
, "mark '%c' does not exist", mark
);
7500 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7501 something changes the document size */
7502 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7509 marks_clear(struct tab
*t
)
7513 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7514 t
->mark
[i
] = XT_INVALID_MARK
;
7520 char file
[PATH_MAX
];
7521 char *line
= NULL
, *p
;
7526 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7527 if ((f
= fopen(file
, "r+")) == NULL
) {
7528 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7532 for (i
= 1; ; i
++) {
7533 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7535 if (strlen(line
) == 0 || line
[0] == '#') {
7541 p
= strtok(line
, " \t");
7543 if (p
== NULL
|| strlen(p
) != 1 ||
7544 (index
= marktoindex(*p
)) == -1) {
7545 warnx("corrupt quickmarks file, line %d", i
);
7549 p
= strtok(NULL
, " \t");
7550 if (qmarks
[index
] != NULL
)
7551 g_free(qmarks
[index
]);
7552 qmarks
[index
] = g_strdup(p
);
7563 char file
[PATH_MAX
];
7567 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7568 if ((f
= fopen(file
, "r+")) == NULL
) {
7569 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7573 for (i
= 0; i
< XT_NOMARKS
; i
++)
7574 if (qmarks
[i
] != NULL
)
7575 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7583 qmark(struct tab
*t
, struct karg
*arg
)
7588 mark
= arg
->s
[strlen(arg
->s
)-1];
7589 index
= marktoindex(mark
);
7595 if (qmarks
[index
] != NULL
)
7596 g_free(qmarks
[index
]);
7598 qmarks_load(); /* sync if multiple instances */
7599 qmarks
[index
] = g_strdup(get_uri(t
));
7603 if (qmarks
[index
] != NULL
)
7604 load_uri(t
, qmarks
[index
]);
7606 show_oops(t
, "quickmark \"%c\" does not exist",
7612 if (qmarks
[index
] != NULL
)
7613 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7615 show_oops(t
, "quickmark \"%c\" does not exist",
7626 go_up(struct tab
*t
, struct karg
*args
)
7632 levels
= atoi(args
->s
);
7636 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7637 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7639 tmp
+= strlen(XT_PROTO_DELIM
);
7641 /* if an uri starts with a slash, leave it alone (for file:///) */
7648 p
= strrchr(tmp
, '/');
7662 gototab(struct tab
*t
, struct karg
*args
)
7665 struct karg arg
= {0, NULL
, -1};
7667 tab
= atoi(args
->s
);
7669 arg
.i
= XT_TAB_NEXT
;
7678 zoom_amount(struct tab
*t
, struct karg
*arg
)
7680 struct karg narg
= {0, NULL
, -1};
7682 narg
.i
= atoi(arg
->s
);
7683 resizetab(t
, &narg
);
7689 flip_colon(struct tab
*t
, struct karg
*arg
)
7691 struct karg narg
= {0, NULL
, -1};
7694 if (t
== NULL
|| arg
== NULL
)
7697 p
= strstr(arg
->s
, ":");
7709 /* buffer commands receive the regex that triggered them in arg.s */
7710 char bcmd
[XT_BUFCMD_SZ
];
7714 #define XT_PRE_NO (0)
7715 #define XT_PRE_YES (1)
7716 #define XT_PRE_MAYBE (2)
7718 int (*func
)(struct tab
*, struct karg
*);
7722 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7723 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7724 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7725 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7726 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7727 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7728 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7729 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7730 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7731 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7732 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7733 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7734 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7735 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7736 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7737 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7738 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7739 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7743 buffercmd_init(void)
7747 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7748 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7749 REG_EXTENDED
| REG_NOSUB
))
7750 startpage_add("invalid buffercmd regex %s",
7751 buffercmds
[i
].regex
);
7755 buffercmd_abort(struct tab
*t
)
7759 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7760 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7763 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7764 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7768 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7770 struct karg arg
= {0, NULL
, -1};
7773 arg
.s
= g_strdup(bcmd
);
7775 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7776 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7786 buffercmd_addkey(struct tab
*t
, guint keyval
)
7789 char s
[XT_BUFCMD_SZ
];
7791 if (keyval
== GDK_Escape
) {
7793 return (XT_CB_HANDLED
);
7796 /* key with modifier or non-ascii character */
7797 if (!isascii(keyval
))
7798 return (XT_CB_PASSTHROUGH
);
7800 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7801 "to buffer \"%s\"\n", keyval
, bcmd
);
7803 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7804 if (bcmd
[i
] == '\0') {
7809 /* buffer full, ignore input */
7810 if (i
>= LENGTH(bcmd
) -1) {
7811 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7813 return (XT_CB_HANDLED
);
7816 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7818 /* find exact match */
7819 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7820 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7821 (size_t) 0, NULL
, 0) == 0) {
7822 buffercmd_execute(t
, &buffercmds
[i
]);
7826 /* find non exact matches to see if we need to abort ot not */
7827 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7828 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7831 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7832 if (isdigit(bcmd
[0])) {
7833 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7837 if (sscanf(bcmd
, "%s", s
) == 0)
7840 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7841 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7844 if (sscanf(bcmd
, "%s", s
) == 0)
7847 if (c
== -1 && buffercmds
[i
].precount
)
7849 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7852 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7853 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7856 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7861 return (XT_CB_HANDLED
);
7865 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7867 struct key_binding
*k
;
7869 /* handle keybindings if buffercmd is empty.
7870 if not empty, allow commands like C-n */
7871 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7872 TAILQ_FOREACH(k
, &kbl
, entry
)
7873 if (e
->keyval
== k
->key
7874 && (entry
? k
->use_in_entry
: 1)) {
7876 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7877 return (cmd_execute(t
, k
->cmd
));
7878 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7879 return (cmd_execute(t
, k
->cmd
));
7883 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7884 return buffercmd_addkey(t
, e
->keyval
);
7886 return (XT_CB_PASSTHROUGH
);
7890 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7894 /* don't use w directly; use t->whatever instead */
7897 show_oops(NULL
, "wv_keypress_after_cb");
7898 return (XT_CB_PASSTHROUGH
);
7901 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7902 e
->keyval
, e
->state
, t
);
7905 /* XXX make sure cmd entry is enabled */
7906 return (XT_CB_HANDLED
);
7909 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7910 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7911 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7914 return (handle_keypress(t
, e
, 0));
7918 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7922 /* Hide buffers, if they are visible, with escape. */
7923 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7924 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7925 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7927 return (XT_CB_HANDLED
);
7930 return (XT_CB_PASSTHROUGH
);
7934 hint_continue(struct tab
*t
)
7936 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7938 const gchar
*errstr
= NULL
;
7942 if (!(c
[0] == '.' || c
[0] == ','))
7944 if (strlen(c
) == 1) {
7945 /* XXX should not happen */
7950 if (isdigit(c
[1])) {
7952 i
= strtonum(&c
[1], 1, 4096, &errstr
);
7954 show_oops(t
, "invalid numerical hint %s", &c
[1]);
7957 s
= g_strdup_printf("hints.updateHints(%lld);", i
);
7961 /* alphanumeric input */
7962 s
= g_strdup_printf("hints.createHints('%s', '%c');",
7963 &c
[1], c
[0] == '.' ? 'f' : 'F');
7974 search_continue(struct tab
*t
)
7976 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7977 gboolean rv
= FALSE
;
7979 if (c
[0] == ':' || c
[0] == '.' || c
[0] == ',')
7981 if (strlen(c
) == 1) {
7982 webkit_web_view_unmark_text_matches(t
->wv
);
7987 t
->search_forward
= TRUE
;
7988 else if (c
[0] == '?')
7989 t
->search_forward
= FALSE
;
7999 search_cb(struct tab
*t
)
8001 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
8004 if (search_continue(t
) == FALSE
)
8008 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
8010 /* not found, mark red */
8011 gdk_color_parse(XT_COLOR_RED
, &color
);
8012 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8013 /* unmark and remove selection */
8014 webkit_web_view_unmark_text_matches(t
->wv
);
8015 /* my kingdom for a way to unselect text in webview */
8017 /* found, highlight all */
8018 webkit_web_view_unmark_text_matches(t
->wv
);
8019 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
8020 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
8021 gdk_color_parse(XT_COLOR_WHITE
, &color
);
8022 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8030 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8032 const gchar
*c
= gtk_entry_get_text(w
);
8035 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
8036 return (XT_CB_PASSTHROUGH
);
8039 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
8040 e
->keyval
, e
->state
, t
);
8043 if (!(e
->keyval
== GDK_Tab
|| e
->keyval
== GDK_ISO_Left_Tab
)) {
8044 if (hint_continue(t
) == FALSE
)
8049 if (search_continue(t
) == FALSE
)
8052 /* if search length is > 4 then no longer play timeout games */
8053 if (strlen(c
) > 4) {
8055 g_source_remove(t
->search_id
);
8062 /* reestablish a new timer if the user types fast */
8064 g_source_remove(t
->search_id
);
8065 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
8068 return (XT_CB_PASSTHROUGH
);
8072 match_uri(const gchar
*uri
, const gchar
*key
) {
8075 gboolean match
= FALSE
;
8079 if (!strncmp(key
, uri
, len
))
8082 voffset
= strstr(uri
, "/") + 2;
8083 if (!strncmp(key
, voffset
, len
))
8085 else if (g_str_has_prefix(voffset
, "www.")) {
8086 voffset
= voffset
+ strlen("www.");
8087 if (!strncmp(key
, voffset
, len
))
8096 match_session(const gchar
*name
, const gchar
*key
) {
8099 sub
= strcasestr(name
, key
);
8105 cmd_getlist(int id
, char *key
)
8112 if (cmds
[id
].type
& XT_URLARG
) {
8113 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
8114 if (match_uri(h
->uri
, key
)) {
8115 cmd_status
.list
[c
] = (char *)h
->uri
;
8121 } else if (cmds
[id
].type
& XT_SESSARG
) {
8122 TAILQ_FOREACH(s
, &sessions
, entry
)
8123 if (match_session(s
->name
, key
)) {
8124 cmd_status
.list
[c
] = (char *)s
->name
;
8130 } else if (cmds
[id
].type
& XT_SETARG
) {
8131 for (i
= 0; i
< LENGTH(rs
); i
++)
8132 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
8133 cmd_status
.list
[c
++] = rs
[i
].name
;
8139 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
8141 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
8142 if (cmds
[i
].level
< dep
)
8144 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
8145 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
8146 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
8154 cmd_getnext(int dir
)
8156 cmd_status
.index
+= dir
;
8158 if (cmd_status
.index
< 0)
8159 cmd_status
.index
= cmd_status
.len
- 1;
8160 else if (cmd_status
.index
>= cmd_status
.len
)
8161 cmd_status
.index
= 0;
8163 return cmd_status
.list
[cmd_status
.index
];
8167 cmd_tokenize(char *s
, char *tokens
[])
8170 char *tok
, *last
= NULL
;
8171 size_t len
= strlen(s
);
8174 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8175 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8176 tok
= strtok_r(NULL
, " ", &last
), i
++)
8186 cmd_complete(struct tab
*t
, char *str
, int dir
)
8188 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8189 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8191 char *tok
, *match
, *s
= g_strdup(str
);
8193 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8196 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8199 for (i
= 0; isdigit(s
[i
]); i
++)
8202 for (; isspace(s
[i
]); i
++)
8207 levels
= cmd_tokenize(s
, tokens
);
8209 for (i
= 0; i
< levels
- 1; i
++) {
8212 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8213 if (cmds
[j
].level
< dep
)
8215 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8219 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8226 if (matchcount
== 1) {
8227 strlcat(res
, tok
, sizeof res
);
8228 strlcat(res
, " ", sizeof res
);
8238 if (cmd_status
.index
== -1)
8239 cmd_getlist(parent
, tokens
[i
]);
8241 if (cmd_status
.len
> 0) {
8242 match
= cmd_getnext(dir
);
8243 strlcat(res
, match
, sizeof res
);
8244 gtk_entry_set_text(w
, res
);
8245 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8252 cmd_execute(struct tab
*t
, char *str
)
8254 struct cmd
*cmd
= NULL
;
8255 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
8257 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8258 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8259 struct karg arg
= {0, NULL
, -1};
8264 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8270 while (isspace(s
[0]))
8273 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8274 prefix
= atoi(prefixstr
);
8278 for (tok
= strtok_r(s
, " ", &last
); tok
;
8279 tok
= strtok_r(NULL
, " ", &last
)) {
8281 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8282 if (cmds
[j
].level
< dep
)
8284 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8286 if (cmds
[j
].level
== dep
&&
8287 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8291 if (len
== strlen(cmds
[j
].cmd
)) {
8297 if (matchcount
== 1) {
8302 show_oops(t
, "Invalid command: %s", str
);
8308 show_oops(t
, "Empty command");
8314 arg
.precount
= prefix
;
8315 else if (cmd_prefix
> 0)
8316 arg
.precount
= cmd_prefix
;
8318 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8319 show_oops(t
, "No prefix allowed: %s", str
);
8323 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8324 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8325 if (arg
.s
== NULL
) {
8326 show_oops(t
, "Invalid command");
8329 arg
.precount
= atoi(arg
.s
);
8330 if (arg
.precount
<= 0) {
8331 if (arg
.s
[0] == '0')
8332 show_oops(t
, "Zero count");
8334 show_oops(t
, "Trailing characters");
8339 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8340 __func__
, arg
.precount
, arg
.s
);
8356 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8359 show_oops(NULL
, "entry_key_cb invalid parameters");
8360 return (XT_CB_PASSTHROUGH
);
8363 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8364 e
->keyval
, e
->state
, t
);
8368 if (e
->keyval
== GDK_Escape
) {
8369 /* don't use focus_webview(t) because we want to type :cmds */
8370 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8373 return (handle_keypress(t
, e
, 1));
8376 struct command_entry
*
8377 history_prev(struct command_list
*l
, struct command_entry
*at
)
8380 at
= TAILQ_LAST(l
, command_list
);
8382 at
= TAILQ_PREV(at
, command_list
, entry
);
8384 at
= TAILQ_LAST(l
, command_list
);
8390 struct command_entry
*
8391 history_next(struct command_list
*l
, struct command_entry
*at
)
8394 at
= TAILQ_FIRST(l
);
8396 at
= TAILQ_NEXT(at
, entry
);
8398 at
= TAILQ_FIRST(l
);
8405 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8407 int rv
= XT_CB_HANDLED
;
8408 const gchar
*c
= gtk_entry_get_text(w
);
8412 show_oops(NULL
, "cmd_keypress_cb parameters");
8413 return (XT_CB_PASSTHROUGH
);
8416 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8417 e
->keyval
, e
->state
, t
);
8421 e
->keyval
= GDK_Escape
;
8422 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?' ||
8423 c
[0] == '.' || c
[0] == ','))
8424 e
->keyval
= GDK_Escape
;
8426 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8427 e
->keyval
!= GDK_ISO_Left_Tab
)
8428 cmd_status
.index
= -1;
8430 switch (e
->keyval
) {
8433 cmd_complete(t
, (char *)&c
[1], 1);
8434 else if (c
[0] == '.' || c
[0] == ',')
8435 run_script(t
, "hints.focusNextHint();");
8437 case GDK_ISO_Left_Tab
:
8439 cmd_complete(t
, (char *)&c
[1], -1);
8440 else if (c
[0] == '.' || c
[0] == ',')
8441 run_script(t
, "hints.focusPreviousHint();");
8445 if ((search_at
= history_next(&shl
, search_at
))) {
8446 search_at
->line
[0] = c
[0];
8447 gtk_entry_set_text(w
, search_at
->line
);
8448 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8450 } else if (c
[0] == '/') {
8451 if ((search_at
= history_prev(&chl
, search_at
))) {
8452 search_at
->line
[0] = c
[0];
8453 gtk_entry_set_text(w
, search_at
->line
);
8454 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8456 } if (c
[0] == ':') {
8457 if ((history_at
= history_prev(&chl
, history_at
))) {
8458 history_at
->line
[0] = c
[0];
8459 gtk_entry_set_text(w
, history_at
->line
);
8460 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8466 if ((search_at
= history_next(&shl
, search_at
))) {
8467 search_at
->line
[0] = c
[0];
8468 gtk_entry_set_text(w
, search_at
->line
);
8469 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8471 } else if (c
[0] == '?') {
8472 if ((search_at
= history_prev(&chl
, search_at
))) {
8473 search_at
->line
[0] = c
[0];
8474 gtk_entry_set_text(w
, search_at
->line
);
8475 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8477 } if (c
[0] == ':') {
8478 if ((history_at
= history_next(&chl
, history_at
))) {
8479 history_at
->line
[0] = c
[0];
8480 gtk_entry_set_text(w
, history_at
->line
);
8481 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8486 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?") ||
8487 !strcmp(c
, ".") || !strcmp(c
, ","))) {
8488 /* see if we are doing hinting and reset it */
8489 if (c
[0] == '.' || c
[0] == ',') {
8490 /* recreate hints */
8491 s
= g_strdup_printf("hints.createHints('', "
8492 "'%c');", c
[0] == '.' ? 'f' : 'F');
8505 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8506 webkit_web_view_unmark_text_matches(t
->wv
);
8508 /* no need to cancel hints */
8512 rv
= XT_CB_PASSTHROUGH
;
8518 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8520 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8524 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8526 /* popup menu enabled */
8531 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8534 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8535 return (XT_CB_PASSTHROUGH
);
8538 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8539 t
->tab_id
, t
->popup
);
8541 /* if popup is enabled don't lose focus */
8544 return (XT_CB_PASSTHROUGH
);
8551 if (show_url
== 0 || t
->focus_wv
)
8554 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8556 return (XT_CB_PASSTHROUGH
);
8560 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8563 const gchar
*c
= gtk_entry_get_text(entry
);
8566 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8570 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8576 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?' ||
8577 c
[0] == '.' || c
[0] == ','))
8583 if (c
[0] == '/' || c
[0] == '?') {
8584 /* see if there is a timer pending */
8586 g_source_remove(t
->search_id
);
8591 if (t
->search_text
) {
8592 g_free(t
->search_text
);
8593 t
->search_text
= NULL
;
8596 t
->search_text
= g_strdup(s
);
8598 g_free(global_search
);
8599 global_search
= g_strdup(s
);
8600 t
->search_forward
= c
[0] == '/';
8602 history_add(&shl
, search_file
, s
, &search_history_count
);
8604 } else if (c
[0] == '.' || c
[0] == ',') {
8605 run_script(t
, "hints.fire();");
8606 /* XXX history for link following? */
8610 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8617 backward_cb(GtkWidget
*w
, struct tab
*t
)
8622 show_oops(NULL
, "backward_cb invalid parameters");
8626 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8633 forward_cb(GtkWidget
*w
, struct tab
*t
)
8638 show_oops(NULL
, "forward_cb invalid parameters");
8642 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8644 a
.i
= XT_NAV_FORWARD
;
8649 home_cb(GtkWidget
*w
, struct tab
*t
)
8652 show_oops(NULL
, "home_cb invalid parameters");
8656 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8662 stop_cb(GtkWidget
*w
, struct tab
*t
)
8664 WebKitWebFrame
*frame
;
8667 show_oops(NULL
, "stop_cb invalid parameters");
8671 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8673 frame
= webkit_web_view_get_main_frame(t
->wv
);
8674 if (frame
== NULL
) {
8675 show_oops(t
, "stop_cb: no frame");
8679 webkit_web_frame_stop_loading(frame
);
8680 abort_favicon_download(t
);
8684 setup_webkit(struct tab
*t
)
8686 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8687 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8688 FALSE
, (char *)NULL
);
8690 warnx("webkit does not have \"enable-dns-prefetching\" property");
8691 g_object_set(G_OBJECT(t
->settings
),
8692 "user-agent", t
->user_agent
, (char *)NULL
);
8693 g_object_set(G_OBJECT(t
->settings
),
8694 "enable-scripts", enable_scripts
, (char *)NULL
);
8695 g_object_set(G_OBJECT(t
->settings
),
8696 "enable-plugins", enable_plugins
, (char *)NULL
);
8697 g_object_set(G_OBJECT(t
->settings
),
8698 "javascript-can-open-windows-automatically", enable_scripts
,
8700 g_object_set(G_OBJECT(t
->settings
),
8701 "enable-html5-database", FALSE
, (char *)NULL
);
8702 g_object_set(G_OBJECT(t
->settings
),
8703 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8704 g_object_set(G_OBJECT(t
->settings
),
8705 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8706 g_object_set(G_OBJECT(t
->settings
),
8707 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8708 g_object_set(G_OBJECT(t
->settings
),
8709 "enable-developer-extras", TRUE
, (char *)NULL
);
8710 g_object_set(G_OBJECT(t
->wv
),
8711 "full-content-zoom", TRUE
, (char *)NULL
);
8713 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8717 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8719 struct tab
*ti
, *t
= NULL
;
8720 gdouble view_size
, value
, max
;
8723 TAILQ_FOREACH(ti
, &tabs
, entry
)
8724 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8732 if (adjustment
== NULL
)
8733 adjustment
= gtk_scrolled_window_get_vadjustment(
8734 GTK_SCROLLED_WINDOW(t
->browser_win
));
8736 view_size
= gtk_adjustment_get_page_size(adjustment
);
8737 value
= gtk_adjustment_get_value(adjustment
);
8738 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8741 position
= g_strdup("All");
8742 else if (value
== max
)
8743 position
= g_strdup("Bot");
8744 else if (value
== 0)
8745 position
= g_strdup("Top");
8747 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8749 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8756 create_window(const gchar
*name
)
8760 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8761 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8762 gtk_widget_set_name(w
, name
);
8763 gtk_window_set_wmclass(GTK_WINDOW(w
), name
, "XXXTerm");
8769 create_browser(struct tab
*t
)
8773 GtkAdjustment
*adjustment
;
8776 show_oops(NULL
, "create_browser invalid parameters");
8780 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8781 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8782 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8783 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8785 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8786 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8787 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8789 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8790 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8793 t
->settings
= webkit_web_settings_new();
8795 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
8797 if (user_agent
== NULL
) {
8798 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8800 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8803 t
->user_agent
= g_strdup(user_agent
);
8805 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8808 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8809 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8810 G_CALLBACK(update_statusbar_position
), NULL
);
8819 create_kiosk_toolbar(struct tab
*t
)
8821 GtkWidget
*toolbar
= NULL
, *b
;
8823 b
= gtk_hbox_new(FALSE
, 0);
8825 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8827 /* backward button */
8828 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8829 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8830 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8831 G_CALLBACK(backward_cb
), t
);
8832 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8834 /* forward button */
8835 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8836 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8837 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8838 G_CALLBACK(forward_cb
), t
);
8839 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8842 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8843 gtk_widget_set_sensitive(t
->gohome
, true);
8844 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8845 G_CALLBACK(home_cb
), t
);
8846 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8848 /* create widgets but don't use them */
8849 t
->uri_entry
= gtk_entry_new();
8850 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8851 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8852 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8858 create_toolbar(struct tab
*t
)
8860 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8862 b
= gtk_hbox_new(FALSE
, 0);
8864 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8866 /* backward button */
8867 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8868 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8869 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8870 G_CALLBACK(backward_cb
), t
);
8871 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8873 /* forward button */
8874 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8875 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8876 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8877 G_CALLBACK(forward_cb
), t
);
8878 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8882 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8883 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8884 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8885 G_CALLBACK(stop_cb
), t
);
8886 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8890 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8891 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8892 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8893 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8894 G_CALLBACK(js_toggle_cb
), t
);
8895 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8897 t
->uri_entry
= gtk_entry_new();
8898 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8899 G_CALLBACK(activate_uri_entry_cb
), t
);
8900 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8901 G_CALLBACK(entry_key_cb
), t
);
8903 eb1
= gtk_hbox_new(FALSE
, 0);
8904 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8905 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8906 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8909 if (search_string
) {
8911 t
->search_entry
= gtk_entry_new();
8912 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8913 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8914 G_CALLBACK(activate_search_entry_cb
), t
);
8915 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8916 G_CALLBACK(entry_key_cb
), t
);
8917 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8918 eb2
= gtk_hbox_new(FALSE
, 0);
8919 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8920 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8922 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8929 create_buffers(struct tab
*t
)
8931 GtkCellRenderer
*renderer
;
8934 view
= gtk_tree_view_new();
8936 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8938 renderer
= gtk_cell_renderer_text_new();
8939 gtk_tree_view_insert_column_with_attributes
8940 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8942 renderer
= gtk_cell_renderer_text_new();
8943 gtk_tree_view_insert_column_with_attributes
8944 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8947 gtk_tree_view_set_model
8948 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8954 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8955 GtkTreeViewColumn
*col
, struct tab
*t
)
8960 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8962 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8965 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8966 set_current_tab(id
- 1);
8972 /* after tab reordering/creation/removal */
8979 TAILQ_FOREACH(t
, &tabs
, entry
) {
8980 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8981 if (t
->tab_id
> maxid
)
8984 gtk_widget_show(t
->tab_elems
.sep
);
8987 TAILQ_FOREACH(t
, &tabs
, entry
) {
8988 if (t
->tab_id
== maxid
) {
8989 gtk_widget_hide(t
->tab_elems
.sep
);
8995 /* after active tab change */
8997 recolor_compact_tabs(void)
9003 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9004 TAILQ_FOREACH(t
, &tabs
, entry
)
9005 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
9008 curid
= gtk_notebook_get_current_page(notebook
);
9009 TAILQ_FOREACH(t
, &tabs
, entry
)
9010 if (t
->tab_id
== curid
) {
9011 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
9012 gtk_widget_modify_fg(t
->tab_elems
.label
,
9013 GTK_STATE_NORMAL
, &color
);
9019 set_current_tab(int page_num
)
9021 buffercmd_abort(get_current_tab());
9022 gtk_notebook_set_current_page(notebook
, page_num
);
9023 recolor_compact_tabs();
9027 undo_close_tab_save(struct tab
*t
)
9031 struct undo
*u1
, *u2
;
9033 WebKitWebHistoryItem
*item
;
9035 if ((uri
= get_uri(t
)) == NULL
)
9038 u1
= g_malloc0(sizeof(struct undo
));
9039 u1
->uri
= g_strdup(uri
);
9041 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9043 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
9044 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
9047 /* forward history */
9048 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
9052 u1
->history
= g_list_prepend(u1
->history
,
9053 webkit_web_history_item_copy(item
));
9054 items
= g_list_next(items
);
9059 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
9060 u1
->history
= g_list_prepend(u1
->history
,
9061 webkit_web_history_item_copy(item
));
9065 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
9069 u1
->history
= g_list_prepend(u1
->history
,
9070 webkit_web_history_item_copy(item
));
9071 items
= g_list_next(items
);
9074 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
9076 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
9077 u2
= TAILQ_LAST(&undos
, undo_tailq
);
9078 TAILQ_REMOVE(&undos
, u2
, entry
);
9080 g_list_free(u2
->history
);
9089 delete_tab(struct tab
*t
)
9093 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
9099 * no need to join thread here because it won't access t on completion
9102 TAILQ_REMOVE(&tabs
, t
, entry
);
9105 /* Halt all webkit activity. */
9106 abort_favicon_download(t
);
9107 webkit_web_view_stop_loading(t
->wv
);
9109 /* Save the tab, so we can undo the close. */
9110 undo_close_tab_save(t
);
9114 g_source_remove(t
->search_id
);
9116 if (browser_mode
== XT_BM_KIOSK
) {
9117 gtk_widget_destroy(t
->uri_entry
);
9118 gtk_widget_destroy(t
->stop
);
9119 gtk_widget_destroy(t
->js_toggle
);
9122 gtk_widget_destroy(t
->tab_elems
.eventbox
);
9123 gtk_widget_destroy(t
->vbox
);
9125 g_free(t
->user_agent
);
9126 g_free(t
->stylesheet
);
9131 if (TAILQ_EMPTY(&tabs
)) {
9132 if (browser_mode
== XT_BM_KIOSK
)
9133 create_new_tab(home
, NULL
, 1, -1);
9135 create_new_tab(NULL
, NULL
, 1, -1);
9138 /* recreate session */
9139 if (session_autosave
) {
9141 save_tabs(NULL
, &a
);
9145 recolor_compact_tabs();
9149 update_statusbar_zoom(struct tab
*t
)
9152 char s
[16] = { '\0' };
9154 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9155 if ((zoom
<= 0.99 || zoom
>= 1.01))
9156 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
9157 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
9161 setzoom_webkit(struct tab
*t
, int adjust
)
9163 #define XT_ZOOMPERCENT 0.04
9168 show_oops(NULL
, "setzoom_webkit invalid parameters");
9172 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9173 if (adjust
== XT_ZOOM_IN
)
9174 zoom
+= XT_ZOOMPERCENT
;
9175 else if (adjust
== XT_ZOOM_OUT
)
9176 zoom
-= XT_ZOOMPERCENT
;
9177 else if (adjust
> 0)
9178 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
9180 show_oops(t
, "setzoom_webkit invalid zoom value");
9184 if (zoom
< XT_ZOOMPERCENT
)
9185 zoom
= XT_ZOOMPERCENT
;
9186 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
9187 update_statusbar_zoom(t
);
9191 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
9193 struct tab
*t
= (struct tab
*) data
;
9195 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
9197 switch (event
->button
) {
9199 set_current_tab(t
->tab_id
);
9210 append_tab(struct tab
*t
)
9215 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9216 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9220 create_sbe(int width
)
9224 sbe
= gtk_entry_new();
9225 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9226 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9227 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9228 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9229 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9230 gtk_widget_set_size_request(sbe
, width
, -1);
9236 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9241 WebKitWebHistoryItem
*item
;
9245 int sbe_p
= 0, sbe_b
= 0,
9248 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9250 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9251 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9255 t
= g_malloc0(sizeof *t
);
9257 if (title
== NULL
) {
9258 title
= "(untitled)";
9262 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9264 /* label + button for tab */
9265 b
= gtk_hbox_new(FALSE
, 0);
9268 #if GTK_CHECK_VERSION(2, 20, 0)
9269 t
->spinner
= gtk_spinner_new();
9271 t
->label
= gtk_label_new(title
);
9272 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9273 gtk_widget_set_size_request(t
->label
, 100, 0);
9274 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9275 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9276 gtk_widget_set_size_request(b
, 130, 0);
9278 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9279 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9280 #if GTK_CHECK_VERSION(2, 20, 0)
9281 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9285 if (browser_mode
== XT_BM_KIOSK
) {
9286 t
->toolbar
= create_kiosk_toolbar(t
);
9287 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9290 t
->toolbar
= create_toolbar(t
);
9292 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9300 t
->browser_win
= create_browser(t
);
9301 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9303 /* oops message for user feedback */
9304 t
->oops
= gtk_entry_new();
9305 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9306 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9307 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9308 gdk_color_parse(XT_COLOR_RED
, &color
);
9309 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9310 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9311 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9314 t
->cmd
= gtk_entry_new();
9315 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9316 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9317 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9318 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9321 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9323 t
->sbe
.statusbar
= gtk_entry_new();
9324 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9325 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9326 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9327 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9329 /* create these widgets only if specified in statusbar_elems */
9331 t
->sbe
.position
= create_sbe(40);
9332 t
->sbe
.zoom
= create_sbe(40);
9333 t
->sbe
.buffercmd
= create_sbe(60);
9335 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9337 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9340 /* gtk widgets cannot be added to a box twice. sbe_* variables
9341 make sure of this */
9342 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9346 GtkWidget
*sep
= gtk_vseparator_new();
9348 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9349 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9350 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9351 FALSE
, FALSE
, FALSE
);
9356 warnx("flag \"%c\" specified more than "
9357 "once in statusbar_elems\n", *p
);
9361 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9362 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9366 warnx("flag \"%c\" specified more than "
9367 "once in statusbar_elems\n", *p
);
9371 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9372 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9376 warnx("flag \"%c\" specified more than "
9377 "once in statusbar_elems\n", *p
);
9381 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9382 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9385 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9390 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9393 t
->buffers
= create_buffers(t
);
9394 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9396 /* xtp meaning is normal by default */
9397 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9399 /* set empty favicon */
9400 xt_icon_from_name(t
, "text-html");
9402 /* and show it all */
9403 gtk_widget_show_all(b
);
9404 gtk_widget_show_all(t
->vbox
);
9406 /* compact tab bar */
9407 t
->tab_elems
.label
= gtk_label_new(title
);
9408 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9409 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9410 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9411 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9413 t
->tab_elems
.eventbox
= gtk_event_box_new();
9414 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9415 t
->tab_elems
.sep
= gtk_vseparator_new();
9417 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9418 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9419 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9420 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9421 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9422 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9424 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9426 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9428 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9431 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9433 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9435 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9438 id
= position
>= 0 ? position
:
9439 gtk_notebook_get_current_page(notebook
) + 1;
9440 if (id
> gtk_notebook_get_n_pages(notebook
))
9443 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9444 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9445 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9446 t
->tab_elems
.eventbox
, id
);
9451 #if GTK_CHECK_VERSION(2, 20, 0)
9452 /* turn spinner off if we are a new tab without uri */
9454 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9455 gtk_widget_hide(t
->spinner
);
9458 /* make notebook tabs reorderable */
9459 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9461 /* compact tabs clickable */
9462 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9463 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9465 g_object_connect(G_OBJECT(t
->cmd
),
9466 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9467 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9468 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9469 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9470 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9473 /* reuse wv_button_cb to hide oops */
9474 g_object_connect(G_OBJECT(t
->oops
),
9475 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9478 g_signal_connect(t
->buffers
,
9479 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9480 g_object_connect(G_OBJECT(t
->buffers
),
9481 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9483 g_object_connect(G_OBJECT(t
->wv
),
9484 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9485 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9486 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9487 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9488 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9489 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9490 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9491 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9492 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9493 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9494 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9495 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9496 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9497 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9498 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9499 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9501 g_signal_connect(t
->wv
,
9502 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9503 g_signal_connect(t
->wv
,
9504 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9506 /* hijack the unused keys as if we were the browser */
9507 g_object_connect(G_OBJECT(t
->toolbar
),
9508 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9511 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9512 G_CALLBACK(tab_close_cb
), t
);
9515 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9516 /* restore the tab's history */
9517 if (u
&& u
->history
) {
9521 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9522 items
= g_list_next(items
);
9525 item
= g_list_nth_data(u
->history
, u
->back
);
9527 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9530 g_list_free(u
->history
);
9532 webkit_web_back_forward_list_clear(t
->bfl
);
9538 url_set_visibility();
9539 statusbar_set_visibility();
9542 set_current_tab(t
->tab_id
);
9543 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9547 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9551 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9558 recolor_compact_tabs();
9559 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9564 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9570 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9572 if (gtk_notebook_get_current_page(notebook
) == -1)
9575 TAILQ_FOREACH(t
, &tabs
, entry
) {
9576 if (t
->tab_id
== pn
) {
9577 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9580 uri
= get_title(t
, TRUE
);
9581 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9587 /* can't use focus_webview here */
9588 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9595 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9598 struct tab
*t
= NULL
, *tt
;
9602 TAILQ_FOREACH(tt
, &tabs
, entry
)
9603 if (tt
->tab_id
== pn
) {
9609 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9611 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9616 menuitem_response(struct tab
*t
)
9618 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9622 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9624 GtkWidget
*menu
, *menu_items
;
9625 GdkEventButton
*bevent
;
9629 if (event
->type
== GDK_BUTTON_PRESS
) {
9630 bevent
= (GdkEventButton
*) event
;
9631 menu
= gtk_menu_new();
9633 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9634 if ((uri
= get_uri(ti
)) == NULL
)
9635 /* XXX make sure there is something to print */
9636 /* XXX add gui pages in here to look purdy */
9638 menu_items
= gtk_menu_item_new_with_label(uri
);
9639 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9640 gtk_widget_show(menu_items
);
9642 g_signal_connect_swapped((menu_items
),
9643 "activate", G_CALLBACK(menuitem_response
),
9647 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9648 bevent
->button
, bevent
->time
);
9650 /* unref object so it'll free itself when popped down */
9651 #if !GTK_CHECK_VERSION(3, 0, 0)
9652 /* XXX does not need unref with gtk+3? */
9653 g_object_ref_sink(menu
);
9654 g_object_unref(menu
);
9657 return (TRUE
/* eat event */);
9660 return (FALSE
/* propagate */);
9664 icon_size_map(int icon_size
)
9666 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9667 icon_size
> GTK_ICON_SIZE_DIALOG
)
9668 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9674 create_button(char *name
, char *stockid
, int size
)
9676 GtkWidget
*button
, *image
;
9680 rcstring
= g_strdup_printf(
9681 "style \"%s-style\"\n"
9683 " GtkWidget::focus-padding = 0\n"
9684 " GtkWidget::focus-line-width = 0\n"
9688 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9689 gtk_rc_parse_string(rcstring
);
9691 button
= gtk_button_new();
9692 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9693 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9695 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9696 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9697 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9698 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9699 gtk_widget_set_name(button
, name
);
9700 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9706 button_set_stockid(GtkWidget
*button
, char *stockid
)
9710 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9711 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9712 gtk_button_set_image(GTK_BUTTON(button
), image
);
9716 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9719 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9722 if (xterm_workaround
== 0)
9726 * xterm doesn't play nice with clipboards because it clears the
9727 * primary when clicked. We rely on primary being set to properly
9728 * handle middle mouse button clicks (paste). So when someone clears
9729 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9730 * other application behavior (as in DON'T clear primary).
9733 p
= gtk_clipboard_wait_for_text(primary
);
9735 if (gdk_property_get(gdk_get_default_root_window(),
9737 gdk_atom_intern("STRING", FALSE
),
9739 1024 * 1024 /* picked out of my butt */,
9745 /* yes sir, we need to NUL the string */
9747 gtk_clipboard_set_text(primary
, p
, -1);
9761 char file
[PATH_MAX
];
9764 vbox
= gtk_vbox_new(FALSE
, 0);
9765 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9766 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9767 #if !GTK_CHECK_VERSION(3, 0, 0)
9768 /* XXX seems to be needed with gtk+2 */
9769 gtk_notebook_set_tab_hborder(notebook
, 0);
9770 gtk_notebook_set_tab_vborder(notebook
, 0);
9772 gtk_notebook_set_scrollable(notebook
, TRUE
);
9773 gtk_notebook_set_show_border(notebook
, FALSE
);
9774 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9776 abtn
= gtk_button_new();
9777 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9778 gtk_widget_set_size_request(arrow
, -1, -1);
9779 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9780 gtk_widget_set_size_request(abtn
, -1, 20);
9782 #if GTK_CHECK_VERSION(2, 20, 0)
9783 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9785 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9787 /* compact tab bar */
9788 tab_bar
= gtk_hbox_new(TRUE
, 0);
9790 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9791 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9792 gtk_widget_set_size_request(vbox
, -1, -1);
9794 g_object_connect(G_OBJECT(notebook
),
9795 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9797 g_object_connect(G_OBJECT(notebook
),
9798 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9799 NULL
, (char *)NULL
);
9800 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9801 G_CALLBACK(arrow_cb
), NULL
);
9803 main_window
= create_window("xxxterm");
9804 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9805 g_signal_connect(G_OBJECT(main_window
), "delete_event",
9806 G_CALLBACK(gtk_main_quit
), NULL
);
9809 for (i
= 0; i
< LENGTH(icons
); i
++) {
9810 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9811 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9812 l
= g_list_append(l
, pb
);
9814 gtk_window_set_default_icon_list(l
);
9816 /* clipboard work around */
9817 if (xterm_workaround
)
9819 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9820 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9822 gtk_widget_show_all(abtn
);
9823 gtk_widget_show_all(main_window
);
9824 notebook_tab_set_visibility();
9828 set_hook(void **hook
, char *name
)
9831 errx(1, "set_hook");
9833 if (*hook
== NULL
) {
9834 *hook
= dlsym(RTLD_NEXT
, name
);
9836 errx(1, "can't hook %s", name
);
9840 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9842 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9844 g_return_val_if_fail(cookie1
, FALSE
);
9845 g_return_val_if_fail(cookie2
, FALSE
);
9847 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9848 !strcmp (cookie1
->value
, cookie2
->value
) &&
9849 !strcmp (cookie1
->path
, cookie2
->path
) &&
9850 !strcmp (cookie1
->domain
, cookie2
->domain
));
9854 transfer_cookies(void)
9857 SoupCookie
*sc
, *pc
;
9859 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9861 for (;cf
; cf
= cf
->next
) {
9863 sc
= soup_cookie_copy(pc
);
9864 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9867 soup_cookies_free(cf
);
9871 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9876 print_cookie("soup_cookie_jar_delete_cookie", c
);
9878 if (cookies_enabled
== 0)
9881 if (jar
== NULL
|| c
== NULL
)
9884 /* find and remove from persistent jar */
9885 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9887 for (;cf
; cf
= cf
->next
) {
9889 if (soup_cookie_equal(ci
, c
)) {
9890 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9895 soup_cookies_free(cf
);
9897 /* delete from session jar */
9898 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9902 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9904 struct domain
*d
= NULL
;
9908 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9909 jar
, p_cookiejar
, s_cookiejar
);
9911 if (cookies_enabled
== 0)
9914 /* see if we are up and running */
9915 if (p_cookiejar
== NULL
) {
9916 _soup_cookie_jar_add_cookie(jar
, cookie
);
9919 /* disallow p_cookiejar adds, shouldn't happen */
9920 if (jar
== p_cookiejar
)
9924 if (jar
== NULL
|| cookie
== NULL
)
9927 if (enable_cookie_whitelist
&&
9928 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9930 DNPRINTF(XT_D_COOKIE
,
9931 "soup_cookie_jar_add_cookie: reject %s\n",
9933 if (save_rejected_cookies
) {
9934 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9935 show_oops(NULL
, "can't open reject cookie file");
9938 fseek(r_cookie_f
, 0, SEEK_END
);
9939 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9940 cookie
->http_only
? "#HttpOnly_" : "",
9942 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9944 cookie
->secure
? "TRUE" : "FALSE",
9946 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9953 if (!allow_volatile_cookies
)
9957 if (cookie
->expires
== NULL
&& session_timeout
) {
9958 soup_cookie_set_expires(cookie
,
9959 soup_date_new_from_now(session_timeout
));
9960 print_cookie("modified add cookie", cookie
);
9963 /* see if we are white listed for persistence */
9964 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9965 /* add to persistent jar */
9966 c
= soup_cookie_copy(cookie
);
9967 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9968 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9971 /* add to session jar */
9972 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9973 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9979 char file
[PATH_MAX
];
9981 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9982 "soup_cookie_jar_add_cookie");
9983 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9984 "soup_cookie_jar_delete_cookie");
9986 if (cookies_enabled
== 0)
9990 * the following code is intricate due to overriding several libsoup
9992 * do not alter order of these operations.
9995 /* rejected cookies */
9996 if (save_rejected_cookies
)
9997 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
10000 /* persistent cookies */
10001 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
10002 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
10004 /* session cookies */
10005 s_cookiejar
= soup_cookie_jar_new();
10006 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
10007 cookie_policy
, (void *)NULL
);
10008 transfer_cookies();
10010 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
10014 setup_proxy(char *uri
)
10017 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
10018 soup_uri_free(proxy_uri
);
10022 if (http_proxy
!= uri
) {
10023 g_free(http_proxy
);
10029 http_proxy
= g_strdup(uri
);
10030 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
10031 proxy_uri
= soup_uri_new(http_proxy
);
10032 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
10033 g_object_set(session
, "proxy-uri", proxy_uri
,
10039 set_http_proxy(char *proxy
)
10046 /* see if we need to clear it instead */
10047 if (strlen(proxy
) == 0) {
10052 uri
= soup_uri_new(proxy
);
10053 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
10056 setup_proxy(proxy
);
10058 soup_uri_free(uri
);
10064 send_cmd_to_socket(char *cmd
)
10066 int s
, len
, rv
= 1;
10067 struct sockaddr_un sa
;
10069 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10070 warnx("%s: socket", __func__
);
10074 sa
.sun_family
= AF_UNIX
;
10075 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10076 work_dir
, XT_SOCKET_FILE
);
10077 len
= SUN_LEN(&sa
);
10079 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10080 warnx("%s: connect", __func__
);
10084 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
10085 warnx("%s: send", __func__
);
10096 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
10099 char str
[XT_MAX_URL_LENGTH
];
10100 socklen_t t
= sizeof(struct sockaddr_un
);
10101 struct sockaddr_un sa
;
10106 gint fd
= g_io_channel_unix_get_fd(source
);
10108 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
10113 if (getpeereid(s
, &uid
, &gid
) == -1) {
10114 warn("getpeereid");
10117 if (uid
!= getuid() || gid
!= getgid()) {
10118 warnx("unauthorized user");
10124 warnx("not a valid user");
10128 n
= recv(s
, str
, sizeof(str
), 0);
10132 tt
= TAILQ_LAST(&tabs
, tab_list
);
10133 cmd_execute(tt
, str
);
10140 int s
, len
, rv
= 1;
10141 struct sockaddr_un sa
;
10143 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10144 warn("is_running: socket");
10148 sa
.sun_family
= AF_UNIX
;
10149 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10150 work_dir
, XT_SOCKET_FILE
);
10151 len
= SUN_LEN(&sa
);
10153 /* connect to see if there is a listener */
10154 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
10155 rv
= 0; /* not running */
10157 rv
= 1; /* already running */
10168 struct sockaddr_un sa
;
10170 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10171 warn("build_socket: socket");
10175 sa
.sun_family
= AF_UNIX
;
10176 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10177 work_dir
, XT_SOCKET_FILE
);
10178 len
= SUN_LEN(&sa
);
10180 /* connect to see if there is a listener */
10181 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10182 /* no listener so we will */
10183 unlink(sa
.sun_path
);
10185 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10186 warn("build_socket: bind");
10190 if (listen(s
, 1) == -1) {
10191 warn("build_socket: listen");
10204 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10205 GtkTreeIter
*iter
, struct tab
*t
)
10209 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10210 load_uri(t
, value
);
10217 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10218 GtkTreeIter
*iter
, struct tab
*t
)
10222 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10223 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10224 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10231 completion_add_uri(const gchar
*uri
)
10235 /* add uri to list_store */
10236 gtk_list_store_append(completion_model
, &iter
);
10237 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10241 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10242 GtkTreeIter
*iter
, gpointer user_data
)
10245 gboolean match
= FALSE
;
10247 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10253 match
= match_uri(value
, key
);
10260 completion_add(struct tab
*t
)
10262 /* enable completion for tab */
10263 t
->completion
= gtk_entry_completion_new();
10264 gtk_entry_completion_set_text_column(t
->completion
, 0);
10265 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10266 gtk_entry_completion_set_model(t
->completion
,
10267 GTK_TREE_MODEL(completion_model
));
10268 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10270 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10271 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10272 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10273 G_CALLBACK(completion_select_cb
), t
);
10274 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10275 G_CALLBACK(completion_hover_cb
), t
);
10283 if (stat(dir
, &sb
)) {
10284 if (mkdir(dir
, S_IRWXU
) == -1)
10285 err(1, "mkdir %s", dir
);
10286 if (stat(dir
, &sb
))
10287 err(1, "stat %s", dir
);
10289 if (S_ISDIR(sb
.st_mode
) == 0)
10290 errx(1, "%s not a dir", dir
);
10291 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10292 warnx("fixing invalid permissions on %s", dir
);
10293 if (chmod(dir
, S_IRWXU
) == -1)
10294 err(1, "chmod %s", dir
);
10302 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10306 GStaticRecMutex my_gdk_mtx
= G_STATIC_REC_MUTEX_INIT
;
10307 volatile int mtx_depth
;
10311 * The linux flash plugin violates the gdk locking mechanism.
10312 * Work around the issue by using a recursive mutex with some match applied
10313 * to see if we hit a buggy condition.
10315 * The following code is painful so just don't read it. It really doesn't
10316 * make much sense but seems to work.
10321 g_static_rec_mutex_lock(&my_gdk_mtx
);
10324 if (mtx_depth
<= 0) {
10325 /* should not happen */
10326 show_oops(NULL
, "negative mutex locking bug, trying to "
10328 fprintf(stderr
, "negative mutex locking bug, trying to "
10330 g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10331 g_static_rec_mutex_lock(&my_gdk_mtx
);
10336 if (mtx_depth
!= 1) {
10337 /* decrease mutext depth to 1 */
10339 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10341 } while (mtx_depth
> 1);
10350 /* if mutex depth isn't 1 then something went bad */
10351 if (mtx_depth
!= 1) {
10352 x
= g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10354 /* should not happen */
10355 show_oops(NULL
, "mutex unlocking bug, trying to "
10357 fprintf(stderr
, "mutex unlocking bug, trying to "
10361 if (mtx_complain
== 0) {
10362 show_oops(NULL
, "buggy mutex implementation detected, "
10363 "work around implemented");
10364 fprintf(stderr
, "buggy mutex implementation detected, "
10365 "work around implemented");
10372 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10376 main(int argc
, char *argv
[])
10379 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10380 char conf
[PATH_MAX
] = { '\0' };
10381 char file
[PATH_MAX
];
10382 char *env_proxy
= NULL
;
10386 struct sigaction sact
;
10387 GIOChannel
*channel
;
10394 g_thread_init(NULL
);
10395 gdk_threads_set_lock_functions(mtx_lock
, mtx_unlock
);
10396 gdk_threads_init();
10397 gdk_threads_enter();
10399 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pthread
);
10401 gtk_init(&argc
, &argv
);
10403 gnutls_global_init();
10405 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10410 RB_INIT(&downloads
);
10412 TAILQ_INIT(&sessions
);
10415 TAILQ_INIT(&aliases
);
10416 TAILQ_INIT(&undos
);
10422 /* fiddle with ulimits */
10423 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10426 /* just use them all */
10427 rlp
.rlim_cur
= rlp
.rlim_max
;
10428 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10430 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10432 else if (rlp
.rlim_cur
<= 256)
10433 startpage_add("%s requires at least 256 file "
10434 "descriptors, currently it has up to %d available",
10435 __progname
, rlp
.rlim_cur
);
10438 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10447 errx(0 , "Version: %s", version
);
10450 strlcpy(conf
, optarg
, sizeof(conf
));
10453 strlcpy(named_session
, optarg
, sizeof(named_session
));
10472 init_keybindings();
10474 /* generate session keys for xtp pages */
10475 generate_xtp_session_key(&dl_session_key
);
10476 generate_xtp_session_key(&hl_session_key
);
10477 generate_xtp_session_key(&cl_session_key
);
10478 generate_xtp_session_key(&fl_session_key
);
10481 bzero(&sact
, sizeof(sact
));
10482 sigemptyset(&sact
.sa_mask
);
10483 sact
.sa_handler
= sigchild
;
10484 sact
.sa_flags
= SA_NOCLDSTOP
;
10485 sigaction(SIGCHLD
, &sact
, NULL
);
10487 /* set download dir */
10488 pwd
= getpwuid(getuid());
10490 errx(1, "invalid user %d", getuid());
10491 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10493 /* compile buffer command regexes */
10496 /* set default string settings */
10497 home
= g_strdup("https://www.cyphertite.com");
10498 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10499 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10500 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10501 cmd_font_name
= g_strdup("monospace normal 9");
10502 oops_font_name
= g_strdup("monospace normal 9");
10503 statusbar_font_name
= g_strdup("monospace normal 9");
10504 tabbar_font_name
= g_strdup("monospace normal 9");
10505 statusbar_elems
= g_strdup("BP");
10506 encoding
= g_strdup("ISO-8859-1");
10508 /* read config file */
10509 if (strlen(conf
) == 0)
10510 snprintf(conf
, sizeof conf
, "%s/.%s",
10511 pwd
->pw_dir
, XT_CONF_FILE
);
10512 config_parse(conf
, 0);
10515 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10516 oops_font
= pango_font_description_from_string(oops_font_name
);
10517 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10518 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10520 /* working directory */
10521 if (strlen(work_dir
) == 0)
10522 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10523 pwd
->pw_dir
, XT_DIR
);
10526 /* icon cache dir */
10527 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10528 xxx_dir(cache_dir
);
10531 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10532 xxx_dir(certs_dir
);
10535 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10536 work_dir
, XT_SESSIONS_DIR
);
10537 xxx_dir(sessions_dir
);
10539 /* runtime settings that can override config file */
10540 if (runtime_settings
[0] != '\0')
10541 config_parse(runtime_settings
, 1);
10544 if (!strcmp(download_dir
, pwd
->pw_dir
))
10545 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10546 xxx_dir(download_dir
);
10548 /* favorites file */
10549 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10550 if (stat(file
, &sb
)) {
10551 warnx("favorites file doesn't exist, creating it");
10552 if ((f
= fopen(file
, "w")) == NULL
)
10553 err(1, "favorites");
10557 /* quickmarks file */
10558 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10559 if (stat(file
, &sb
)) {
10560 warnx("quickmarks file doesn't exist, creating it");
10561 if ((f
= fopen(file
, "w")) == NULL
)
10562 err(1, "quickmarks");
10566 /* search history */
10567 if (history_autosave
) {
10568 snprintf(search_file
, sizeof search_file
, "%s/%s",
10569 work_dir
, XT_SEARCH_FILE
);
10570 if (stat(search_file
, &sb
)) {
10571 warnx("search history file doesn't exist, creating it");
10572 if ((f
= fopen(search_file
, "w")) == NULL
)
10573 err(1, "search_history");
10576 history_read(&shl
, search_file
, &search_history_count
);
10579 /* command history */
10580 if (history_autosave
) {
10581 snprintf(command_file
, sizeof command_file
, "%s/%s",
10582 work_dir
, XT_COMMAND_FILE
);
10583 if (stat(command_file
, &sb
)) {
10584 warnx("command history file doesn't exist, creating it");
10585 if ((f
= fopen(command_file
, "w")) == NULL
)
10586 err(1, "command_history");
10589 history_read(&chl
, command_file
, &cmd_history_count
);
10593 session
= webkit_get_default_session();
10598 if (stat(ssl_ca_file
, &sb
)) {
10599 warnx("no CA file: %s", ssl_ca_file
);
10600 g_free(ssl_ca_file
);
10601 ssl_ca_file
= NULL
;
10603 g_object_set(session
,
10604 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10605 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10609 /* guess_search regex */
10610 if (url_regex
== NULL
)
10611 url_regex
= g_strdup(XT_URL_REGEX
);
10613 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10614 startpage_add("invalid url regex %s", url_regex
);
10617 env_proxy
= getenv("http_proxy");
10619 setup_proxy(env_proxy
);
10621 setup_proxy(http_proxy
);
10624 send_cmd_to_socket(argv
[0]);
10628 /* set some connection parameters */
10629 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10630 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10633 /* see if there is already an xxxterm running */
10634 if (single_instance
&& is_running()) {
10636 warnx("already running");
10641 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10642 send_cmd_to_socket(cmd
);
10652 /* uri completion */
10653 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10656 buffers_store
= gtk_list_store_new
10657 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10663 notebook_tab_set_visibility();
10665 if (save_global_history
)
10666 restore_global_history();
10668 /* restore session list */
10669 restore_sessions_list();
10671 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10672 restore_saved_tabs();
10674 a
.s
= named_session
;
10675 a
.i
= XT_SES_DONOTHING
;
10676 open_tabs(NULL
, &a
);
10679 /* see if we have an exception */
10680 if (!TAILQ_EMPTY(&spl
)) {
10681 create_new_tab("about:startpage", NULL
, focus
, -1);
10686 create_new_tab(argv
[0], NULL
, focus
, -1);
10693 if (TAILQ_EMPTY(&tabs
))
10694 create_new_tab(home
, NULL
, 1, -1);
10697 if ((s
= build_socket()) != -1) {
10698 channel
= g_io_channel_unix_new(s
);
10699 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10705 gdk_threads_leave();
10706 g_static_rec_mutex_unlock_full(&my_gdk_mtx
); /* just in case */
10709 gnutls_global_deinit();