3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * multi letter commands
24 * pre and post counts for commands
25 * autocompletion on various inputs
26 * create privacy browsing
27 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #elif defined(__FreeBSD__)
50 #include "freebsd/util.h"
56 #include <sys/queue.h>
58 #include <sys/socket.h>
62 #include <gdk/gdkkeysyms.h>
63 #include <webkit/webkit.h>
64 #include <libsoup/soup.h>
65 #include <gnutls/gnutls.h>
66 #include <JavaScriptCore/JavaScript.h>
67 #include <gnutls/x509.h>
69 #include "javascript.h"
72 javascript.h borrowed from vimprobable2 under the following license:
74 Copyright (c) 2009 Leon Winter
75 Copyright (c) 2009 Hannes Schueller
76 Copyright (c) 2009 Matto Fransen
78 Permission is hereby granted, free of charge, to any person obtaining a copy
79 of this software and associated documentation files (the "Software"), to deal
80 in the Software without restriction, including without limitation the rights
81 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
82 copies of the Software, and to permit persons to whom the Software is
83 furnished to do so, subject to the following conditions:
85 The above copyright notice and this permission notice shall be included in
86 all copies or substantial portions of the Software.
88 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
89 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
91 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
92 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
93 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
97 static char *version
= "$xxxterm$";
99 /* hooked functions */
100 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
101 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
106 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
107 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
108 #define XT_D_MOVE 0x0001
109 #define XT_D_KEY 0x0002
110 #define XT_D_TAB 0x0004
111 #define XT_D_URL 0x0008
112 #define XT_D_CMD 0x0010
113 #define XT_D_NAV 0x0020
114 #define XT_D_DOWNLOAD 0x0040
115 #define XT_D_CONFIG 0x0080
116 #define XT_D_JS 0x0100
117 #define XT_D_FAVORITE 0x0200
118 #define XT_D_PRINTING 0x0400
119 #define XT_D_COOKIE 0x0800
120 #define XT_D_KEYBINDING 0x1000
121 u_int32_t swm_debug
= 0
137 #define DPRINTF(x...)
138 #define DNPRINTF(n,x...)
141 #define LENGTH(x) (sizeof x / sizeof x[0])
142 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
143 ~(GDK_BUTTON1_MASK) & \
144 ~(GDK_BUTTON2_MASK) & \
145 ~(GDK_BUTTON3_MASK) & \
146 ~(GDK_BUTTON4_MASK) & \
158 TAILQ_ENTRY(tab
) entry
;
160 GtkWidget
*tab_content
;
163 GtkWidget
*uri_entry
;
164 GtkWidget
*search_entry
;
166 GtkWidget
*browser_win
;
167 GtkWidget
*statusbar
;
174 GtkWidget
*js_toggle
;
175 GtkEntryCompletion
*completion
;
179 WebKitWebHistoryItem
*item
;
180 WebKitWebBackForwardList
*bfl
;
183 WebKitNetworkRequest
*icon_request
;
184 WebKitDownload
*icon_download
;
185 GdkPixbuf
*icon_pixbuf
;
186 gchar
*icon_dest_uri
;
188 /* adjustments for browser */
191 GtkAdjustment
*adjust_h
;
192 GtkAdjustment
*adjust_v
;
198 int xtp_meaning
; /* identifies dls/favorites */
203 #define XT_HINT_NONE (0)
204 #define XT_HINT_NUMERICAL (1)
205 #define XT_HINT_ALPHANUM (2)
209 /* custom stylesheet */
218 WebKitWebSettings
*settings
;
222 TAILQ_HEAD(tab_list
, tab
);
225 RB_ENTRY(history
) entry
;
229 RB_HEAD(history_list
, history
);
232 RB_ENTRY(download
) entry
;
234 WebKitDownload
*download
;
237 RB_HEAD(download_list
, download
);
240 RB_ENTRY(domain
) entry
;
242 int handy
; /* app use */
244 RB_HEAD(domain_list
, domain
);
247 TAILQ_ENTRY(undo
) entry
;
250 int back
; /* Keeps track of how many back
251 * history items there are. */
253 TAILQ_HEAD(undo_tailq
, undo
);
255 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
256 int next_download_id
= 1;
264 #define XT_NAME ("XXXTerm")
265 #define XT_DIR (".xxxterm")
266 #define XT_CACHE_DIR ("cache")
267 #define XT_CERT_DIR ("certs/")
268 #define XT_SESSIONS_DIR ("sessions/")
269 #define XT_CONF_FILE ("xxxterm.conf")
270 #define XT_FAVS_FILE ("favorites")
271 #define XT_SAVED_TABS_FILE ("main_session")
272 #define XT_RESTART_TABS_FILE ("restart_tabs")
273 #define XT_SOCKET_FILE ("socket")
274 #define XT_HISTORY_FILE ("history")
275 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
276 #define XT_CB_HANDLED (TRUE)
277 #define XT_CB_PASSTHROUGH (FALSE)
278 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
279 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
280 #define XT_DLMAN_REFRESH "10"
281 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
282 "td {overflow: hidden;" \
283 " padding: 2px 2px 2px 2px;" \
284 " border: 1px solid black}\n" \
285 "tr:hover {background: #ffff99 ;}\n" \
286 "th {background-color: #cccccc;" \
287 " border: 1px solid black}" \
288 "table {border-spacing: 0; " \
290 " border: 1px black solid;}\n" \
292 " border: 1px solid black;" \
298 " background: green;}" \
300 " font-size: small;" \
301 " text-align: center;}" \
303 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
304 #define XT_MAX_UNDO_CLOSE_TAB (32)
305 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
306 #define XT_PRINT_EXTRA_MARGIN 10
309 #define XT_COLOR_RED "#cc0000"
310 #define XT_COLOR_YELLOW "#ffff66"
311 #define XT_COLOR_BLUE "lightblue"
312 #define XT_COLOR_GREEN "#99ff66"
313 #define XT_COLOR_WHITE "white"
314 #define XT_COLOR_BLACK "black"
317 * xxxterm "protocol" (xtp)
318 * We use this for managing stuff like downloads and favorites. They
319 * make magical HTML pages in memory which have xxxt:// links in order
320 * to communicate with xxxterm's internals. These links take the format:
321 * xxxt://class/session_key/action/arg
323 * Don't begin xtp class/actions as 0. atoi returns that on error.
325 * Typically we have not put addition of items in this framework, as
326 * adding items is either done via an ex-command or via a keybinding instead.
329 #define XT_XTP_STR "xxxt://"
331 /* XTP classes (xxxt://<class>) */
332 #define XT_XTP_DL 1 /* downloads */
333 #define XT_XTP_HL 2 /* history */
334 #define XT_XTP_CL 3 /* cookies */
335 #define XT_XTP_FL 4 /* favorites */
337 /* XTP download actions */
338 #define XT_XTP_DL_LIST 1
339 #define XT_XTP_DL_CANCEL 2
340 #define XT_XTP_DL_REMOVE 3
342 /* XTP history actions */
343 #define XT_XTP_HL_LIST 1
344 #define XT_XTP_HL_REMOVE 2
346 /* XTP cookie actions */
347 #define XT_XTP_CL_LIST 1
348 #define XT_XTP_CL_REMOVE 2
350 /* XTP cookie actions */
351 #define XT_XTP_FL_LIST 1
352 #define XT_XTP_FL_REMOVE 2
354 /* xtp tab meanings - identifies which tabs have xtp pages in */
355 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
356 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
357 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
358 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
359 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
362 #define XT_MOVE_INVALID (0)
363 #define XT_MOVE_DOWN (1)
364 #define XT_MOVE_UP (2)
365 #define XT_MOVE_BOTTOM (3)
366 #define XT_MOVE_TOP (4)
367 #define XT_MOVE_PAGEDOWN (5)
368 #define XT_MOVE_PAGEUP (6)
369 #define XT_MOVE_HALFDOWN (7)
370 #define XT_MOVE_HALFUP (8)
371 #define XT_MOVE_LEFT (9)
372 #define XT_MOVE_FARLEFT (10)
373 #define XT_MOVE_RIGHT (11)
374 #define XT_MOVE_FARRIGHT (12)
376 #define XT_TAB_LAST (-4)
377 #define XT_TAB_FIRST (-3)
378 #define XT_TAB_PREV (-2)
379 #define XT_TAB_NEXT (-1)
380 #define XT_TAB_INVALID (0)
381 #define XT_TAB_NEW (1)
382 #define XT_TAB_DELETE (2)
383 #define XT_TAB_DELQUIT (3)
384 #define XT_TAB_OPEN (4)
385 #define XT_TAB_UNDO_CLOSE (5)
386 #define XT_TAB_SHOW (6)
387 #define XT_TAB_HIDE (7)
389 #define XT_NAV_INVALID (0)
390 #define XT_NAV_BACK (1)
391 #define XT_NAV_FORWARD (2)
392 #define XT_NAV_RELOAD (3)
393 #define XT_NAV_RELOAD_CACHE (4)
395 #define XT_FOCUS_INVALID (0)
396 #define XT_FOCUS_URI (1)
397 #define XT_FOCUS_SEARCH (2)
399 #define XT_SEARCH_INVALID (0)
400 #define XT_SEARCH_NEXT (1)
401 #define XT_SEARCH_PREV (2)
403 #define XT_PASTE_CURRENT_TAB (0)
404 #define XT_PASTE_NEW_TAB (1)
406 #define XT_FONT_SET (0)
408 #define XT_URL_SHOW (1)
409 #define XT_URL_HIDE (2)
411 #define XT_STATUSBAR_SHOW (1)
412 #define XT_STATUSBAR_HIDE (2)
414 #define XT_WL_TOGGLE (1<<0)
415 #define XT_WL_ENABLE (1<<1)
416 #define XT_WL_DISABLE (1<<2)
417 #define XT_WL_FQDN (1<<3) /* default */
418 #define XT_WL_TOPLEVEL (1<<4)
420 #define XT_CMD_OPEN (0)
421 #define XT_CMD_OPEN_CURRENT (1)
422 #define XT_CMD_TABNEW (2)
423 #define XT_CMD_TABNEW_CURRENT (3)
425 #define XT_STATUS_NOTHING (0)
426 #define XT_STATUS_LINK (1)
427 #define XT_STATUS_URI (2)
428 #define XT_STATUS_LOADING (3)
430 #define XT_SES_DONOTHING (0)
431 #define XT_SES_CLOSETABS (1)
433 #define XT_BM_NORMAL (0)
434 #define XT_BM_WHITELIST (1)
435 #define XT_BM_KIOSK (2)
443 TAILQ_ENTRY(mime_type
) entry
;
445 TAILQ_HEAD(mime_type_list
, mime_type
);
451 TAILQ_ENTRY(alias
) entry
;
453 TAILQ_HEAD(alias_list
, alias
);
455 /* settings that require restart */
456 int tabless
= 0; /* allow only 1 tab */
457 int enable_socket
= 0;
458 int single_instance
= 0; /* only allow one xxxterm to run */
459 int fancy_bar
= 1; /* fancy toolbar */
460 int browser_mode
= XT_BM_NORMAL
;
462 /* runtime settings */
463 int show_tabs
= 1; /* show tabs on notebook */
464 int show_url
= 1; /* show url toolbar on notebook */
465 int show_statusbar
= 0; /* vimperator style status bar */
466 int ctrl_click_focus
= 0; /* ctrl click gets focus */
467 int cookies_enabled
= 1; /* enable cookies */
468 int read_only_cookies
= 0; /* enable to not write cookies */
469 int enable_scripts
= 1;
470 int enable_plugins
= 0;
471 int default_font_size
= 12;
472 gfloat default_zoom_level
= 1.0;
473 int window_height
= 768;
474 int window_width
= 1024;
475 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
476 unsigned refresh_interval
= 10; /* download refresh interval */
477 int enable_cookie_whitelist
= 0;
478 int enable_js_whitelist
= 0;
479 time_t session_timeout
= 3600; /* cookie session timeout */
480 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
481 char *ssl_ca_file
= NULL
;
482 char *resource_dir
= NULL
;
483 gboolean ssl_strict_certs
= FALSE
;
484 int append_next
= 1; /* append tab after current tab */
486 char *search_string
= NULL
;
487 char *http_proxy
= NULL
;
488 char download_dir
[PATH_MAX
];
489 char runtime_settings
[PATH_MAX
]; /* override of settings */
490 int allow_volatile_cookies
= 0;
491 int save_global_history
= 0; /* save global history to disk */
492 char *user_agent
= NULL
;
493 int save_rejected_cookies
= 0;
494 time_t session_autosave
= 0;
495 int guess_search
= 0;
496 int dns_prefetch
= FALSE
;
500 int set_download_dir(struct settings
*, char *);
501 int set_work_dir(struct settings
*, char *);
502 int set_runtime_dir(struct settings
*, char *);
503 int set_browser_mode(struct settings
*, char *);
504 int set_cookie_policy(struct settings
*, char *);
505 int add_alias(struct settings
*, char *);
506 int add_mime_type(struct settings
*, char *);
507 int add_cookie_wl(struct settings
*, char *);
508 int add_js_wl(struct settings
*, char *);
509 int add_kb(struct settings
*, char *);
510 void button_set_stockid(GtkWidget
*, char *);
511 GtkWidget
* create_button(char *, char *, int);
513 char *get_browser_mode(struct settings
*);
514 char *get_cookie_policy(struct settings
*);
516 char *get_download_dir(struct settings
*);
517 char *get_work_dir(struct settings
*);
518 char *get_runtime_dir(struct settings
*);
520 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
521 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
522 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
523 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
524 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
527 int (*set
)(struct settings
*, char *);
528 char *(*get
)(struct settings
*);
529 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
532 struct special s_browser_mode
= {
538 struct special s_cookie
= {
544 struct special s_alias
= {
550 struct special s_mime
= {
556 struct special s_js
= {
562 struct special s_kb
= {
568 struct special s_cookie_wl
= {
574 struct special s_download_dir
= {
580 struct special s_work_dir
= {
589 #define XT_S_INVALID (0)
592 #define XT_S_FLOAT (3)
594 #define XT_SF_RESTART (1<<0)
595 #define XT_SF_RUNTIME (1<<1)
601 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
602 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
603 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
604 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
605 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
606 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
607 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
608 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
609 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
610 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
611 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
612 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
613 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
614 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
615 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
616 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
617 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
618 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
619 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
620 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
621 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
622 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
623 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
624 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
625 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
626 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
627 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
628 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
629 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
630 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
631 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
632 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
633 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
634 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
635 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
636 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
637 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
639 /* runtime settings */
640 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
641 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
642 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
643 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
644 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
647 int about(struct tab
*, struct karg
*);
648 int blank(struct tab
*, struct karg
*);
649 int cookie_show_wl(struct tab
*, struct karg
*);
650 int js_show_wl(struct tab
*, struct karg
*);
651 int help(struct tab
*, struct karg
*);
652 int set(struct tab
*, struct karg
*);
653 int stats(struct tab
*, struct karg
*);
654 int xtp_page_cl(struct tab
*, struct karg
*);
655 int xtp_page_dl(struct tab
*, struct karg
*);
656 int xtp_page_fl(struct tab
*, struct karg
*);
657 int xtp_page_hl(struct tab
*, struct karg
*);
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") /* XXX NOT YET */
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_SET ("set")
672 #define XT_URI_ABOUT_STATS ("stats")
676 int (*func
)(struct tab
*, struct karg
*);
678 { XT_URI_ABOUT_ABOUT
, about
},
679 { XT_URI_ABOUT_BLANK
, blank
},
680 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
681 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
682 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
683 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
684 { XT_URI_ABOUT_HELP
, help
},
685 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
686 { XT_URI_ABOUT_JSWL
, js_show_wl
},
687 { XT_URI_ABOUT_SET
, set
},
688 { XT_URI_ABOUT_STATS
, stats
},
692 extern char *__progname
;
695 GtkWidget
*main_window
;
696 GtkNotebook
*notebook
;
697 GtkWidget
*arrow
, *abtn
;
698 struct tab_list tabs
;
699 struct history_list hl
;
700 struct download_list downloads
;
701 struct domain_list c_wl
;
702 struct domain_list js_wl
;
703 struct undo_tailq undos
;
704 struct keybinding_list kbl
;
706 int updating_dl_tabs
= 0;
707 int updating_hl_tabs
= 0;
708 int updating_cl_tabs
= 0;
709 int updating_fl_tabs
= 0;
711 uint64_t blocked_cookies
= 0;
712 char named_session
[PATH_MAX
];
713 void update_favicon(struct tab
*);
714 int icon_size_map(int);
716 GtkListStore
*completion_model
;
717 void completion_add(struct tab
*);
718 void completion_add_uri(const gchar
*);
723 int saved_errno
, status
;
728 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
732 if (errno
!= ECHILD
) {
734 clog_warn("sigchild: waitpid:");
740 if (WIFEXITED(status
)) {
741 if (WEXITSTATUS(status
) != 0) {
743 clog_warnx("sigchild: child exit status: %d",
744 WEXITSTATUS(status));
749 clog_warnx("sigchild: child is terminated abnormally");
758 is_g_object_setting(GObject
*o
, char *str
)
760 guint n_props
= 0, i
;
761 GParamSpec
**proplist
;
763 if (! G_IS_OBJECT(o
))
766 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
769 for (i
=0; i
< n_props
; i
++) {
770 if (! strcmp(proplist
[i
]->name
, str
))
777 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
783 /* we set this to indicate we want to manually do navaction */
785 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
786 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
789 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, title
);
790 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
793 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
794 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
795 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
796 GTK_ENTRY_ICON_PRIMARY
, pb
);
797 gdk_pixbuf_unref(pb
);
802 set_status(struct tab
*t
, gchar
*s
, int status
)
810 case XT_STATUS_LOADING
:
811 type
= g_strdup_printf("Loading: %s", s
);
815 type
= g_strdup_printf("Link: %s", s
);
817 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
821 type
= g_strdup_printf("%s", s
);
823 t
->status
= g_strdup(type
);
827 t
->status
= g_strdup(s
);
829 case XT_STATUS_NOTHING
:
834 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
840 hide_oops(struct tab
*t
)
842 gtk_widget_hide(t
->oops
);
846 hide_cmd(struct tab
*t
)
848 gtk_widget_hide(t
->cmd
);
852 show_cmd(struct tab
*t
)
854 gtk_widget_hide(t
->oops
);
855 gtk_widget_show(t
->cmd
);
859 show_oops(struct tab
*t
, const char *fmt
, ...)
868 if (vasprintf(&msg
, fmt
, ap
) == -1)
869 errx(1, "show_oops failed");
872 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
873 gtk_widget_hide(t
->cmd
);
874 gtk_widget_show(t
->oops
);
877 /* XXX collapse with show_oops */
879 show_oops_s(const char *fmt
, ...)
883 struct tab
*ti
, *t
= NULL
;
888 TAILQ_FOREACH(ti
, &tabs
, entry
)
889 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
897 if (vasprintf(&msg
, fmt
, ap
) == -1)
898 errx(1, "show_oops_s failed");
901 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
902 gtk_widget_hide(t
->cmd
);
903 gtk_widget_show(t
->oops
);
907 get_as_string(struct settings
*s
)
918 warnx("get_as_string skip %s\n", s
->name
);
919 } else if (s
->type
== XT_S_INT
)
920 r
= g_strdup_printf("%d", *s
->ival
);
921 else if (s
->type
== XT_S_STR
)
922 r
= g_strdup(*s
->sval
);
923 else if (s
->type
== XT_S_FLOAT
)
924 r
= g_strdup_printf("%f", *s
->fval
);
926 r
= g_strdup_printf("INVALID TYPE");
932 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
937 for (i
= 0; i
< LENGTH(rs
); i
++) {
938 if (rs
[i
].s
&& rs
[i
].s
->walk
)
939 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
941 s
= get_as_string(&rs
[i
]);
942 cb(&rs
[i
], s
, cb_args
);
949 set_browser_mode(struct settings
*s
, char *val
)
951 if (!strcmp(val
, "whitelist")) {
952 browser_mode
= XT_BM_WHITELIST
;
953 allow_volatile_cookies
= 0;
954 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
956 enable_cookie_whitelist
= 1;
957 read_only_cookies
= 0;
958 save_rejected_cookies
= 0;
959 session_timeout
= 3600;
961 enable_js_whitelist
= 1;
962 } else if (!strcmp(val
, "normal")) {
963 browser_mode
= XT_BM_NORMAL
;
964 allow_volatile_cookies
= 0;
965 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
967 enable_cookie_whitelist
= 0;
968 read_only_cookies
= 0;
969 save_rejected_cookies
= 0;
970 session_timeout
= 3600;
972 enable_js_whitelist
= 0;
973 } else if (!strcmp(val
, "kiosk")) {
974 browser_mode
= XT_BM_KIOSK
;
975 allow_volatile_cookies
= 0;
976 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
978 enable_cookie_whitelist
= 0;
979 read_only_cookies
= 0;
980 save_rejected_cookies
= 0;
981 session_timeout
= 3600;
983 enable_js_whitelist
= 0;
992 get_browser_mode(struct settings
*s
)
996 if (browser_mode
== XT_BM_WHITELIST
)
997 r
= g_strdup("whitelist");
998 else if (browser_mode
== XT_BM_NORMAL
)
999 r
= g_strdup("normal");
1000 else if (browser_mode
== XT_BM_KIOSK
)
1001 r
= g_strdup("kiosk");
1009 set_cookie_policy(struct settings
*s
, char *val
)
1011 if (!strcmp(val
, "no3rdparty"))
1012 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1013 else if (!strcmp(val
, "accept"))
1014 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1015 else if (!strcmp(val
, "reject"))
1016 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1024 get_cookie_policy(struct settings
*s
)
1028 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1029 r
= g_strdup("no3rdparty");
1030 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1031 r
= g_strdup("accept");
1032 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1033 r
= g_strdup("reject");
1041 get_download_dir(struct settings
*s
)
1043 if (download_dir
[0] == '\0')
1045 return (g_strdup(download_dir
));
1049 set_download_dir(struct settings
*s
, char *val
)
1052 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1053 pwd
->pw_dir
, &val
[1]);
1055 strlcpy(download_dir
, val
, sizeof download_dir
);
1062 * We use these to prevent people putting xxxt:// URLs on
1063 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1065 #define XT_XTP_SES_KEY_SZ 8
1066 #define XT_XTP_SES_KEY_HEX_FMT \
1067 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1068 char *dl_session_key
; /* downloads */
1069 char *hl_session_key
; /* history list */
1070 char *cl_session_key
; /* cookie list */
1071 char *fl_session_key
; /* favorites list */
1073 char work_dir
[PATH_MAX
];
1074 char certs_dir
[PATH_MAX
];
1075 char cache_dir
[PATH_MAX
];
1076 char sessions_dir
[PATH_MAX
];
1077 char cookie_file
[PATH_MAX
];
1078 SoupURI
*proxy_uri
= NULL
;
1079 SoupSession
*session
;
1080 SoupCookieJar
*s_cookiejar
;
1081 SoupCookieJar
*p_cookiejar
;
1082 char rc_fname
[PATH_MAX
];
1084 struct mime_type_list mtl
;
1085 struct alias_list aliases
;
1088 struct tab
*create_new_tab(char *, struct undo
*, int);
1089 void delete_tab(struct tab
*);
1090 void adjustfont_webkit(struct tab
*, int);
1091 int run_script(struct tab
*, char *);
1092 int download_rb_cmp(struct download
*, struct download
*);
1095 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1097 return (strcmp(h1
->uri
, h2
->uri
));
1099 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1102 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1104 return (strcmp(d1
->d
, d2
->d
));
1106 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1109 get_work_dir(struct settings
*s
)
1111 if (work_dir
[0] == '\0')
1113 return (g_strdup(work_dir
));
1117 set_work_dir(struct settings
*s
, char *val
)
1120 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1121 pwd
->pw_dir
, &val
[1]);
1123 strlcpy(work_dir
, val
, sizeof work_dir
);
1129 * generate a session key to secure xtp commands.
1130 * pass in a ptr to the key in question and it will
1131 * be modified in place.
1134 generate_xtp_session_key(char **key
)
1136 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1142 /* make a new one */
1143 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1144 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1145 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1146 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1148 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1152 * validate a xtp session key.
1156 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1158 if (strcmp(trusted
, untrusted
) != 0) {
1159 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1168 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1170 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1172 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1174 struct valid_url_types
{
1185 valid_url_type(char *url
)
1189 for (i
= 0; i
< LENGTH(vut
); i
++)
1190 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1197 print_cookie(char *msg
, SoupCookie
*c
)
1203 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1204 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1205 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1206 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1207 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1208 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1209 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1210 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1211 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1212 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1216 walk_alias(struct settings
*s
,
1217 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1222 if (s
== NULL
|| cb
== NULL
) {
1223 show_oops_s("walk_alias invalid parameters");
1227 TAILQ_FOREACH(a
, &aliases
, entry
) {
1228 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1229 cb(s
, str
, cb_args
);
1235 match_alias(char *url_in
)
1239 char *url_out
= NULL
, *search
, *enc_arg
;
1241 search
= g_strdup(url_in
);
1243 if (strsep(&arg
, " \t") == NULL
) {
1244 show_oops_s("match_alias: NULL URL");
1248 TAILQ_FOREACH(a
, &aliases
, entry
) {
1249 if (!strcmp(search
, a
->a_name
))
1254 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1257 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1258 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1261 url_out
= g_strdup(a
->a_uri
);
1269 guess_url_type(char *url_in
)
1272 char *url_out
= NULL
, *enc_search
= NULL
;
1274 url_out
= match_alias(url_in
);
1275 if (url_out
!= NULL
)
1280 * If there is no dot nor slash in the string and it isn't a
1281 * path to a local file and doesn't resolves to an IP, assume
1282 * that the user wants to search for the string.
1285 if (strchr(url_in
, '.') == NULL
&&
1286 strchr(url_in
, '/') == NULL
&&
1287 stat(url_in
, &sb
) != 0 &&
1288 gethostbyname(url_in
) == NULL
) {
1290 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1291 url_out
= g_strdup_printf(search_string
, enc_search
);
1297 /* XXX not sure about this heuristic */
1298 if (stat(url_in
, &sb
) == 0)
1299 url_out
= g_strdup_printf("file://%s", url_in
);
1301 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1303 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1309 load_uri(struct tab
*t
, gchar
*uri
)
1312 gchar
*newuri
= NULL
;
1318 /* Strip leading spaces. */
1319 while(*uri
&& isspace(*uri
))
1322 if (strlen(uri
) == 0) {
1327 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1328 for (i
= 0; i
< LENGTH(about_list
); i
++)
1329 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1330 bzero(&args
, sizeof args
);
1331 about_list
[i
].func(t
, &args
);
1334 show_oops(t
, "invalid about page");
1338 if (valid_url_type(uri
)) {
1339 newuri
= guess_url_type(uri
);
1343 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1344 webkit_web_view_load_uri(t
->wv
, uri
);
1351 get_uri(WebKitWebView
*wv
)
1353 WebKitWebFrame
*frame
;
1356 frame
= webkit_web_view_get_main_frame(wv
);
1357 uri
= webkit_web_frame_get_uri(frame
);
1359 if (uri
&& strlen(uri
) > 0)
1366 add_alias(struct settings
*s
, char *line
)
1369 struct alias
*a
= NULL
;
1371 if (s
== NULL
|| line
== NULL
) {
1372 show_oops_s("add_alias invalid parameters");
1377 a
= g_malloc(sizeof(*a
));
1379 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1380 show_oops_s("add_alias: incomplete alias definition");
1383 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1384 show_oops_s("add_alias: invalid alias definition");
1388 a
->a_name
= g_strdup(alias
);
1389 a
->a_uri
= g_strdup(l
);
1391 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1393 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1403 add_mime_type(struct settings
*s
, char *line
)
1407 struct mime_type
*m
= NULL
;
1408 int downloadfirst
= 0;
1410 /* XXX this could be smarter */
1412 if (line
== NULL
&& strlen(line
) == 0) {
1413 show_oops_s("add_mime_type invalid parameters");
1422 m
= g_malloc(sizeof(*m
));
1424 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1425 show_oops_s("add_mime_type: invalid mime_type");
1428 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1429 mime_type
[strlen(mime_type
) - 1] = '\0';
1434 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1435 show_oops_s("add_mime_type: invalid mime_type");
1439 m
->mt_type
= g_strdup(mime_type
);
1440 m
->mt_action
= g_strdup(l
);
1441 m
->mt_download
= downloadfirst
;
1443 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1444 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1446 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1456 find_mime_type(char *mime_type
)
1458 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1460 TAILQ_FOREACH(m
, &mtl
, entry
) {
1461 if (m
->mt_default
&&
1462 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1465 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1478 walk_mime_type(struct settings
*s
,
1479 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1481 struct mime_type
*m
;
1484 if (s
== NULL
|| cb
== NULL
)
1485 show_oops_s("walk_mime_type invalid parameters");
1487 TAILQ_FOREACH(m
, &mtl
, entry
) {
1488 str
= g_strdup_printf("%s%s --> %s",
1490 m
->mt_default
? "*" : "",
1492 cb(s
, str
, cb_args
);
1498 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1503 if (str
== NULL
|| wl
== NULL
)
1505 if (strlen(str
) < 2)
1508 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1510 /* treat *.moo.com the same as .moo.com */
1511 if (str
[0] == '*' && str
[1] == '.')
1513 else if (str
[0] == '.')
1518 d
= g_malloc(sizeof *d
);
1520 d
->d
= g_strdup_printf(".%s", str
);
1522 d
->d
= g_strdup(str
);
1525 if (RB_INSERT(domain_list
, wl
, d
))
1528 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1539 add_cookie_wl(struct settings
*s
, char *entry
)
1541 wl_add(entry
, &c_wl
, 1);
1546 walk_cookie_wl(struct settings
*s
,
1547 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1551 if (s
== NULL
|| cb
== NULL
) {
1552 show_oops_s("walk_cookie_wl invalid parameters");
1556 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1557 cb(s
, d
->d
, cb_args
);
1561 walk_js_wl(struct settings
*s
,
1562 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1566 if (s
== NULL
|| cb
== NULL
) {
1567 show_oops_s("walk_js_wl invalid parameters");
1571 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1572 cb(s
, d
->d
, cb_args
);
1576 add_js_wl(struct settings
*s
, char *entry
)
1578 wl_add(entry
, &js_wl
, 1 /* persistent */);
1583 wl_find(const gchar
*search
, struct domain_list
*wl
)
1586 struct domain
*d
= NULL
, dfind
;
1589 if (search
== NULL
|| wl
== NULL
)
1591 if (strlen(search
) < 2)
1594 if (search
[0] != '.')
1595 s
= g_strdup_printf(".%s", search
);
1597 s
= g_strdup(search
);
1599 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1602 d
= RB_FIND(domain_list
, wl
, &dfind
);
1616 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1622 if (s
== NULL
|| wl
== NULL
)
1625 if (!strncmp(s
, "http://", strlen("http://")))
1626 s
= &s
[strlen("http://")];
1627 else if (!strncmp(s
, "https://", strlen("https://")))
1628 s
= &s
[strlen("https://")];
1633 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1634 /* chop string at first slash */
1635 if (s
[i
] == '/' || s
[i
] == '\0') {
1638 r
= wl_find(ss
, wl
);
1647 get_toplevel_domain(char *domain
)
1654 if (strlen(domain
) < 2)
1657 s
= &domain
[strlen(domain
) - 1];
1658 while (s
!= domain
) {
1674 settings_add(char *var
, char *val
)
1681 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1682 if (strcmp(var
, rs
[i
].name
))
1686 if (rs
[i
].s
->set(&rs
[i
], val
))
1687 errx(1, "invalid value for %s: %s", var
, val
);
1691 switch (rs
[i
].type
) {
1700 errx(1, "invalid sval for %s",
1714 errx(1, "invalid type for %s", var
);
1723 config_parse(char *filename
, int runtime
)
1726 char *line
, *cp
, *var
, *val
;
1727 size_t len
, lineno
= 0;
1729 char file
[PATH_MAX
];
1732 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1734 if (filename
== NULL
)
1737 if (runtime
&& runtime_settings
[0] != '\0') {
1738 snprintf(file
, sizeof file
, "%s/%s",
1739 work_dir
, runtime_settings
);
1740 if (stat(file
, &sb
)) {
1741 warnx("runtime file doesn't exist, creating it");
1742 if ((f
= fopen(file
, "w")) == NULL
)
1744 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1748 strlcpy(file
, filename
, sizeof file
);
1750 if ((config
= fopen(file
, "r")) == NULL
) {
1751 warn("config_parse: cannot open %s", filename
);
1756 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1757 if (feof(config
) || ferror(config
))
1761 cp
+= (long)strspn(cp
, WS
);
1762 if (cp
[0] == '\0') {
1768 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1769 errx(1, "invalid config file entry: %s", line
);
1771 cp
+= (long)strspn(cp
, WS
);
1773 if ((val
= strsep(&cp
, "\0")) == NULL
)
1776 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1777 handled
= settings_add(var
, val
);
1779 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1788 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1794 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1798 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1801 JSStringGetUTF8CString(jsref
, s
, l
);
1802 JSStringRelease(jsref
);
1808 disable_hints(struct tab
*t
)
1810 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1811 bzero(t
->hint_num
, sizeof t
->hint_num
);
1812 run_script(t
, "vimprobable_clear()");
1814 t
->hint_mode
= XT_HINT_NONE
;
1818 enable_hints(struct tab
*t
)
1820 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1821 run_script(t
, "vimprobable_show_hints()");
1823 t
->hint_mode
= XT_HINT_NONE
;
1826 #define XT_JS_OPEN ("open;")
1827 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1828 #define XT_JS_FIRE ("fire;")
1829 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1830 #define XT_JS_FOUND ("found;")
1831 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1834 run_script(struct tab
*t
, char *s
)
1836 JSGlobalContextRef ctx
;
1837 WebKitWebFrame
*frame
;
1839 JSValueRef val
, exception
;
1842 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1843 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1845 frame
= webkit_web_view_get_main_frame(t
->wv
);
1846 ctx
= webkit_web_frame_get_global_context(frame
);
1848 str
= JSStringCreateWithUTF8CString(s
);
1849 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1850 NULL
, 0, &exception
);
1851 JSStringRelease(str
);
1853 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1855 es
= js_ref_to_string(ctx
, exception
);
1856 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1860 es
= js_ref_to_string(ctx
, val
);
1861 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1863 /* handle return value right here */
1864 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1866 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1869 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1870 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1871 &es
[XT_JS_FIRE_LEN
]);
1876 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1877 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1888 hint(struct tab
*t
, struct karg
*args
)
1891 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1893 if (t
->hints_on
== 0)
1902 apply_style(struct tab
*t
)
1904 g_object_set(G_OBJECT(t
->settings
),
1905 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
1909 userstyle(struct tab
*t
, struct karg
*args
)
1911 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
1915 g_object_set(G_OBJECT(t
->settings
),
1916 "user-stylesheet-uri", NULL
, (char *)NULL
);
1925 * Doesn't work fully, due to the following bug:
1926 * https://bugs.webkit.org/show_bug.cgi?id=51747
1929 restore_global_history(void)
1931 char file
[PATH_MAX
];
1937 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1939 if ((f
= fopen(file
, "r")) == NULL
) {
1940 warnx("%s: fopen", __func__
);
1945 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1946 if (feof(f
) || ferror(f
))
1949 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1950 if (feof(f
) || ferror(f
)) {
1952 warnx("%s: broken history file\n", __func__
);
1956 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1957 webkit_web_history_item_new_with_data(uri
, title
);
1958 h
= g_malloc(sizeof(struct history
));
1959 h
->uri
= g_strdup(uri
);
1960 h
->title
= g_strdup(title
);
1961 RB_INSERT(history_list
, &hl
, h
);
1962 completion_add_uri(h
->uri
);
1964 warnx("%s: failed to restore history\n", __func__
);
1980 save_global_history_to_disk(struct tab
*t
)
1982 char file
[PATH_MAX
];
1986 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1988 if ((f
= fopen(file
, "w")) == NULL
) {
1989 show_oops(t
, "%s: global history file: %s",
1990 __func__
, strerror(errno
));
1994 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1995 if (h
->uri
&& h
->title
)
1996 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2005 quit(struct tab
*t
, struct karg
*args
)
2007 if (save_global_history
)
2008 save_global_history_to_disk(t
);
2016 open_tabs(struct tab
*t
, struct karg
*a
)
2018 char file
[PATH_MAX
];
2022 struct tab
*ti
, *tt
;
2027 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2028 if ((f
= fopen(file
, "r")) == NULL
)
2031 ti
= TAILQ_LAST(&tabs
, tab_list
);
2034 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2035 if (feof(f
) || ferror(f
))
2038 /* retrieve session name */
2039 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2040 strlcpy(named_session
,
2041 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2042 sizeof named_session
);
2046 if (uri
&& strlen(uri
))
2047 create_new_tab(uri
, NULL
, 1);
2053 /* close open tabs */
2054 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2056 tt
= TAILQ_FIRST(&tabs
);
2075 restore_saved_tabs(void)
2077 char file
[PATH_MAX
];
2078 int unlink_file
= 0;
2083 snprintf(file
, sizeof file
, "%s/%s",
2084 sessions_dir
, XT_RESTART_TABS_FILE
);
2085 if (stat(file
, &sb
) == -1)
2086 a
.s
= XT_SAVED_TABS_FILE
;
2089 a
.s
= XT_RESTART_TABS_FILE
;
2092 a
.i
= XT_SES_DONOTHING
;
2093 rv
= open_tabs(NULL
, &a
);
2102 save_tabs(struct tab
*t
, struct karg
*a
)
2104 char file
[PATH_MAX
];
2109 const gchar
**arr
= NULL
;
2114 snprintf(file
, sizeof file
, "%s/%s",
2115 sessions_dir
, named_session
);
2117 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2119 if ((f
= fopen(file
, "w")) == NULL
) {
2120 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2124 /* save session name */
2125 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2127 /* save tabs, in the order they are arranged in the notebook */
2128 TAILQ_FOREACH(ti
, &tabs
, entry
)
2131 arr
= g_malloc0(len
* sizeof(gchar
*));
2133 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2134 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2135 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2138 for (i
= 0; i
< len
; i
++)
2140 fprintf(f
, "%s\n", arr
[i
]);
2149 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2161 yank_uri(struct tab
*t
, struct karg
*args
)
2164 GtkClipboard
*clipboard
;
2166 if ((uri
= get_uri(t
->wv
)) == NULL
)
2169 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2170 gtk_clipboard_set_text(clipboard
, uri
, -1);
2181 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
2183 struct paste_args
*pap
;
2185 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
2188 pap
= (struct paste_args
*)data
;
2191 case XT_PASTE_CURRENT_TAB
:
2192 load_uri(pap
->t
, (gchar
*)text
);
2194 case XT_PASTE_NEW_TAB
:
2195 create_new_tab((gchar
*)text
, NULL
, 1);
2203 paste_uri(struct tab
*t
, struct karg
*args
)
2205 GtkClipboard
*clipboard
;
2206 struct paste_args
*pap
;
2208 pap
= g_malloc(sizeof(struct paste_args
));
2213 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2214 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
2220 find_domain(const gchar
*s
, int add_dot
)
2223 char *r
= NULL
, *ss
= NULL
;
2228 if (!strncmp(s
, "http://", strlen("http://")))
2229 s
= &s
[strlen("http://")];
2230 else if (!strncmp(s
, "https://", strlen("https://")))
2231 s
= &s
[strlen("https://")];
2237 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2238 /* chop string at first slash */
2239 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2242 r
= g_strdup_printf(".%s", ss
);
2253 toggle_cwl(struct tab
*t
, struct karg
*args
)
2257 char *dom
= NULL
, *dom_toggle
= NULL
;
2263 uri
= get_uri(t
->wv
);
2264 dom
= find_domain(uri
, 1);
2265 d
= wl_find(dom
, &c_wl
);
2272 if (args
->i
& XT_WL_TOGGLE
)
2274 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2276 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2279 if (args
->i
& XT_WL_TOPLEVEL
)
2280 dom_toggle
= get_toplevel_domain(dom
);
2285 /* enable cookies for domain */
2286 wl_add(dom_toggle
, &c_wl
, 0);
2288 /* disable cookies for domain */
2289 RB_REMOVE(domain_list
, &c_wl
, d
);
2291 webkit_web_view_reload(t
->wv
);
2298 toggle_js(struct tab
*t
, struct karg
*args
)
2303 char *dom
= NULL
, *dom_toggle
= NULL
;
2308 g_object_get(G_OBJECT(t
->settings
),
2309 "enable-scripts", &es
, (char *)NULL
);
2310 if (args
->i
& XT_WL_TOGGLE
)
2312 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2314 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2319 uri
= get_uri(t
->wv
);
2320 dom
= find_domain(uri
, 1);
2322 if (uri
== NULL
|| dom
== NULL
) {
2323 show_oops(t
, "Can't toggle domain in JavaScript white list");
2327 if (args
->i
& XT_WL_TOPLEVEL
)
2328 dom_toggle
= get_toplevel_domain(dom
);
2333 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2334 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2336 d
= wl_find(dom_toggle
, &js_wl
);
2338 RB_REMOVE(domain_list
, &js_wl
, d
);
2339 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2341 g_object_set(G_OBJECT(t
->settings
),
2342 "enable-scripts", es
, (char *)NULL
);
2343 g_object_set(G_OBJECT(t
->settings
),
2344 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2345 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2346 webkit_web_view_reload(t
->wv
);
2354 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2358 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2363 toggle_src(struct tab
*t
, struct karg
*args
)
2370 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2371 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2372 webkit_web_view_reload(t
->wv
);
2378 focus_webview(struct tab
*t
)
2383 /* only grab focus if we are visible */
2384 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2385 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2389 focus(struct tab
*t
, struct karg
*args
)
2391 if (t
== NULL
|| args
== NULL
)
2397 if (args
->i
== XT_FOCUS_URI
)
2398 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2399 else if (args
->i
== XT_FOCUS_SEARCH
)
2400 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2406 stats(struct tab
*t
, struct karg
*args
)
2408 char *stats
, *s
, line
[64 * 1024];
2409 uint64_t line_count
= 0;
2413 show_oops_s("stats invalid parameters");
2416 if (save_rejected_cookies
) {
2417 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2419 s
= fgets(line
, sizeof line
, r_cookie_f
);
2420 if (s
== NULL
|| feof(r_cookie_f
) ||
2426 snprintf(line
, sizeof line
,
2427 "<br>Cookies blocked(*) total: %llu", line_count
);
2429 show_oops(t
, "Can't open blocked cookies file: %s",
2433 stats
= g_strdup_printf(XT_DOCTYPE
2436 "<title>Statistics</title>"
2438 "<h1>Statistics</h1>"
2440 "Cookies blocked(*) this session: %llu"
2442 "<p><small><b>*</b> results vary based on settings"
2448 load_webkit_string(t
, stats
, XT_URI_ABOUT_STATS
);
2455 blank(struct tab
*t
, struct karg
*args
)
2458 show_oops_s("about invalid parameters");
2460 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2465 about(struct tab
*t
, struct karg
*args
)
2470 show_oops_s("about invalid parameters");
2472 about
= g_strdup_printf(XT_DOCTYPE
2475 "<title>About</title>"
2479 "<b>Version: %s</b><p>"
2482 "<li>Marco Peereboom <marco@peereboom.us></li>"
2483 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2484 "<li>Edd Barrett <vext01@gmail.com> </li>"
2485 "<li>Todd T. Fries <todd@fries.net> </li>"
2487 "Copyrights and licenses can be found on the XXXterm "
2488 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2494 load_webkit_string(t
, about
, XT_URI_ABOUT_ABOUT
);
2501 help(struct tab
*t
, struct karg
*args
)
2506 show_oops_s("help invalid parameters");
2511 "<title>XXXterm</title>"
2512 "<meta http-equiv=\"REFRESH\" content=\"0;"
2513 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2516 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2517 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2518 "cgi-bin/man-cgi?xxxterm</a>"
2523 load_webkit_string(t
, help
, XT_URI_ABOUT_HELP
);
2529 * update all favorite tabs apart from one. Pass NULL if
2530 * you want to update all.
2533 update_favorite_tabs(struct tab
*apart_from
)
2536 if (!updating_fl_tabs
) {
2537 updating_fl_tabs
= 1; /* stop infinite recursion */
2538 TAILQ_FOREACH(t
, &tabs
, entry
)
2539 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2540 && (t
!= apart_from
))
2541 xtp_page_fl(t
, NULL
);
2542 updating_fl_tabs
= 0;
2546 /* show a list of favorites (bookmarks) */
2548 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2550 char file
[PATH_MAX
];
2552 char *uri
= NULL
, *title
= NULL
;
2553 size_t len
, lineno
= 0;
2555 char *header
, *body
, *tmp
, *html
= NULL
;
2557 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2560 warn("%s: bad param", __func__
);
2562 /* mark tab as favorite list */
2563 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2565 /* new session key */
2566 if (!updating_fl_tabs
)
2567 generate_xtp_session_key(&fl_session_key
);
2569 /* open favorites */
2570 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2571 if ((f
= fopen(file
, "r")) == NULL
) {
2572 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2577 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2578 "<title>Favorites</title>\n"
2581 "<h1>Favorites</h1>\n",
2585 body
= g_strdup_printf("<div align='center'><table><tr>"
2586 "<th style='width: 4%%'>#</th><th>Link</th>"
2587 "<th style='width: 15%%'>Remove</th></tr>\n");
2590 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2591 if (feof(f
) || ferror(f
))
2599 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2600 if (feof(f
) || ferror(f
)) {
2601 show_oops(t
, "favorites file corrupt");
2607 body
= g_strdup_printf("%s<tr>"
2609 "<td><a href='%s'>%s</a></td>"
2610 "<td style='text-align: center'>"
2611 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2613 body
, i
, uri
, title
,
2614 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2626 /* if none, say so */
2629 body
= g_strdup_printf("%s<tr>"
2630 "<td colspan='3' style='text-align: center'>"
2631 "No favorites - To add one use the 'favadd' command."
2632 "</td></tr>", body
);
2643 html
= g_strdup_printf("%s%s</table></div></html>",
2645 load_webkit_string(t
, html
, XT_URI_ABOUT_FAVORITES
);
2648 update_favorite_tabs(t
);
2661 getparams(char *cmd
, char *cmp
)
2666 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2667 rv
= cmd
+ strlen(cmp
);
2670 if (strlen(rv
) == 0)
2679 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2680 size_t cert_count
, char *title
)
2682 gnutls_datum_t cinfo
;
2683 char *tmp
, *header
, *body
, *footer
;
2686 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2687 footer
= g_strdup("</body></html>");
2688 body
= g_strdup("");
2690 for (i
= 0; i
< cert_count
; i
++) {
2691 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2696 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2697 body
, i
, cinfo
.data
);
2698 gnutls_free(cinfo
.data
);
2702 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2706 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2711 ca_cmd(struct tab
*t
, struct karg
*args
)
2714 int rv
= 1, certs
= 0, certs_read
;
2717 gnutls_x509_crt_t
*c
= NULL
;
2718 char *certs_buf
= NULL
, *s
;
2720 /* yeah yeah stat race */
2721 if (stat(ssl_ca_file
, &sb
)) {
2722 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2726 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2727 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2731 certs_buf
= g_malloc(sb
.st_size
+ 1);
2732 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2733 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2736 certs_buf
[sb
.st_size
] = '\0';
2739 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2741 s
+= strlen("BEGIN CERTIFICATE");
2744 bzero(&dt
, sizeof dt
);
2745 dt
.data
= certs_buf
;
2746 dt
.size
= sb
.st_size
;
2747 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2748 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2749 GNUTLS_X509_FMT_PEM
, 0);
2750 if (certs_read
<= 0) {
2751 show_oops(t
, "No cert(s) available");
2754 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2767 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2770 struct addrinfo hints
, *res
= NULL
, *ai
;
2774 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2777 su
= soup_uri_new(uri
);
2780 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2783 snprintf(port
, sizeof port
, "%d", su
->port
);
2784 bzero(&hints
, sizeof(struct addrinfo
));
2785 hints
.ai_flags
= AI_CANONNAME
;
2786 hints
.ai_family
= AF_UNSPEC
;
2787 hints
.ai_socktype
= SOCK_STREAM
;
2789 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2792 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2793 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2796 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2799 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2803 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2808 strlcpy(domain
, su
->host
, domain_sz
);
2819 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2822 gnutls_deinit(gsession
);
2824 gnutls_certificate_free_credentials(xcred
);
2830 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2831 gnutls_certificate_credentials_t
*xc
)
2833 gnutls_certificate_credentials_t xcred
;
2834 gnutls_session_t gsession
;
2837 if (gs
== NULL
|| xc
== NULL
)
2843 gnutls_certificate_allocate_credentials(&xcred
);
2844 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2845 GNUTLS_X509_FMT_PEM
);
2846 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2847 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2848 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2849 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2850 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2851 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2853 gnutls_error_is_fatal(rv
),
2854 gnutls_strerror_name(rv
));
2855 stop_tls(gsession
, xcred
);
2859 gnutls_credentials_type_t cred
;
2860 cred
= gnutls_auth_get_type(gsession
);
2861 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2862 stop_tls(gsession
, xcred
);
2874 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2878 const gnutls_datum_t
*cl
;
2879 gnutls_x509_crt_t
*all_certs
;
2882 if (certs
== NULL
|| cert_count
== NULL
)
2884 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2886 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2890 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2891 for (i
= 0; i
< len
; i
++) {
2892 gnutls_x509_crt_init(&all_certs
[i
]);
2893 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2894 GNUTLS_X509_FMT_PEM
< 0)) {
2908 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2912 for (i
= 0; i
< cert_count
; i
++)
2913 gnutls_x509_crt_deinit(certs
[i
]);
2918 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2919 size_t cert_count
, char *domain
)
2922 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2927 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2930 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2931 if ((f
= fopen(file
, "w")) == NULL
) {
2932 show_oops(t
, "Can't create cert file %s %s",
2933 file
, strerror(errno
));
2937 for (i
= 0; i
< cert_count
; i
++) {
2938 cert_buf_sz
= sizeof cert_buf
;
2939 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2940 cert_buf
, &cert_buf_sz
)) {
2941 show_oops(t
, "gnutls_x509_crt_export failed");
2944 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2945 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2950 /* not the best spot but oh well */
2951 gdk_color_parse("lightblue", &color
);
2952 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2953 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2954 gdk_color_parse(XT_COLOR_BLACK
, &color
);
2955 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2961 load_compare_cert(struct tab
*t
, struct karg
*args
)
2964 char domain
[8182], file
[PATH_MAX
];
2965 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2966 int s
= -1, rv
= 1, i
;
2970 gnutls_session_t gsession
;
2971 gnutls_x509_crt_t
*certs
;
2972 gnutls_certificate_credentials_t xcred
;
2977 if ((uri
= get_uri(t
->wv
)) == NULL
)
2980 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2984 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2985 show_oops(t
, "Start TLS failed");
2990 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2991 show_oops(t
, "Can't get connection certificates");
2995 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2996 if ((f
= fopen(file
, "r")) == NULL
)
2999 for (i
= 0; i
< cert_count
; i
++) {
3000 cert_buf_sz
= sizeof cert_buf
;
3001 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3002 cert_buf
, &cert_buf_sz
)) {
3005 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3006 rv
= -1; /* critical */
3009 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3010 rv
= -1; /* critical */
3019 free_connection_certs(certs
, cert_count
);
3021 /* we close the socket first for speed */
3024 stop_tls(gsession
, xcred
);
3030 cert_cmd(struct tab
*t
, struct karg
*args
)
3033 char *action
, domain
[8182];
3036 gnutls_session_t gsession
;
3037 gnutls_x509_crt_t
*certs
;
3038 gnutls_certificate_credentials_t xcred
;
3043 if ((action
= getparams(args
->s
, "cert")))
3048 if ((uri
= get_uri(t
->wv
)) == NULL
) {
3049 show_oops(t
, "Invalid URI");
3053 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3054 show_oops(t
, "Invalid certidicate URI: %s", uri
);
3059 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3060 show_oops(t
, "Start TLS failed");
3065 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3066 show_oops(t
, "get_connection_certs failed");
3070 if (!strcmp(action
, "show"))
3071 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3072 else if (!strcmp(action
, "save"))
3073 save_certs(t
, certs
, cert_count
, domain
);
3075 show_oops(t
, "Invalid command: %s", action
);
3077 free_connection_certs(certs
, cert_count
);
3079 /* we close the socket first for speed */
3082 stop_tls(gsession
, xcred
);
3088 remove_cookie(int index
)
3094 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3096 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3098 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3102 print_cookie("remove cookie", c
);
3103 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3108 soup_cookies_free(cf
);
3114 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
3117 char *tmp
, *header
, *body
, *footer
;
3118 int p_js
= 0, s_js
= 0;
3120 /* we set this to indicate we want to manually do navaction */
3121 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3123 if (g_str_has_prefix(args
, "show a") ||
3124 !strcmp(args
, "show")) {
3128 } else if (g_str_has_prefix(args
, "show p")) {
3129 /* show persistent */
3131 } else if (g_str_has_prefix(args
, "show s")) {
3137 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
3139 footer
= g_strdup("</body></html>");
3140 body
= g_strdup("");
3145 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3147 RB_FOREACH(d
, domain_list
, wl
) {
3151 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3159 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3161 RB_FOREACH(d
, domain_list
, wl
) {
3165 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3170 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3175 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3177 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3183 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3185 char file
[PATH_MAX
];
3187 char *line
= NULL
, *lt
= NULL
;
3190 char *dom
= NULL
, *dom_save
= NULL
;
3197 if (t
== NULL
|| args
== NULL
)
3200 if (runtime_settings
[0] == '\0')
3203 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3204 if ((f
= fopen(file
, "r+")) == NULL
)
3207 uri
= get_uri(t
->wv
);
3208 dom
= find_domain(uri
, 1);
3209 if (uri
== NULL
|| dom
== NULL
) {
3210 show_oops(t
, "Can't add domain to %s white list",
3211 js
? "JavaScript" : "cookie");
3215 if (g_str_has_prefix(args
->s
, "save d")) {
3217 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3218 show_oops(t
, "invalid domain: %s", dom
);
3221 flags
= XT_WL_TOPLEVEL
;
3222 } else if (g_str_has_prefix(args
->s
, "save f") ||
3223 !strcmp(args
->s
, "save")) {
3228 show_oops(t
, "invalid command: %s", args
->s
);
3232 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3235 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3238 if (!strcmp(line
, lt
))
3244 fprintf(f
, "%s\n", lt
);
3249 d
= wl_find(dom_save
, &js_wl
);
3251 settings_add("js_wl", dom_save
);
3252 d
= wl_find(dom_save
, &js_wl
);
3256 d
= wl_find(dom_save
, &c_wl
);
3258 settings_add("cookie_wl", dom_save
);
3259 d
= wl_find(dom_save
, &c_wl
);
3263 /* find and add to persistent jar */
3264 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3265 for (;cf
; cf
= cf
->next
) {
3267 if (!strcmp(dom_save
, ci
->domain
) ||
3268 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3269 c
= soup_cookie_copy(ci
);
3270 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3273 soup_cookies_free(cf
);
3291 js_show_wl(struct tab
*t
, struct karg
*args
)
3293 wl_show(t
, "show all", "JavaScript White List", &js_wl
);
3299 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3301 wl_show(t
, "show all", "Cookie White List", &c_wl
);
3307 cookie_cmd(struct tab
*t
, struct karg
*args
)
3312 if ((cmd
= getparams(args
->s
, "cookie")))
3318 if (g_str_has_prefix(cmd
, "show")) {
3319 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
3320 } else if (g_str_has_prefix(cmd
, "save")) {
3323 } else if (g_str_has_prefix(cmd
, "toggle")) {
3325 if (g_str_has_prefix(cmd
, "toggle d"))
3326 a
.i
|= XT_WL_TOPLEVEL
;
3330 } else if (g_str_has_prefix(cmd
, "delete")) {
3331 show_oops(t
, "'cookie delete' currently unimplemented");
3333 show_oops(t
, "unknown cookie command: %s", cmd
);
3339 js_cmd(struct tab
*t
, struct karg
*args
)
3344 if ((cmd
= getparams(args
->s
, "js")))
3349 if (g_str_has_prefix(cmd
, "show")) {
3350 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
3351 } else if (g_str_has_prefix(cmd
, "save")) {
3354 } else if (g_str_has_prefix(cmd
, "toggle")) {
3356 if (g_str_has_prefix(cmd
, "toggle d"))
3357 a
.i
|= XT_WL_TOPLEVEL
;
3361 } else if (g_str_has_prefix(cmd
, "delete")) {
3362 show_oops(t
, "'js delete' currently unimplemented");
3364 show_oops(t
, "unknown js command: %s", cmd
);
3370 add_favorite(struct tab
*t
, struct karg
*args
)
3372 char file
[PATH_MAX
];
3375 size_t urilen
, linelen
;
3376 const gchar
*uri
, *title
;
3381 /* don't allow adding of xtp pages to favorites */
3382 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3383 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3387 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3388 if ((f
= fopen(file
, "r+")) == NULL
) {
3389 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3393 title
= webkit_web_view_get_title(t
->wv
);
3394 uri
= get_uri(t
->wv
);
3399 if (title
== NULL
|| uri
== NULL
) {
3400 show_oops(t
, "can't add page to favorites");
3404 urilen
= strlen(uri
);
3407 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3408 if (feof(f
) || ferror(f
))
3411 if (linelen
== urilen
&& !strcmp(line
, uri
))
3418 fprintf(f
, "\n%s\n%s", title
, uri
);
3424 update_favorite_tabs(NULL
);
3430 navaction(struct tab
*t
, struct karg
*args
)
3432 WebKitWebHistoryItem
*item
;
3434 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3435 t
->tab_id
, args
->i
);
3438 if (args
->i
== XT_NAV_BACK
)
3439 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3441 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3443 return (XT_CB_PASSTHROUGH
);
3444 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3446 return (XT_CB_PASSTHROUGH
);
3451 webkit_web_view_go_back(t
->wv
);
3453 case XT_NAV_FORWARD
:
3454 webkit_web_view_go_forward(t
->wv
);
3457 webkit_web_view_reload(t
->wv
);
3459 case XT_NAV_RELOAD_CACHE
:
3460 webkit_web_view_reload_bypass_cache(t
->wv
);
3463 return (XT_CB_PASSTHROUGH
);
3467 move(struct tab
*t
, struct karg
*args
)
3469 GtkAdjustment
*adjust
;
3470 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3475 case XT_MOVE_BOTTOM
:
3477 case XT_MOVE_PAGEDOWN
:
3478 case XT_MOVE_PAGEUP
:
3479 case XT_MOVE_HALFDOWN
:
3480 case XT_MOVE_HALFUP
:
3481 adjust
= t
->adjust_v
;
3484 adjust
= t
->adjust_h
;
3488 pos
= gtk_adjustment_get_value(adjust
);
3489 ps
= gtk_adjustment_get_page_size(adjust
);
3490 upper
= gtk_adjustment_get_upper(adjust
);
3491 lower
= gtk_adjustment_get_lower(adjust
);
3492 si
= gtk_adjustment_get_step_increment(adjust
);
3493 pi
= gtk_adjustment_get_page_increment(adjust
);
3496 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3497 "max %f si %f pi %f\n",
3498 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3499 pos
, ps
, upper
, lower
, max
, si
, pi
);
3505 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3510 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3512 case XT_MOVE_BOTTOM
:
3513 case XT_MOVE_FARRIGHT
:
3514 gtk_adjustment_set_value(adjust
, max
);
3517 case XT_MOVE_FARLEFT
:
3518 gtk_adjustment_set_value(adjust
, lower
);
3520 case XT_MOVE_PAGEDOWN
:
3522 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3524 case XT_MOVE_PAGEUP
:
3526 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3528 case XT_MOVE_HALFDOWN
:
3530 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3532 case XT_MOVE_HALFUP
:
3534 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3537 return (XT_CB_PASSTHROUGH
);
3540 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3542 return (XT_CB_HANDLED
);
3546 url_set_visibility(void)
3550 TAILQ_FOREACH(t
, &tabs
, entry
) {
3551 if (show_url
== 0) {
3552 gtk_widget_hide(t
->toolbar
);
3555 gtk_widget_show(t
->toolbar
);
3560 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3563 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3565 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3569 statusbar_set_visibility(void)
3573 TAILQ_FOREACH(t
, &tabs
, entry
) {
3574 if (show_statusbar
== 0) {
3575 gtk_widget_hide(t
->statusbar
);
3578 gtk_widget_show(t
->statusbar
);
3583 url_set(struct tab
*t
, int enable_url_entry
)
3588 show_url
= enable_url_entry
;
3590 if (enable_url_entry
) {
3591 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3592 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3593 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3595 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3596 GTK_ENTRY_ICON_PRIMARY
);
3598 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3599 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3600 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3601 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3607 fullscreen(struct tab
*t
, struct karg
*args
)
3609 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3612 return (XT_CB_PASSTHROUGH
);
3614 if (show_url
== 0) {
3622 url_set_visibility();
3623 notebook_tab_set_visibility(notebook
);
3625 return (XT_CB_HANDLED
);
3629 statusaction(struct tab
*t
, struct karg
*args
)
3631 int rv
= XT_CB_HANDLED
;
3633 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3636 return (XT_CB_PASSTHROUGH
);
3639 case XT_STATUSBAR_SHOW
:
3640 if (show_statusbar
== 0) {
3642 statusbar_set_visibility();
3645 case XT_STATUSBAR_HIDE
:
3646 if (show_statusbar
== 1) {
3648 statusbar_set_visibility();
3656 urlaction(struct tab
*t
, struct karg
*args
)
3658 int rv
= XT_CB_HANDLED
;
3660 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3663 return (XT_CB_PASSTHROUGH
);
3667 if (show_url
== 0) {
3669 url_set_visibility();
3673 if (show_url
== 1) {
3675 url_set_visibility();
3683 tabaction(struct tab
*t
, struct karg
*args
)
3685 int rv
= XT_CB_HANDLED
;
3689 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3692 return (XT_CB_PASSTHROUGH
);
3696 if ((url
= getparams(args
->s
, "tabnew")))
3697 create_new_tab(url
, NULL
, 1);
3699 create_new_tab(NULL
, NULL
, 1);
3704 case XT_TAB_DELQUIT
:
3705 if (gtk_notebook_get_n_pages(notebook
) > 1)
3711 if ((url
= getparams(args
->s
, "open")) ||
3712 ((url
= getparams(args
->s
, "op"))) ||
3713 ((url
= getparams(args
->s
, "o"))))
3716 rv
= XT_CB_PASSTHROUGH
;
3722 if (show_tabs
== 0) {
3724 notebook_tab_set_visibility(notebook
);
3728 if (show_tabs
== 1) {
3730 notebook_tab_set_visibility(notebook
);
3733 case XT_TAB_UNDO_CLOSE
:
3734 if (undo_count
== 0) {
3735 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3739 u
= TAILQ_FIRST(&undos
);
3740 create_new_tab(u
->uri
, u
, 1);
3742 TAILQ_REMOVE(&undos
, u
, entry
);
3744 /* u->history is freed in create_new_tab() */
3749 rv
= XT_CB_PASSTHROUGH
;
3763 resizetab(struct tab
*t
, struct karg
*args
)
3765 if (t
== NULL
|| args
== NULL
) {
3766 show_oops_s("resizetab invalid parameters");
3767 return (XT_CB_PASSTHROUGH
);
3770 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3771 t
->tab_id
, args
->i
);
3773 adjustfont_webkit(t
, args
->i
);
3775 return (XT_CB_HANDLED
);
3779 movetab(struct tab
*t
, struct karg
*args
)
3784 if (t
== NULL
|| args
== NULL
) {
3785 show_oops_s("movetab invalid parameters");
3786 return (XT_CB_PASSTHROUGH
);
3789 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3790 t
->tab_id
, args
->i
);
3792 if (args
->i
== XT_TAB_INVALID
)
3793 return (XT_CB_PASSTHROUGH
);
3795 if (args
->i
< XT_TAB_INVALID
) {
3796 /* next or previous tab */
3797 if (TAILQ_EMPTY(&tabs
))
3798 return (XT_CB_PASSTHROUGH
);
3802 /* if at the last page, loop around to the first */
3803 if (gtk_notebook_get_current_page(notebook
) ==
3804 gtk_notebook_get_n_pages(notebook
) - 1)
3805 gtk_notebook_set_current_page(notebook
, 0);
3807 gtk_notebook_next_page(notebook
);
3810 /* if at the first page, loop around to the last */
3811 if (gtk_notebook_current_page(notebook
) == 0)
3812 gtk_notebook_set_current_page(notebook
,
3813 gtk_notebook_get_n_pages(notebook
) - 1);
3815 gtk_notebook_prev_page(notebook
);
3818 gtk_notebook_set_current_page(notebook
, 0);
3821 gtk_notebook_set_current_page(notebook
, -1);
3824 return (XT_CB_PASSTHROUGH
);
3827 return (XT_CB_HANDLED
);
3832 if (t
->tab_id
== x
) {
3833 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3834 return (XT_CB_HANDLED
);
3837 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3838 if (tt
->tab_id
== x
) {
3839 gtk_notebook_set_current_page(notebook
, x
);
3840 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3846 return (XT_CB_HANDLED
);
3850 command(struct tab
*t
, struct karg
*args
)
3852 char *s
= NULL
, *ss
= NULL
;
3856 if (t
== NULL
|| args
== NULL
) {
3857 show_oops_s("command invalid parameters");
3858 return (XT_CB_PASSTHROUGH
);
3877 case XT_CMD_OPEN_CURRENT
:
3880 case XT_CMD_TABNEW_CURRENT
:
3881 if (!s
) /* FALL THROUGH? */
3883 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3884 ss
= g_strdup_printf("%s%s", s
, uri
);
3889 show_oops(t
, "command: invalid opcode %d", args
->i
);
3890 return (XT_CB_PASSTHROUGH
);
3893 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3895 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3896 gdk_color_parse(XT_COLOR_WHITE
, &color
);
3897 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3899 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3900 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3905 return (XT_CB_HANDLED
);
3909 * Return a new string with a download row (in html)
3910 * appended. Old string is freed.
3913 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3916 WebKitDownloadStatus stat
;
3917 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3919 char cur_sz
[FMT_SCALED_STRSIZE
];
3920 char tot_sz
[FMT_SCALED_STRSIZE
];
3923 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3925 /* All actions wil take this form:
3926 * xxxt://class/seskey
3928 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3929 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3931 stat
= webkit_download_get_status(dl
->download
);
3934 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3935 status_html
= g_strdup_printf("Finished");
3936 cmd_html
= g_strdup_printf(
3937 "<a href='%s%d/%d'>Remove</a>",
3938 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3940 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3941 /* gather size info */
3942 progress
= 100 * webkit_download_get_progress(dl
->download
);
3945 webkit_download_get_current_size(dl
->download
), cur_sz
);
3947 webkit_download_get_total_size(dl
->download
), tot_sz
);
3949 status_html
= g_strdup_printf(
3950 "<div style='width: 100%%' align='center'>"
3951 "<div class='progress-outer'>"
3952 "<div class='progress-inner' style='width: %.2f%%'>"
3953 "</div></div></div>"
3954 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3955 progress
, cur_sz
, tot_sz
, progress
);
3957 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3958 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3962 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3963 status_html
= g_strdup_printf("Cancelled");
3964 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3965 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3967 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3968 status_html
= g_strdup_printf("Error!");
3969 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3970 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3972 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3973 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3974 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3975 status_html
= g_strdup_printf("Starting");
3978 show_oops(t
, "%s: unknown download status", __func__
);
3981 new_html
= g_strdup_printf(
3982 "%s\n<tr><td>%s</td><td>%s</td>"
3983 "<td style='text-align:center'>%s</td></tr>\n",
3984 html
, basename(webkit_download_get_destination_uri(dl
->download
)),
3985 status_html
, cmd_html
);
3989 g_free(status_html
);
4000 * update all download tabs apart from one. Pass NULL if
4001 * you want to update all.
4004 update_download_tabs(struct tab
*apart_from
)
4007 if (!updating_dl_tabs
) {
4008 updating_dl_tabs
= 1; /* stop infinite recursion */
4009 TAILQ_FOREACH(t
, &tabs
, entry
)
4010 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4011 && (t
!= apart_from
))
4012 xtp_page_dl(t
, NULL
);
4013 updating_dl_tabs
= 0;
4018 * update all cookie tabs apart from one. Pass NULL if
4019 * you want to update all.
4022 update_cookie_tabs(struct tab
*apart_from
)
4025 if (!updating_cl_tabs
) {
4026 updating_cl_tabs
= 1; /* stop infinite recursion */
4027 TAILQ_FOREACH(t
, &tabs
, entry
)
4028 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4029 && (t
!= apart_from
))
4030 xtp_page_cl(t
, NULL
);
4031 updating_cl_tabs
= 0;
4036 * update all history tabs apart from one. Pass NULL if
4037 * you want to update all.
4040 update_history_tabs(struct tab
*apart_from
)
4044 if (!updating_hl_tabs
) {
4045 updating_hl_tabs
= 1; /* stop infinite recursion */
4046 TAILQ_FOREACH(t
, &tabs
, entry
)
4047 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4048 && (t
!= apart_from
))
4049 xtp_page_hl(t
, NULL
);
4050 updating_hl_tabs
= 0;
4054 /* cookie management XTP page */
4056 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4058 char *header
, *body
, *footer
, *page
, *tmp
;
4059 int i
= 1; /* all ids start 1 */
4060 GSList
*sc
, *pc
, *pc_start
;
4062 char *type
, *table_headers
;
4063 char *last_domain
= strdup("");
4065 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4068 show_oops_s("%s invalid parameters", __func__
);
4071 /* mark this tab as cookie jar */
4072 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
4074 /* Generate a new session key */
4075 if (!updating_cl_tabs
)
4076 generate_xtp_session_key(&cl_session_key
);
4079 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4080 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
4081 "</head><body><h1>Cookie Jar</h1>\n");
4084 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
4091 "<th>HTTP<br />only</th>"
4092 "<th>Rm</th></tr>\n");
4094 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4095 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4099 for (; sc
; sc
= sc
->next
) {
4102 if (strcmp(last_domain
, c
->domain
) != 0) {
4105 last_domain
= strdup(c
->domain
);
4109 body
= g_strdup_printf("%s</table></div>"
4111 body
, c
->domain
, table_headers
);
4115 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4116 c
->domain
, table_headers
);
4121 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4122 if (soup_cookie_equal(pc
->data
, c
)) {
4123 type
= "Session + Persistent";
4128 body
= g_strdup_printf(
4130 "<td style='width: text-align: center'>%s</td>"
4131 "<td style='width: 1px'>%s</td>"
4132 "<td style='width=70%%;overflow: visible'>"
4133 " <textarea rows='4'>%s</textarea>"
4137 "<td style='width: 1px; text-align: center'>%d</td>"
4138 "<td style='width: 1px; text-align: center'>%d</td>"
4139 "<td style='width: 1px; text-align: center'>"
4140 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4147 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4162 soup_cookies_free(sc
);
4163 soup_cookies_free(pc
);
4165 /* small message if there are none */
4167 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4168 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4172 footer
= g_strdup_printf("</table></div></body></html>");
4174 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4179 g_free(table_headers
);
4180 g_free(last_domain
);
4182 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4183 update_cookie_tabs(t
);
4191 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4193 char *header
, *body
, *footer
, *page
, *tmp
;
4195 int i
= 1; /* all ids start 1 */
4197 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4200 show_oops_s("%s invalid parameters", __func__
);
4204 /* mark this tab as history manager */
4205 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4207 /* Generate a new session key */
4208 if (!updating_hl_tabs
)
4209 generate_xtp_session_key(&hl_session_key
);
4212 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4213 "<title>History</title>\n"
4216 "<h1>History</h1>\n",
4220 body
= g_strdup_printf("<div align='center'><table><tr>"
4221 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4223 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4225 body
= g_strdup_printf(
4227 "<td><a href='%s'>%s</a></td>"
4229 "<td style='text-align: center'>"
4230 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4231 body
, h
->uri
, h
->uri
, h
->title
,
4232 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4233 XT_XTP_HL_REMOVE
, i
);
4239 /* small message if there are none */
4242 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4243 "colspan='3'>No History</td></tr>\n", body
);
4248 footer
= g_strdup_printf("</table></div></body></html>");
4250 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4253 * update all history manager tabs as the xtp session
4254 * key has now changed. No need to update the current tab.
4255 * Already did that above.
4257 update_history_tabs(t
);
4263 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4270 * Generate a web page detailing the status of any downloads
4273 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4275 struct download
*dl
;
4276 char *header
, *body
, *footer
, *page
, *tmp
;
4280 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4283 show_oops_s("%s invalid parameters", __func__
);
4286 /* mark as a download manager tab */
4287 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4290 * Generate a new session key for next page instance.
4291 * This only happens for the top level call to xtp_page_dl()
4292 * in which case updating_dl_tabs is 0.
4294 if (!updating_dl_tabs
)
4295 generate_xtp_session_key(&dl_session_key
);
4297 /* header - with refresh so as to update */
4298 if (refresh_interval
>= 1)
4299 ref
= g_strdup_printf(
4300 "<meta http-equiv='refresh' content='%u"
4301 ";url=%s%d/%s/%d' />\n",
4311 header
= g_strdup_printf(
4313 "<title>Downloads</title>\n%s%s</head>\n",
4314 XT_DOCTYPE XT_HTML_TAG
,
4318 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4319 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4320 "</p><table><tr><th style='width: 60%%'>"
4321 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4322 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4324 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4325 body
= xtp_page_dl_row(t
, body
, dl
);
4329 /* message if no downloads in list */
4332 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4333 " style='text-align: center'>"
4334 "No downloads</td></tr>\n", body
);
4339 footer
= g_strdup_printf("</table></div></body></html>");
4341 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4345 * update all download manager tabs as the xtp session
4346 * key has now changed. No need to update the current tab.
4347 * Already did that above.
4349 update_download_tabs(t
);
4356 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4363 search(struct tab
*t
, struct karg
*args
)
4367 if (t
== NULL
|| args
== NULL
) {
4368 show_oops_s("search invalid parameters");
4371 if (t
->search_text
== NULL
) {
4372 if (global_search
== NULL
)
4373 return (XT_CB_PASSTHROUGH
);
4375 t
->search_text
= g_strdup(global_search
);
4376 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4377 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4381 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4382 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4385 case XT_SEARCH_NEXT
:
4386 d
= t
->search_forward
;
4388 case XT_SEARCH_PREV
:
4389 d
= !t
->search_forward
;
4392 return (XT_CB_PASSTHROUGH
);
4395 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4397 return (XT_CB_HANDLED
);
4400 struct settings_args
{
4406 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4409 struct settings_args
*sa
= cb_args
;
4414 if (s
->flags
& XT_SF_RUNTIME
)
4420 *sa
->body
= g_strdup_printf(
4422 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4423 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4435 set(struct tab
*t
, struct karg
*args
)
4437 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
4439 struct settings_args sa
;
4441 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
4442 bzero(&sa
, sizeof sa
);
4446 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4447 "\n<head><title>Settings</title>\n"
4448 "</head><body><h1>Settings</h1>\n");
4451 body
= g_strdup_printf("<div align='center'><table><tr>"
4452 "<th align='left'>Setting</th>"
4453 "<th align='left'>Value</th></tr>\n");
4455 settings_walk(print_setting
, &sa
);
4458 /* small message if there are none */
4461 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4462 "colspan='2'>No settings</td></tr>\n", body
);
4467 footer
= g_strdup_printf("</table></div></body></html>");
4469 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4475 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4477 show_oops(t
, "Invalid command: %s", pars
);
4479 return (XT_CB_PASSTHROUGH
);
4483 session_save(struct tab
*t
, char *filename
, char **ret
)
4489 f
+= strlen("save");
4490 while (*f
== ' ' && *f
!= '\0')
4496 if (f
[0] == '.' || f
[0] == '/')
4500 if (save_tabs(t
, &a
))
4502 strlcpy(named_session
, f
, sizeof named_session
);
4510 session_open(struct tab
*t
, char *filename
, char **ret
)
4516 f
+= strlen("open");
4517 while (*f
== ' ' && *f
!= '\0')
4523 if (f
[0] == '.' || f
[0] == '/')
4527 a
.i
= XT_SES_CLOSETABS
;
4528 if (open_tabs(t
, &a
))
4531 strlcpy(named_session
, f
, sizeof named_session
);
4539 session_delete(struct tab
*t
, char *filename
, char **ret
)
4541 char file
[PATH_MAX
];
4545 f
+= strlen("delete");
4546 while (*f
== ' ' && *f
!= '\0')
4552 if (f
[0] == '.' || f
[0] == '/')
4555 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, f
);
4559 if (!strcmp(f
, named_session
))
4560 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4561 sizeof named_session
);
4569 session_cmd(struct tab
*t
, struct karg
*args
)
4571 char *action
= NULL
;
4572 char *filename
= NULL
;
4577 if ((action
= getparams(args
->s
, "session")))
4582 if (!strcmp(action
, "show"))
4583 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4584 XT_SAVED_TABS_FILE
: named_session
);
4585 else if (g_str_has_prefix(action
, "save ")) {
4586 if (session_save(t
, action
, &filename
)) {
4587 show_oops(t
, "Can't save session: %s",
4588 filename
? filename
: "INVALID");
4591 } else if (g_str_has_prefix(action
, "open ")) {
4592 if (session_open(t
, action
, &filename
)) {
4593 show_oops(t
, "Can't open session: %s",
4594 filename
? filename
: "INVALID");
4597 } else if (g_str_has_prefix(action
, "delete ")) {
4598 if (session_delete(t
, action
, &filename
)) {
4599 show_oops(t
, "Can't delete session: %s",
4600 filename
? filename
: "INVALID");
4604 show_oops(t
, "Invalid command: %s", action
);
4606 return (XT_CB_PASSTHROUGH
);
4610 * Make a hardcopy of the page
4613 print_page(struct tab
*t
, struct karg
*args
)
4615 WebKitWebFrame
*frame
;
4617 GtkPrintOperation
*op
;
4618 GtkPrintOperationAction action
;
4619 GtkPrintOperationResult print_res
;
4620 GError
*g_err
= NULL
;
4621 int marg_l
, marg_r
, marg_t
, marg_b
;
4623 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4625 ps
= gtk_page_setup_new();
4626 op
= gtk_print_operation_new();
4627 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4628 frame
= webkit_web_view_get_main_frame(t
->wv
);
4630 /* the default margins are too small, so we will bump them */
4631 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4632 XT_PRINT_EXTRA_MARGIN
;
4633 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4634 XT_PRINT_EXTRA_MARGIN
;
4635 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4636 XT_PRINT_EXTRA_MARGIN
;
4637 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4638 XT_PRINT_EXTRA_MARGIN
;
4641 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4642 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4643 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4644 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4646 gtk_print_operation_set_default_page_setup(op
, ps
);
4648 /* this appears to free 'op' and 'ps' */
4649 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4651 /* check it worked */
4652 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4653 show_oops_s("can't print: %s", g_err
->message
);
4654 g_error_free (g_err
);
4662 go_home(struct tab
*t
, struct karg
*args
)
4669 restart(struct tab
*t
, struct karg
*args
)
4673 a
.s
= XT_RESTART_TABS_FILE
;
4675 execvp(start_argv
[0], start_argv
);
4681 #define CTRL GDK_CONTROL_MASK
4682 #define MOD1 GDK_MOD1_MASK
4683 #define SHFT GDK_SHIFT_MASK
4685 /* inherent to GTK not all keys will be caught at all times */
4686 /* XXX sort key bindings */
4687 struct key_binding
{
4693 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4695 { "cookiejar", MOD1
, 0, GDK_j
, {0} },
4696 { "downloadmgr", MOD1
, 0, GDK_d
, {0} },
4697 { "history", MOD1
, 0, GDK_h
, {0} },
4698 { "print", CTRL
, 0, GDK_p
, {0} },
4699 { "search", 0, 0, GDK_slash
, {0} },
4700 { "searchb", 0, 0, GDK_question
, {0} },
4701 { "command", 0, 0, GDK_colon
, {0} },
4702 { "quit", CTRL
, 0, GDK_q
, {0} },
4703 { "restart", MOD1
, 0, GDK_q
, {0} },
4704 { "togglejs", CTRL
, 0, GDK_j
, {0} }, /* XXX broken */
4705 { "togglecookie", MOD1
, 0, GDK_c
, {0} }, /* XXX broken */
4706 { "togglesrc", CTRL
, 0, GDK_s
, {0} },
4707 { "yankuri", 0, 0, GDK_y
, {0} },
4708 { "pasteuricur", 0, 0, GDK_p
, {0} },
4709 { "pasteurinew", 0, 0, GDK_P
, {0} },
4712 { "searchnext", 0, 0, GDK_n
, {0} },
4713 { "searchprevious", 0, 0, GDK_N
, {0} },
4716 { "focusaddress", 0, 0, GDK_F6
, {0} },
4717 { "focussearch", 0, 0, GDK_F7
, {0} },
4720 { "hinting", 0, 0, GDK_f
, {0} },
4722 /* custom stylesheet */
4723 { "userstyle", 0, 0, GDK_i
, {0} },
4726 { "goback", 0, 0, GDK_BackSpace
, {0} },
4727 { "goback", MOD1
, 0, GDK_Left
, {0} },
4728 { "goforward", SHFT
, 0, GDK_BackSpace
, {0} },
4729 { "goforward", MOD1
, 0, GDK_Right
, {0} },
4730 { "reload", 0, 0, GDK_F5
, {0} },
4731 { "reload", CTRL
, 0, GDK_r
, {0} },
4732 { "reloadforce", CTRL
, 0, GDK_R
, {0} },
4733 { "reload", CTRL
, 0, GDK_l
, {0} },
4734 { "favorites", MOD1
, 1, GDK_f
, {0} },
4736 /* vertical movement */
4737 { "scrolldown", 0, 0, GDK_j
, {0} },
4738 { "scrolldown", 0, 0, GDK_Down
, {0} },
4739 { "scrollup", 0, 0, GDK_Up
, {0} },
4740 { "scrollup", 0, 0, GDK_k
, {0} },
4741 { "scrollbottom", 0, 0, GDK_G
, {0} },
4742 { "scrollbottom", 0, 0, GDK_End
, {0} },
4743 { "scrolltop", 0, 0, GDK_Home
, {0} },
4744 { "scrolltop", 0, 0, GDK_g
, {0} },
4745 { "scrollpagedown", 0, 0, GDK_space
, {0} },
4746 { "scrollpagedown", CTRL
, 0, GDK_f
, {0} },
4747 { "scrollhalfdown", CTRL
, 0, GDK_d
, {0} },
4748 { "scrollpagedown", 0, 0, GDK_Page_Down
, {0} },
4749 { "scrollpageup", 0, 0, GDK_Page_Up
, {0} },
4750 { "scrollpageup", CTRL
, 0, GDK_b
, {0} },
4751 { "scrollhalfup", CTRL
, 0, GDK_u
, {0} },
4752 /* horizontal movement */
4753 { "scrollright", 0, 0, GDK_l
, {0} },
4754 { "scrollright", 0, 0, GDK_Right
, {0} },
4755 { "scrollleft", 0, 0, GDK_Left
, {0} },
4756 { "scrollleft", 0, 0, GDK_h
, {0} },
4757 { "scrollfarright", 0, 0, GDK_dollar
, {0} },
4758 { "scrollfarleft", 0, 0, GDK_0
, {0} },
4761 { "tabnew", CTRL
, 0, GDK_t
, {0} },
4762 { "tabclose", CTRL
, 1, GDK_w
, {0} },
4763 { "tabundoclose", 0, 0, GDK_U
, {0} },
4764 { "tabgoto1", CTRL
, 0, GDK_1
, {0} },
4765 { "tabgoto2", CTRL
, 0, GDK_2
, {0} },
4766 { "tabgoto3", CTRL
, 0, GDK_3
, {0} },
4767 { "tabgoto4", CTRL
, 0, GDK_4
, {0} },
4768 { "tabgoto5", CTRL
, 0, GDK_5
, {0} },
4769 { "tabgoto6", CTRL
, 0, GDK_6
, {0} },
4770 { "tabgoto7", CTRL
, 0, GDK_7
, {0} },
4771 { "tabgoto8", CTRL
, 0, GDK_8
, {0} },
4772 { "tabgoto9", CTRL
, 0, GDK_9
, {0} },
4773 { "tabgoto10", CTRL
, 0, GDK_0
, {0} },
4774 { "tabfirst", CTRL
, 0, GDK_less
, {0} },
4775 { "tablast", CTRL
, 0, GDK_greater
, {0} },
4776 { "tabprevious", CTRL
, 0, GDK_Left
, {0} },
4777 { "tabnext", CTRL
, 0, GDK_Right
, {0} },
4778 { "focusout", CTRL
, 0, GDK_minus
, {0} },
4779 { "focusin", CTRL
, 0, GDK_plus
, {0} },
4780 { "focusin", CTRL
, 0, GDK_equal
, {0} },
4782 /* command aliases (handy when -S flag is used) */
4783 { "promptopen", 0, 0, GDK_F9
, {0} },
4784 { "promptopencurrent", 0, 0, GDK_F10
, {0} },
4785 { "prompttabnew", 0, 0, GDK_F11
, {0} },
4786 { "prompttabnewcurrent",0, 0, GDK_F12
, {0} },
4788 TAILQ_HEAD(keybinding_list
, key_binding
);
4791 walk_kb(struct settings
*s
,
4792 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4794 struct key_binding
*k
;
4797 if (s
== NULL
|| cb
== NULL
) {
4798 show_oops_s("walk_kb invalid parameters");
4802 TAILQ_FOREACH(k
, &kbl
, entry
) {
4803 if (k
->name
== NULL
)
4808 if (gdk_keyval_name(k
->key
) == NULL
)
4811 strlcat(str
, k
->name
, sizeof str
);
4812 strlcat(str
, ",", sizeof str
);
4814 if (k
->mask
& GDK_SHIFT_MASK
)
4815 strlcat(str
, "S-", sizeof str
);
4816 if (k
->mask
& GDK_CONTROL_MASK
)
4817 strlcat(str
, "C-", sizeof str
);
4818 if (k
->mask
& GDK_MOD1_MASK
)
4819 strlcat(str
, "M1-", sizeof str
);
4820 if (k
->mask
& GDK_MOD2_MASK
)
4821 strlcat(str
, "M2-", sizeof str
);
4822 if (k
->mask
& GDK_MOD3_MASK
)
4823 strlcat(str
, "M3-", sizeof str
);
4824 if (k
->mask
& GDK_MOD4_MASK
)
4825 strlcat(str
, "M4-", sizeof str
);
4826 if (k
->mask
& GDK_MOD5_MASK
)
4827 strlcat(str
, "M5-", sizeof str
);
4829 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4830 cb(s
, str
, cb_args
);
4834 init_keybindings(void)
4837 struct key_binding
*k
;
4839 for (i
= 0; i
< LENGTH(keys
); i
++) {
4840 k
= g_malloc0(sizeof *k
);
4841 k
->name
= keys
[i
].name
;
4842 k
->mask
= keys
[i
].mask
;
4843 k
->use_in_entry
= keys
[i
].use_in_entry
;
4844 k
->key
= keys
[i
].key
;
4845 bcopy(&keys
[i
].arg
, &k
->arg
, sizeof k
->arg
);
4846 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4848 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4849 k
->name
? k
->name
: "unnamed key");
4854 keybinding_clearall(void)
4856 struct key_binding
*k
, *next
;
4858 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4859 next
= TAILQ_NEXT(k
, entry
);
4860 if (k
->name
== NULL
)
4863 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4864 k
->name
? k
->name
: "unnamed key");
4865 TAILQ_REMOVE(&kbl
, k
, entry
);
4871 keybinding_add(char *kb
, char *value
, struct key_binding
*orig
)
4873 struct key_binding
*k
;
4874 guint keyval
, mask
= 0;
4877 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s %s\n", kb
, value
, orig
->name
);
4881 if (strcmp(kb
, orig
->name
))
4884 /* find modifier keys */
4885 if (strstr(value
, "S-"))
4886 mask
|= GDK_SHIFT_MASK
;
4887 if (strstr(value
, "C-"))
4888 mask
|= GDK_CONTROL_MASK
;
4889 if (strstr(value
, "M1-"))
4890 mask
|= GDK_MOD1_MASK
;
4891 if (strstr(value
, "M2-"))
4892 mask
|= GDK_MOD2_MASK
;
4893 if (strstr(value
, "M3-"))
4894 mask
|= GDK_MOD3_MASK
;
4895 if (strstr(value
, "M4-"))
4896 mask
|= GDK_MOD4_MASK
;
4897 if (strstr(value
, "M5-"))
4898 mask
|= GDK_MOD5_MASK
;
4901 for (i
= strlen(value
) - 1; i
> 0; i
--)
4902 if (value
[i
] == '-')
4903 value
= &value
[i
+ 1];
4905 /* validate keyname */
4906 keyval
= gdk_keyval_from_name(value
);
4907 if (keyval
== GDK_VoidSymbol
) {
4908 warnx("invalid keybinding name %s", value
);
4911 /* must run this test too, gtk+ doesn't handle 10 for example */
4912 if (gdk_keyval_name(keyval
) == NULL
) {
4913 warnx("invalid keybinding name %s", value
);
4917 /* make sure it isn't a dupe */
4918 TAILQ_FOREACH(k
, &kbl
, entry
)
4919 if (k
->key
== keyval
&& k
->mask
== mask
) {
4920 warnx("duplicate keybinding for %s", value
);
4925 k
= g_malloc0(sizeof *k
);
4926 k
->name
= orig
->name
;
4928 k
->use_in_entry
= orig
->use_in_entry
;
4930 bcopy(&orig
->arg
, &k
->arg
, sizeof k
->arg
);
4932 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4937 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4938 k
->name
, gdk_keyval_name(keyval
));
4940 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4946 add_kb(struct settings
*s
, char *entry
)
4951 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4953 /* clearall is special */
4954 if (!strcmp(entry
, "clearall")) {
4955 keybinding_clearall();
4959 kb
= strstr(entry
, ",");
4965 /* make sure it is a valid keybinding */
4966 for (i
= 0; i
< LENGTH(keys
); i
++)
4967 if (keys
[i
].name
&& !strcmp(entry
, keys
[i
].name
)) {
4968 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s 0x%x %d 0x%x\n",
4971 keys
[i
].use_in_entry
,
4974 return (keybinding_add(entry
, value
, &keys
[i
]));
4983 int (*func
)(struct tab
*, struct karg
*);
4985 bool userarg
; /* allow free text arg */
4987 { "command", 0, command
, {.i
= ':'}, FALSE
},
4988 { "search", 0, command
, {.i
= '/'}, FALSE
},
4989 { "searchb", 0, command
, {.i
= '?'}, FALSE
},
4990 { "togglesrc", 0, toggle_src
, {0}, FALSE
},
4992 /* yanking and pasting */
4993 { "yankuri", 0, yank_uri
, {0}, FALSE
},
4994 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
4995 { "pasteuricur", 0, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
}, FALSE
},
4996 { "pasteurinew", 0, paste_uri
, {.i
= XT_PASTE_NEW_TAB
}, FALSE
},
4999 { "searchnext", 0, search
, {.i
= XT_SEARCH_NEXT
}, FALSE
},
5000 { "searchprevious", 0, search
, {.i
= XT_SEARCH_PREV
}, FALSE
},
5001 { "searchprev", 0, search
, {.i
= XT_SEARCH_PREV
}, FALSE
},
5004 { "focusaddress", 0, focus
, {.i
= XT_FOCUS_URI
}, FALSE
},
5005 { "focussearch", 0, focus
, {.i
= XT_FOCUS_SEARCH
}, FALSE
},
5008 { "hinting", 0, hint
, {.i
= 0}, FALSE
},
5010 /* custom stylesheet */
5011 { "userstyle", 0, userstyle
, {.i
= 0 }, FALSE
},
5014 { "goback", 0, navaction
, {.i
= XT_NAV_BACK
}, FALSE
},
5015 { "goforward", 0, navaction
, {.i
= XT_NAV_FORWARD
}, FALSE
},
5016 { "reload", 0, navaction
, {.i
= XT_NAV_RELOAD
}, FALSE
},
5017 { "reloadforce", 0, navaction
, {.i
= XT_NAV_RELOAD_CACHE
}, FALSE
},
5019 /* vertical movement */
5020 { "scrolldown", 0, move
, {.i
= XT_MOVE_DOWN
}, FALSE
},
5021 { "scrollup", 0, move
, {.i
= XT_MOVE_UP
}, FALSE
},
5022 { "scrollbottom", 0, move
, {.i
= XT_MOVE_BOTTOM
}, FALSE
},
5023 { "scrolltop", 0, move
, {.i
= XT_MOVE_TOP
}, FALSE
},
5024 { "1", 0, move
, {.i
= XT_MOVE_TOP
}, FALSE
},
5025 { "scrollhalfdown", 0, move
, {.i
= XT_MOVE_HALFDOWN
},FALSE
},
5026 { "scrollhalfup", 0, move
, {.i
= XT_MOVE_HALFUP
}, FALSE
},
5027 { "scrollpagedown", 0, move
, {.i
= XT_MOVE_PAGEDOWN
},FALSE
},
5028 { "scrollpageup", 0, move
, {.i
= XT_MOVE_PAGEUP
}, FALSE
},
5029 /* horizontal movement */
5030 { "scrollright", 0, move
, {.i
= XT_MOVE_RIGHT
}, FALSE
},
5031 { "scrollleft", 0, move
, {.i
= XT_MOVE_LEFT
}, FALSE
},
5032 { "scrollfarright", 0, move
, {.i
= XT_MOVE_FARRIGHT
},FALSE
},
5033 { "scrollfarleft", 0, move
, {.i
= XT_MOVE_FARLEFT
}, FALSE
},
5036 { "favorites", 0, xtp_page_fl
, {0}, FALSE
},
5037 { "fav", 0, xtp_page_fl
, {0}, FALSE
},
5038 { "favadd", 0, add_favorite
, {0}, FALSE
},
5040 { "quit", 0, quit
, {0}, FALSE
},
5041 { "q!", 0, quit
, {0}, FALSE
},
5042 { "qa", 0, quit
, {0}, FALSE
},
5043 { "qa!", 0, quit
, {0}, FALSE
},
5044 { "w", 0, save_tabs
, {0}, FALSE
},
5045 { "wq", 0, save_tabs_and_quit
, {0}, FALSE
},
5046 { "wq!", 0, save_tabs_and_quit
, {0}, FALSE
},
5047 { "help", 0, help
, {0}, FALSE
},
5048 { "about", 0, about
, {0}, FALSE
},
5049 { "stats", 0, stats
, {0}, FALSE
},
5050 { "version", 0, about
, {0}, FALSE
},
5051 { "cookiejar", 0, xtp_page_cl
, {0}, FALSE
},
5054 { "js", 0, js_cmd
, {0}, FALSE
},
5055 { "save", 1, js_cmd
, {0}, FALSE
},
5056 { "domain", 2, js_cmd
, {0}, FALSE
},
5057 { "fqdn", 2, js_cmd
, {0}, FALSE
},
5058 { "toggle", 1, js_cmd
, {0}, FALSE
},
5059 { "domain", 2, js_cmd
, {0}, FALSE
},
5060 { "fqdn", 2, js_cmd
, {0}, FALSE
},
5061 { "show", 1, js_cmd
, {0}, FALSE
},
5062 { "all", 2, js_cmd
, {0}, FALSE
},
5063 { "persistent", 2, js_cmd
, {0}, FALSE
},
5064 { "session", 2, js_cmd
, {0}, FALSE
},
5066 /* cookie command */
5067 { "cookie", 0, cookie_cmd
, {0}, FALSE
},
5068 { "show", 1, cookie_cmd
, {0}, FALSE
},
5069 { "all", 2, cookie_cmd
, {0}, FALSE
},
5070 { "persistent", 2, cookie_cmd
, {0}, FALSE
},
5071 { "session", 2, cookie_cmd
, {0}, FALSE
},
5072 { "save", 1, cookie_cmd
, {0}, FALSE
},
5073 { "fqdn", 2, cookie_cmd
, {0}, FALSE
},
5074 { "domain", 2, cookie_cmd
, {0}, FALSE
},
5075 { "toggle", 1, cookie_cmd
, {0}, FALSE
},
5076 { "domain", 2, cookie_cmd
, {0}, FALSE
},
5077 { "fqdn", 2, cookie_cmd
, {0}, FALSE
},
5080 { "cert", 0, cert_cmd
, {0}, FALSE
},
5081 { "show", 1, cert_cmd
, {0}, FALSE
},
5082 { "save", 1, cert_cmd
, {0}, FALSE
},
5084 { "ca", 0, ca_cmd
, {0}, FALSE
},
5085 { "downloadmgr", 0, xtp_page_dl
, {0}, FALSE
},
5086 { "dl", 0, xtp_page_dl
, {0}, FALSE
},
5087 { "h", 0, xtp_page_hl
, {0}, FALSE
},
5088 { "hist", 0, xtp_page_hl
, {0}, FALSE
},
5089 { "history", 0, xtp_page_hl
, {0}, FALSE
},
5090 { "home", 0, go_home
, {0}, FALSE
},
5091 { "restart", 0, restart
, {0}, FALSE
},
5092 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
}, FALSE
},
5093 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
}, FALSE
},
5094 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
}, FALSE
},
5095 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
}, FALSE
},
5096 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
}, FALSE
},
5097 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
}, FALSE
},
5098 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
}, FALSE
},
5099 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
}, FALSE
},
5101 { "print", 0, print_page
, {0}, FALSE
},
5104 { "o", 0, tabaction
, {.i
= XT_TAB_OPEN
}, TRUE
},
5105 { "op", 0, tabaction
, {.i
= XT_TAB_OPEN
}, TRUE
},
5106 { "open", 0, tabaction
, {.i
= XT_TAB_OPEN
}, TRUE
},
5107 { "tabnew", 0, tabaction
, {.i
= XT_TAB_NEW
}, TRUE
},
5108 { "tabedit", 0, tabaction
, {.i
= XT_TAB_NEW
}, TRUE
},
5109 { "tabe", 0, tabaction
, {.i
= XT_TAB_NEW
}, TRUE
},
5110 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
}, FALSE
},
5111 { "tabundoclose", 0, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
5112 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
}, FALSE
},
5113 { "tabshow", 0, tabaction
, {.i
= XT_TAB_SHOW
}, FALSE
},
5114 { "tabs", 0, tabaction
, {.i
= XT_TAB_SHOW
}, FALSE
},
5115 { "tabhide", 0, tabaction
, {.i
= XT_TAB_HIDE
}, FALSE
},
5116 { "tabh", 0, tabaction
, {.i
= XT_TAB_HIDE
}, FALSE
},
5117 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
}, FALSE
},
5118 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
}, FALSE
},
5119 /* XXX add count to these commands */
5120 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5121 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5122 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5123 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5124 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
}, FALSE
},
5125 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
}, FALSE
},
5126 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
}, FALSE
},
5127 { "tabprev", 0, movetab
, {.i
= XT_TAB_PREV
}, FALSE
},
5128 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
}, FALSE
},
5129 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
}, FALSE
},
5130 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
}, FALSE
},
5131 { "tabgoto1", 0, movetab
, {.i
= 1}, FALSE
},
5132 { "tabgoto2", 0, movetab
, {.i
= 2}, FALSE
},
5133 { "tabgoto3", 0, movetab
, {.i
= 3}, FALSE
},
5134 { "tabgoto4", 0, movetab
, {.i
= 4}, FALSE
},
5135 { "tabgoto5", 0, movetab
, {.i
= 5}, FALSE
},
5136 { "tabgoto6", 0, movetab
, {.i
= 6}, FALSE
},
5137 { "tabgoto7", 0, movetab
, {.i
= 7}, FALSE
},
5138 { "tabgoto8", 0, movetab
, {.i
= 8}, FALSE
},
5139 { "tabgoto9", 0, movetab
, {.i
= 9}, FALSE
},
5140 { "tabgoto10", 0, movetab
, {.i
= 10}, FALSE
},
5141 { "focusout", 0, resizetab
, {.i
= -1}, FALSE
},
5142 { "focusin", 0, resizetab
, {.i
= 1}, FALSE
},
5143 { "focusin", 0, resizetab
, {.i
= 1}, FALSE
},
5145 /* command aliases (handy when -S flag is used) */
5146 { "promptopen", 0, command
, {.i
= XT_CMD_OPEN
}, FALSE
},
5147 { "promptopencurrent", 0, command
, {.i
= XT_CMD_OPEN_CURRENT
}, FALSE
},
5148 { "prompttabnew", 0, command
, {.i
= XT_CMD_TABNEW
}, FALSE
},
5149 { "prompttabnewcurrent",0, command
, {.i
= XT_CMD_TABNEW_CURRENT
}, FALSE
},
5152 { "set", 0, set
, {0}, FALSE
},
5153 { "fullscreen", 0, fullscreen
, {0}, FALSE
},
5154 { "f", 0, fullscreen
, {0}, FALSE
},
5157 { "session", 0, session_cmd
, {0}, FALSE
},
5158 { "show", 1, session_cmd
, {0}, FALSE
},
5159 { "delete", 1, session_cmd
, {0}, TRUE
},
5160 { "open", 1, session_cmd
, {0}, TRUE
},
5161 { "save", 1, session_cmd
, {0}, TRUE
},
5168 } cmd_status
= {-1, 0};
5171 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5177 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5183 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5185 a
.i
= XT_NAV_FORWARD
;
5195 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5197 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5199 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5206 * cancel, remove, etc. downloads
5209 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5211 struct download find
, *d
;
5213 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5215 /* some commands require a valid download id */
5216 if (cmd
!= XT_XTP_DL_LIST
) {
5217 /* lookup download in question */
5219 d
= RB_FIND(download_list
, &downloads
, &find
);
5222 show_oops(t
, "%s: no such download", __func__
);
5227 /* decide what to do */
5229 case XT_XTP_DL_CANCEL
:
5230 webkit_download_cancel(d
->download
);
5232 case XT_XTP_DL_REMOVE
:
5233 webkit_download_cancel(d
->download
); /* just incase */
5234 g_object_unref(d
->download
);
5235 RB_REMOVE(download_list
, &downloads
, d
);
5237 case XT_XTP_DL_LIST
:
5241 show_oops(t
, "%s: unknown command", __func__
);
5244 xtp_page_dl(t
, NULL
);
5248 * Actions on history, only does one thing for now, but
5249 * we provide the function for future actions
5252 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5254 struct history
*h
, *next
;
5258 case XT_XTP_HL_REMOVE
:
5259 /* walk backwards, as listed in reverse */
5260 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5261 next
= RB_PREV(history_list
, &hl
, h
);
5263 RB_REMOVE(history_list
, &hl
, h
);
5264 g_free((gpointer
) h
->title
);
5265 g_free((gpointer
) h
->uri
);
5272 case XT_XTP_HL_LIST
:
5273 /* Nothing - just xtp_page_hl() below */
5276 show_oops(t
, "%s: unknown command", __func__
);
5280 xtp_page_hl(t
, NULL
);
5283 /* remove a favorite */
5285 remove_favorite(struct tab
*t
, int index
)
5287 char file
[PATH_MAX
], *title
, *uri
;
5288 char *new_favs
, *tmp
;
5293 /* open favorites */
5294 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5296 if ((f
= fopen(file
, "r")) == NULL
) {
5297 show_oops(t
, "%s: can't open favorites: %s",
5298 __func__
, strerror(errno
));
5302 /* build a string which will become the new favroites file */
5303 new_favs
= g_strdup_printf("%s", "");
5306 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5307 if (feof(f
) || ferror(f
))
5309 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5316 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5317 if (feof(f
) || ferror(f
)) {
5318 show_oops(t
, "%s: can't parse favorites %s",
5319 __func__
, strerror(errno
));
5324 /* as long as this isn't the one we are deleting add to file */
5327 new_favs
= g_strdup_printf("%s%s\n%s\n",
5328 new_favs
, title
, uri
);
5340 /* write back new favorites file */
5341 if ((f
= fopen(file
, "w")) == NULL
) {
5342 show_oops(t
, "%s: can't open favorites: %s",
5343 __func__
, strerror(errno
));
5347 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5360 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5363 case XT_XTP_FL_LIST
:
5364 /* nothing, just the below call to xtp_page_fl() */
5366 case XT_XTP_FL_REMOVE
:
5367 remove_favorite(t
, arg
);
5370 show_oops(t
, "%s: invalid favorites command", __func__
);
5374 xtp_page_fl(t
, NULL
);
5378 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5381 case XT_XTP_CL_LIST
:
5382 /* nothing, just xtp_page_cl() */
5384 case XT_XTP_CL_REMOVE
:
5388 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5392 xtp_page_cl(t
, NULL
);
5395 /* link an XTP class to it's session key and handler function */
5396 struct xtp_despatch
{
5399 void (*handle_func
)(struct tab
*, uint8_t, int);
5402 struct xtp_despatch xtp_despatches
[] = {
5403 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5404 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5405 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5406 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5407 { NULL
, NULL
, NULL
}
5411 * is the url xtp protocol? (xxxt://)
5412 * if so, parse and despatch correct bahvior
5415 parse_xtp_url(struct tab
*t
, const char *url
)
5417 char *dup
= NULL
, *p
, *last
;
5418 uint8_t n_tokens
= 0;
5419 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5420 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5424 * tokens array meaning:
5426 * tokens[1] = session key
5427 * tokens[2] = action
5428 * tokens[3] = optional argument
5431 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5433 /*xtp tab meaning is normal unless proven special */
5434 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5436 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5439 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5441 /* split out the url */
5442 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5443 (p
= strtok_r(NULL
, "/", &last
))) {
5445 tokens
[n_tokens
++] = p
;
5448 /* should be atleast three fields 'class/seskey/command/arg' */
5452 dsp
= xtp_despatches
;
5453 req_class
= atoi(tokens
[0]);
5454 while (dsp
->xtp_class
!= NULL
) {
5455 if (dsp
->xtp_class
== req_class
) {
5462 /* did we find one atall? */
5463 if (dsp_match
== NULL
) {
5464 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5468 /* check session key and call despatch function */
5469 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5470 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5483 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5485 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5487 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5490 show_oops_s("activate_uri_entry_cb invalid parameters");
5495 show_oops(t
, "activate_uri_entry_cb no uri");
5499 uri
+= strspn(uri
, "\t ");
5501 /* if xxxt:// treat specially */
5502 if (!parse_xtp_url(t
, uri
)) {
5503 load_uri(t
, (gchar
*)uri
);
5509 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5511 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5512 char *newuri
= NULL
;
5515 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5518 show_oops_s("activate_search_entry_cb invalid parameters");
5522 if (search_string
== NULL
) {
5523 show_oops(t
, "no search_string");
5527 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5528 newuri
= g_strdup_printf(search_string
, enc_search
);
5531 webkit_web_view_load_uri(t
->wv
, newuri
);
5539 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5541 struct domain
*d
= NULL
;
5544 if (uri
== NULL
|| t
== NULL
)
5547 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5552 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5553 es
? "enable" : "disable", uri
);
5555 g_object_set(G_OBJECT(t
->settings
),
5556 "enable-scripts", es
, (char *)NULL
);
5557 g_object_set(G_OBJECT(t
->settings
),
5558 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5559 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5561 button_set_stockid(t
->js_toggle
,
5562 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5566 show_ca_status(struct tab
*t
, const char *uri
)
5568 WebKitWebFrame
*frame
;
5569 WebKitWebDataSource
*source
;
5570 WebKitNetworkRequest
*request
;
5571 SoupMessage
*message
;
5573 gchar
*col_str
= XT_COLOR_WHITE
;
5576 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5577 ssl_strict_certs
, ssl_ca_file
, uri
);
5581 if (ssl_ca_file
== NULL
) {
5582 if (g_str_has_prefix(uri
, "http://"))
5584 if (g_str_has_prefix(uri
, "https://")) {
5585 col_str
= XT_COLOR_RED
;
5590 if (g_str_has_prefix(uri
, "http://") ||
5591 !g_str_has_prefix(uri
, "https://"))
5594 frame
= webkit_web_view_get_main_frame(t
->wv
);
5595 source
= webkit_web_frame_get_data_source(frame
);
5596 request
= webkit_web_data_source_get_request(source
);
5597 message
= webkit_network_request_get_message(request
);
5599 if (message
&& (soup_message_get_flags(message
) &
5600 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5601 col_str
= XT_COLOR_GREEN
;
5604 r
= load_compare_cert(t
, NULL
);
5606 col_str
= XT_COLOR_BLUE
;
5608 col_str
= XT_COLOR_YELLOW
;
5610 col_str
= XT_COLOR_RED
;
5615 gdk_color_parse(col_str
, &color
);
5616 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5618 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5619 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5621 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5622 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5625 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5627 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5628 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5635 free_favicon(struct tab
*t
)
5637 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5638 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5640 if (t
->icon_request
)
5641 g_object_unref(t
->icon_request
);
5643 g_object_unref(t
->icon_pixbuf
);
5644 if (t
->icon_dest_uri
)
5645 g_free(t
->icon_dest_uri
);
5647 t
->icon_pixbuf
= NULL
;
5648 t
->icon_request
= NULL
;
5649 t
->icon_dest_uri
= NULL
;
5653 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5655 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5656 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5658 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5659 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5661 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5662 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5666 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5668 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5669 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5671 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5672 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5674 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5675 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5679 is_valid_icon(char *file
)
5682 const char *mime_type
;
5686 gf
= g_file_new_for_path(file
);
5687 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5689 mime_type
= g_file_info_get_content_type(fi
);
5690 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5691 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5692 g_strcmp0(mime_type
, "image/png") == 0 ||
5693 g_strcmp0(mime_type
, "image/gif") == 0 ||
5694 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5702 set_favicon_from_file(struct tab
*t
, char *file
)
5705 GdkPixbuf
*pixbuf
, *scaled
;
5708 if (t
== NULL
|| file
== NULL
)
5710 if (t
->icon_pixbuf
) {
5711 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5715 if (g_str_has_prefix(file
, "file://"))
5716 file
+= strlen("file://");
5717 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5719 if (!stat(file
, &sb
)) {
5720 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5721 /* corrupt icon so trash it */
5722 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5725 /* no need to set icon to default here */
5730 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5731 if (pixbuf
== NULL
) {
5732 xt_icon_from_name(t
, "text-html");
5736 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5738 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5739 __func__
, t
->tab_id
, width
, height
);
5741 if (width
> 16 || height
> 16) {
5742 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5743 GDK_INTERP_BILINEAR
);
5744 g_object_unref(pixbuf
);
5748 if (scaled
== NULL
) {
5749 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5750 GDK_INTERP_BILINEAR
);
5754 t
->icon_pixbuf
= scaled
;
5755 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5759 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5762 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5763 struct tab
*tt
= NULL
, *t
= NULL
;
5766 * find the webview instead of passing in the tab as it could have been
5767 * deleted from underneath us.
5769 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5778 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5779 __func__
, t
->tab_id
, status
);
5782 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5784 t
->icon_download
= NULL
;
5787 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5790 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5793 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5795 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5796 __func__
, t
->tab_id
);
5797 t
->icon_download
= NULL
;
5800 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5803 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5804 __func__
, t
->icon_dest_uri
);
5805 set_favicon_from_file(t
, t
->icon_dest_uri
);
5806 /* these will be freed post callback */
5807 t
->icon_request
= NULL
;
5808 t
->icon_download
= NULL
;
5816 abort_favicon_download(struct tab
*t
)
5818 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5820 if (t
->icon_download
) {
5821 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5822 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5823 webkit_download_cancel(t
->icon_download
);
5824 t
->icon_download
= NULL
;
5828 xt_icon_from_name(t
, "text-html");
5832 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5834 gchar
*name_hash
, file
[PATH_MAX
];
5837 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5839 if (uri
== NULL
|| t
== NULL
)
5842 if (t
->icon_request
) {
5843 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5847 /* check to see if we got the icon in cache */
5848 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5849 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5852 if (!stat(file
, &sb
)) {
5853 if (sb
.st_size
> 0) {
5854 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5856 set_favicon_from_file(t
, file
);
5860 /* corrupt icon so trash it */
5861 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5866 /* create download for icon */
5867 t
->icon_request
= webkit_network_request_new(uri
);
5868 if (t
->icon_request
== NULL
) {
5869 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5874 t
->icon_download
= webkit_download_new(t
->icon_request
);
5875 if (t
->icon_download
== NULL
) {
5876 fprintf(stderr
, "%s: icon_download", __func__
);
5880 /* we have to free icon_dest_uri later */
5881 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5882 webkit_download_set_destination_uri(t
->icon_download
,
5885 if (webkit_download_get_status(t
->icon_download
) ==
5886 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5887 fprintf(stderr
, "%s: download failed to start", __func__
);
5888 g_object_unref(t
->icon_request
);
5889 g_free(t
->icon_dest_uri
);
5890 t
->icon_request
= NULL
;
5891 t
->icon_dest_uri
= NULL
;
5895 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5896 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5898 webkit_download_start(t
->icon_download
);
5902 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5904 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5905 struct history
*h
, find
;
5906 const gchar
*s_loading
;
5909 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
5910 webkit_web_view_get_load_status(wview
), get_uri(wview
) ? get_uri(wview
) : "NOTHING");
5913 show_oops_s("notify_load_status_cb invalid paramters");
5917 switch (webkit_web_view_get_load_status(wview
)) {
5918 case WEBKIT_LOAD_PROVISIONAL
:
5920 abort_favicon_download(t
);
5921 #if GTK_CHECK_VERSION(2, 20, 0)
5922 gtk_widget_show(t
->spinner
);
5923 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5925 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5927 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5933 case WEBKIT_LOAD_COMMITTED
:
5935 if ((uri
= get_uri(wview
)) != NULL
) {
5936 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5937 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5943 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5946 /* check if js white listing is enabled */
5947 if (enable_js_whitelist
) {
5948 uri
= get_uri(wview
);
5949 check_and_set_js(uri
, t
);
5955 show_ca_status(t
, uri
);
5957 /* we know enough to autosave the session */
5958 if (session_autosave
) {
5964 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5968 case WEBKIT_LOAD_FINISHED
:
5970 uri
= get_uri(wview
);
5974 if (!strncmp(uri
, "http://", strlen("http://")) ||
5975 !strncmp(uri
, "https://", strlen("https://")) ||
5976 !strncmp(uri
, "file://", strlen("file://"))) {
5978 h
= RB_FIND(history_list
, &hl
, &find
);
5980 title
= webkit_web_view_get_title(wview
);
5981 set
= title
? title
: uri
;
5982 h
= g_malloc(sizeof *h
);
5983 h
->uri
= g_strdup(uri
);
5984 h
->title
= g_strdup(set
);
5985 RB_INSERT(history_list
, &hl
, h
);
5986 completion_add_uri(h
->uri
);
5987 update_history_tabs(NULL
);
5991 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5992 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5993 case WEBKIT_LOAD_FAILED
:
5996 #if GTK_CHECK_VERSION(2, 20, 0)
5997 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5998 gtk_widget_hide(t
->spinner
);
6000 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
6001 if (s_loading
&& !strcmp(s_loading
, "Loading"))
6002 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6004 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6009 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6011 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6012 webkit_web_view_can_go_back(wview
));
6014 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6015 webkit_web_view_can_go_forward(wview
));
6017 /* take focus if we are visible */
6022 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6024 const gchar
*set
= NULL
, *title
= NULL
;
6026 title
= webkit_web_view_get_title(wview
);
6027 set
= title
? title
: get_uri(wview
);
6028 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
6029 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
6033 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6035 run_script(t
, JS_HINTING
);
6039 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6041 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6042 progress
== 100 ? 0 : (double)progress
/ 100);
6043 if (show_url
== 0) {
6044 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
6045 progress
== 100 ? 0 : (double)progress
/ 100);
6050 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6051 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6052 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6055 WebKitWebNavigationReason reason
;
6056 struct domain
*d
= NULL
;
6059 show_oops_s("webview_npd_cb invalid parameters");
6063 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6065 webkit_network_request_get_uri(request
));
6067 uri
= (char *)webkit_network_request_get_uri(request
);
6069 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
6071 create_new_tab(uri
, NULL
, ctrl_click_focus
);
6072 webkit_web_policy_decision_ignore(pd
);
6073 return (TRUE
); /* we made the decission */
6077 * This is a little hairy but it comes down to this:
6078 * when we run in whitelist mode we have to assist the browser in
6079 * opening the URL that it would have opened in a new tab.
6081 reason
= webkit_web_navigation_action_get_reason(na
);
6082 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6083 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6084 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6087 webkit_web_policy_decision_use(pd
);
6088 return (TRUE
); /* we made the decission */
6095 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6098 struct domain
*d
= NULL
;
6100 WebKitWebView
*webview
= NULL
;
6102 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6103 webkit_web_view_get_uri(wv
));
6105 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6106 uri
= webkit_web_view_get_uri(wv
);
6107 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6110 tt
= create_new_tab(NULL
, NULL
, 1);
6112 } else if (enable_scripts
== 1) {
6113 tt
= create_new_tab(NULL
, NULL
, 1);
6121 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6124 struct domain
*d
= NULL
;
6126 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6128 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6129 uri
= webkit_web_view_get_uri(wv
);
6130 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6134 } else if (enable_scripts
== 1)
6141 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6143 /* we can not eat the event without throwing gtk off so defer it */
6145 /* catch middle click */
6146 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6151 /* catch ctrl click */
6152 if (e
->type
== GDK_BUTTON_RELEASE
&&
6153 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6158 return (XT_CB_PASSTHROUGH
);
6162 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6164 struct mime_type
*m
;
6166 m
= find_mime_type(mime_type
);
6174 show_oops(t
, "can't fork mime handler");
6183 execlp(m
->mt_action
, m
->mt_action
,
6184 webkit_network_request_get_uri(request
), (void *)NULL
);
6193 get_mime_type(char *file
)
6195 const char *mime_type
;
6199 if (g_str_has_prefix(file
, "file://"))
6200 file
+= strlen("file://");
6202 gf
= g_file_new_for_path(file
);
6203 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6205 mime_type
= g_file_info_get_content_type(fi
);
6213 run_download_mimehandler(char *mime_type
, char *file
)
6215 struct mime_type
*m
;
6217 m
= find_mime_type(mime_type
);
6223 show_oops_s("can't fork download mime handler");
6232 if (g_str_has_prefix(file
, "file://"))
6233 file
+= strlen("file://");
6234 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6243 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6246 WebKitDownloadStatus status
;
6247 const gchar
*file
= NULL
, *mime
= NULL
;
6249 if (download
== NULL
)
6251 status
= webkit_download_get_status(download
);
6252 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6255 file
= webkit_download_get_destination_uri(download
);
6258 mime
= get_mime_type((char *)file
);
6262 run_download_mimehandler((char *)mime
, (char *)file
);
6266 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6267 WebKitNetworkRequest
*request
, char *mime_type
,
6268 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6271 show_oops_s("webview_mimetype_cb invalid parameters");
6275 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6276 t
->tab_id
, mime_type
);
6278 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6279 webkit_web_policy_decision_ignore(decision
);
6284 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6285 webkit_web_policy_decision_download(decision
);
6293 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6296 const gchar
*filename
;
6298 struct download
*download_entry
;
6301 if (wk_download
== NULL
|| t
== NULL
) {
6302 show_oops_s("%s invalid parameters", __func__
);
6306 filename
= webkit_download_get_suggested_filename(wk_download
);
6307 if (filename
== NULL
)
6308 return (FALSE
); /* abort download */
6310 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
6312 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6313 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6315 webkit_download_set_destination_uri(wk_download
, uri
);
6317 if (webkit_download_get_status(wk_download
) ==
6318 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6319 show_oops(t
, "%s: download failed to start", __func__
);
6321 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6323 /* connect "download first" mime handler */
6324 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6325 G_CALLBACK(download_status_changed_cb
), NULL
);
6327 download_entry
= g_malloc(sizeof(struct download
));
6328 download_entry
->download
= wk_download
;
6329 download_entry
->tab
= t
;
6330 download_entry
->id
= next_download_id
++;
6331 RB_INSERT(download_list
, &downloads
, download_entry
);
6332 /* get from history */
6333 g_object_ref(wk_download
);
6334 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6335 show_oops(t
, "Download of '%s' started...",
6336 basename(webkit_download_get_destination_uri(wk_download
)));
6342 /* sync other download manager tabs */
6343 update_download_tabs(NULL
);
6346 * NOTE: never redirect/render the current tab before this
6347 * function returns. This will cause the download to never start.
6349 return (ret
); /* start download */
6353 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6355 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6358 show_oops_s("webview_hover_cb");
6363 set_status(t
, uri
, XT_STATUS_LINK
);
6366 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6371 _handle_keypress(struct key_binding
*k
, GdkEventKey
*e
)
6375 for (i
= 0; i
< LENGTH(cmds
); i
++) {
6376 if (!strcmp(k
->name
, cmds
[i
].cmd
)) {
6378 if (k
->arg
.s
&& strlen(k
->arg
.s
) > 0)
6379 cmds
[i
].arg
.s
= g_strdup_printf("%s %s",
6389 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6392 struct key_binding
*k
;
6394 TAILQ_FOREACH(k
, &kbl
, entry
)
6395 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6397 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6398 i
= _handle_keypress(k
, e
);
6399 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6400 i
= _handle_keypress(k
, e
);
6404 if (0 <= i
&& i
< LENGTH(cmds
)) {
6405 cmds
[i
].func(t
, &cmds
[i
].arg
);
6407 g_free(cmds
[i
].arg
.s
);
6408 return (XT_CB_HANDLED
);
6410 return (XT_CB_PASSTHROUGH
);
6414 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6416 char s
[2], buf
[128];
6417 const char *errstr
= NULL
;
6420 /* don't use w directly; use t->whatever instead */
6423 show_oops_s("wv_keypress_after_cb");
6424 return (XT_CB_PASSTHROUGH
);
6427 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6428 e
->keyval
, e
->state
, t
);
6432 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6434 return (XT_CB_HANDLED
);
6438 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6439 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6441 /* we have a string */
6443 /* we have a number */
6444 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6452 /* XXX unfuck this */
6453 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6454 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6455 /* last input was numerical */
6457 l
= strlen(t
->hint_num
);
6464 t
->hint_num
[l
] = '\0';
6468 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6469 /* last input was alphanumerical */
6471 l
= strlen(t
->hint_buf
);
6478 t
->hint_buf
[l
] = '\0';
6488 /* numerical input */
6489 if (CLEAN(e
->state
) == 0 &&
6490 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6491 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6492 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6493 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6496 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6498 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6501 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6503 t
->hint_mode
= XT_HINT_NUMERICAL
;
6507 /* empty the counter buffer */
6508 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6509 return (XT_CB_HANDLED
);
6512 /* alphanumerical input */
6514 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6515 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6516 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6517 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6518 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6519 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6520 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6523 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6526 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6528 t
->hint_mode
= XT_HINT_ALPHANUM
;
6531 /* empty the counter buffer */
6532 bzero(t
->hint_num
, sizeof t
->hint_num
);
6533 return (XT_CB_HANDLED
);
6536 return (XT_CB_HANDLED
);
6539 return (handle_keypress(t
, e
, 0));
6543 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6547 return (XT_CB_PASSTHROUGH
);
6551 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6553 const gchar
*c
= gtk_entry_get_text(w
);
6557 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6558 e
->keyval
, e
->state
, t
);
6561 show_oops_s("cmd_keyrelease_cb invalid parameters");
6562 return (XT_CB_PASSTHROUGH
);
6565 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6566 e
->keyval
, e
->state
, t
);
6570 if (strlen(c
) == 1) {
6571 webkit_web_view_unmark_text_matches(t
->wv
);
6577 else if (c
[0] == '?')
6583 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6585 /* not found, mark red */
6586 gdk_color_parse(XT_COLOR_RED
, &color
);
6587 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6588 /* unmark and remove selection */
6589 webkit_web_view_unmark_text_matches(t
->wv
);
6590 /* my kingdom for a way to unselect text in webview */
6592 /* found, highlight all */
6593 webkit_web_view_unmark_text_matches(t
->wv
);
6594 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6595 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6596 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6597 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6600 return (XT_CB_PASSTHROUGH
);
6604 match_uri(const gchar
*uri
, const gchar
*key
) {
6607 gboolean match
= FALSE
;
6611 if (!strncmp(key
, uri
, len
))
6614 voffset
= strstr(uri
, "/") + 2;
6615 if (!strncmp(key
, voffset
, len
))
6617 else if (g_str_has_prefix(voffset
, "www.")) {
6618 voffset
= voffset
+ strlen("www.");
6619 if (!strncmp(key
, voffset
, len
))
6628 cmd_getlist(int id
, char *key
)
6633 if (id
>= 0 && (cmds
[id
].arg
.i
== XT_TAB_OPEN
|| cmds
[id
].arg
.i
== XT_TAB_NEW
)) {
6634 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6635 if (match_uri(h
->uri
, key
)) {
6636 cmd_status
.list
[c
] = (char *)h
->uri
;
6645 dep
= (id
== -1) ? 0 : cmds
[id
].params
+ 1;
6647 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6648 if(cmds
[i
].params
< dep
)
6650 if (cmds
[i
].params
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6651 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6659 cmd_getnext(int dir
)
6661 cmd_status
.index
+= dir
;
6663 if (cmd_status
.index
<0)
6664 cmd_status
.index
= cmd_status
.len
-1;
6665 else if (cmd_status
.index
>= cmd_status
.len
)
6666 cmd_status
.index
= 0;
6668 return cmd_status
.list
[cmd_status
.index
];
6672 cmd_tokenize(char *s
, char *tokens
[])
6676 size_t len
= strlen(s
);
6677 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6679 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6689 cmd_complete(struct tab
*t
, char *str
, int dir
)
6691 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6692 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
6693 char *tok
, *match
, *s
= strdup(str
);
6695 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6697 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", str
);
6699 levels
= cmd_tokenize(s
, tokens
);
6701 for (i
= 0; i
< levels
- 1; i
++) {
6703 for (j
= c
; j
< LENGTH(cmds
); j
++)
6704 if (cmds
[j
].params
== dep
&& !strcmp(tok
, cmds
[j
].cmd
)) {
6705 strlcat(res
, tok
, sizeof res
);
6706 strlcat(res
, " ", sizeof res
);
6715 if (cmd_status
.index
== -1 )
6716 cmd_getlist(parent
, tokens
[i
]);
6718 if (cmd_status
.len
> 0) {
6719 match
= cmd_getnext(dir
);
6720 strlcat(res
, match
, sizeof res
);
6721 gtk_entry_set_text(w
, res
);
6722 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6727 cmd_execute(struct tab
*t
, char *str
)
6729 struct cmd
*cmd
= NULL
;
6730 char *tok
, *last
, *s
= g_strdup(str
);
6731 int i
, c
= 0, dep
= 0;
6733 for (tok
= strtok_r(s
, " ", &last
); tok
;
6734 tok
= strtok_r(NULL
, " ", &last
)) {
6735 for (i
= c
; i
< LENGTH(cmds
); i
++) {
6736 if(cmds
[i
].params
< dep
) {
6737 show_oops(t
, "Invalid command: %s", str
);
6740 if (cmds
[i
].params
== dep
&& !strcmp(tok
, cmds
[i
].cmd
)) {
6750 if (i
== LENGTH(cmds
)) {
6751 show_oops(t
, "Invalid command: %s", str
);
6757 cmd
->arg
.s
= g_strdup(str
);
6758 cmd
->func(t
, &cmd
->arg
);
6764 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6767 show_oops_s("entry_key_cb invalid parameters");
6768 return (XT_CB_PASSTHROUGH
);
6771 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6772 e
->keyval
, e
->state
, t
);
6776 if (e
->keyval
== GDK_Escape
) {
6777 /* don't use focus_webview(t) because we want to type :cmds */
6778 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6781 return (handle_keypress(t
, e
, 1));
6785 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6787 int rv
= XT_CB_HANDLED
;
6788 const gchar
*c
= gtk_entry_get_text(w
);
6791 show_oops_s("cmd_keypress_cb parameters");
6792 return (XT_CB_PASSTHROUGH
);
6795 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6796 e
->keyval
, e
->state
, t
);
6800 e
->keyval
= GDK_Escape
;
6801 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6802 e
->keyval
= GDK_Escape
;
6804 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
6805 cmd_status
.index
= -1;
6807 switch (e
->keyval
) {
6810 cmd_complete(t
, (char *)&c
[1], 1);
6812 case GDK_ISO_Left_Tab
:
6814 cmd_complete(t
, (char *)&c
[1], -1);
6818 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6826 if (c
[0] == '/' || c
[0] == '?')
6827 webkit_web_view_unmark_text_matches(t
->wv
);
6831 rv
= XT_CB_PASSTHROUGH
;
6837 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6840 show_oops_s("cmd_focusout_cb invalid parameters");
6841 return (XT_CB_PASSTHROUGH
);
6843 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6848 if (show_url
== 0 || t
->focus_wv
)
6851 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6853 return (XT_CB_PASSTHROUGH
);
6857 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6860 const gchar
*c
= gtk_entry_get_text(entry
);
6863 show_oops_s("cmd_activate_cb invalid parameters");
6867 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6872 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6878 if (c
[0] == '/' || c
[0] == '?') {
6879 if (t
->search_text
) {
6880 g_free(t
->search_text
);
6881 t
->search_text
= NULL
;
6884 t
->search_text
= g_strdup(s
);
6886 g_free(global_search
);
6887 global_search
= g_strdup(s
);
6888 t
->search_forward
= c
[0] == '/';
6900 backward_cb(GtkWidget
*w
, struct tab
*t
)
6905 show_oops_s("backward_cb invalid parameters");
6909 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6916 forward_cb(GtkWidget
*w
, struct tab
*t
)
6921 show_oops_s("forward_cb invalid parameters");
6925 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6927 a
.i
= XT_NAV_FORWARD
;
6932 home_cb(GtkWidget
*w
, struct tab
*t
)
6935 show_oops_s("home_cb invalid parameters");
6939 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
6945 stop_cb(GtkWidget
*w
, struct tab
*t
)
6947 WebKitWebFrame
*frame
;
6950 show_oops_s("stop_cb invalid parameters");
6954 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6956 frame
= webkit_web_view_get_main_frame(t
->wv
);
6957 if (frame
== NULL
) {
6958 show_oops(t
, "stop_cb: no frame");
6962 webkit_web_frame_stop_loading(frame
);
6963 abort_favicon_download(t
);
6967 setup_webkit(struct tab
*t
)
6969 if (is_g_object_setting(G_OBJECT(t
->settings
),
6970 "enable-dns-prefetching"))
6971 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
6972 FALSE
, (char *)NULL
);
6973 g_object_set(G_OBJECT(t
->settings
),
6974 "user-agent", t
->user_agent
, (char *)NULL
);
6975 g_object_set(G_OBJECT(t
->settings
),
6976 "enable-scripts", enable_scripts
, (char *)NULL
);
6977 g_object_set(G_OBJECT(t
->settings
),
6978 "enable-plugins", enable_plugins
, (char *)NULL
);
6979 g_object_set(G_OBJECT(t
->settings
),
6980 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
6981 g_object_set(G_OBJECT(t
->wv
),
6982 "full-content-zoom", TRUE
, (char *)NULL
);
6983 adjustfont_webkit(t
, XT_FONT_SET
);
6985 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6989 create_browser(struct tab
*t
)
6995 show_oops_s("create_browser invalid parameters");
6999 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7000 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7001 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7002 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7004 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7005 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7006 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7008 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7009 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7012 t
->settings
= webkit_web_settings_new();
7014 if (user_agent
== NULL
) {
7015 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7017 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7020 t
->user_agent
= g_strdup(user_agent
);
7022 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7034 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7035 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7036 gtk_widget_set_name(w
, "xxxterm");
7037 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7038 g_signal_connect(G_OBJECT(w
), "delete_event",
7039 G_CALLBACK (gtk_main_quit
), NULL
);
7045 create_kiosk_toolbar(struct tab
*t
)
7047 GtkWidget
*toolbar
= NULL
, *b
;
7049 b
= gtk_hbox_new(FALSE
, 0);
7051 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7053 /* backward button */
7054 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
7055 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7056 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7057 G_CALLBACK(backward_cb
), t
);
7058 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7060 /* forward button */
7061 t
->forward
= create_button("GoForward", GTK_STOCK_GO_FORWARD
, 0);
7062 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7063 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7064 G_CALLBACK(forward_cb
), t
);
7065 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7068 t
->gohome
= create_button("GoForward", GTK_STOCK_HOME
, 0);
7069 gtk_widget_set_sensitive(t
->gohome
, true);
7070 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7071 G_CALLBACK(home_cb
), t
);
7072 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7074 /* create widgets but don't use them */
7075 t
->uri_entry
= gtk_entry_new();
7076 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7077 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7078 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7084 create_toolbar(struct tab
*t
)
7086 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7088 b
= gtk_hbox_new(FALSE
, 0);
7090 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7093 /* backward button */
7094 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
7095 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7096 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7097 G_CALLBACK(backward_cb
), t
);
7098 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7100 /* forward button */
7101 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
7102 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7103 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7104 G_CALLBACK(forward_cb
), t
);
7105 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7109 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7110 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7111 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7112 G_CALLBACK(stop_cb
), t
);
7113 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7117 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7118 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7119 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7120 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7121 G_CALLBACK(js_toggle_cb
), t
);
7122 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7125 t
->uri_entry
= gtk_entry_new();
7126 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7127 G_CALLBACK(activate_uri_entry_cb
), t
);
7128 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7129 G_CALLBACK(entry_key_cb
), t
);
7131 eb1
= gtk_hbox_new(FALSE
, 0);
7132 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7133 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7134 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7137 if (fancy_bar
&& search_string
) {
7139 t
->search_entry
= gtk_entry_new();
7140 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7141 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7142 G_CALLBACK(activate_search_entry_cb
), t
);
7143 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7144 G_CALLBACK(entry_key_cb
), t
);
7145 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7146 eb2
= gtk_hbox_new(FALSE
, 0);
7147 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7148 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7150 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7160 TAILQ_FOREACH(t
, &tabs
, entry
)
7161 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7165 undo_close_tab_save(struct tab
*t
)
7169 struct undo
*u1
, *u2
;
7171 WebKitWebHistoryItem
*item
;
7173 if ((uri
= get_uri(t
->wv
)) == NULL
)
7176 u1
= g_malloc0(sizeof(struct undo
));
7177 u1
->uri
= g_strdup(uri
);
7179 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7181 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7182 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7185 /* forward history */
7186 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7190 u1
->history
= g_list_prepend(u1
->history
,
7191 webkit_web_history_item_copy(item
));
7192 items
= g_list_next(items
);
7197 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7198 u1
->history
= g_list_prepend(u1
->history
,
7199 webkit_web_history_item_copy(item
));
7203 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7207 u1
->history
= g_list_prepend(u1
->history
,
7208 webkit_web_history_item_copy(item
));
7209 items
= g_list_next(items
);
7212 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7214 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7215 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7216 TAILQ_REMOVE(&undos
, u2
, entry
);
7218 g_list_free(u2
->history
);
7227 delete_tab(struct tab
*t
)
7231 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7236 TAILQ_REMOVE(&tabs
, t
, entry
);
7238 /* halt all webkit activity */
7239 abort_favicon_download(t
);
7240 webkit_web_view_stop_loading(t
->wv
);
7241 undo_close_tab_save(t
);
7243 if (browser_mode
== XT_BM_KIOSK
) {
7244 gtk_widget_destroy(t
->uri_entry
);
7245 gtk_widget_destroy(t
->stop
);
7246 gtk_widget_destroy(t
->js_toggle
);
7250 gtk_widget_destroy(t
->vbox
);
7251 g_free(t
->user_agent
);
7252 g_free(t
->stylesheet
);
7256 if (TAILQ_EMPTY(&tabs
)) {
7257 if (browser_mode
== XT_BM_KIOSK
)
7258 create_new_tab(home
, NULL
, 1);
7260 create_new_tab(NULL
, NULL
, 1);
7263 /* recreate session */
7264 if (session_autosave
) {
7271 adjustfont_webkit(struct tab
*t
, int adjust
)
7276 show_oops_s("adjustfont_webkit invalid parameters");
7280 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7281 if (adjust
== XT_FONT_SET
) {
7282 t
->font_size
= default_font_size
;
7283 zoom
= default_zoom_level
;
7284 t
->font_size
+= adjust
;
7285 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7286 t
->font_size
, (char *)NULL
);
7287 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7288 &t
->font_size
, (char *)NULL
);
7290 t
->font_size
+= adjust
;
7291 zoom
+= adjust
/25.0;
7296 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7297 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7301 append_tab(struct tab
*t
)
7306 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7307 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7311 create_new_tab(char *title
, struct undo
*u
, int focus
)
7314 int load
= 1, id
, notfound
;
7316 WebKitWebHistoryItem
*item
;
7320 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7322 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7323 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7327 t
= g_malloc0(sizeof *t
);
7329 if (title
== NULL
) {
7330 title
= "(untitled)";
7334 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7336 /* label + button for tab */
7337 b
= gtk_hbox_new(FALSE
, 0);
7340 #if GTK_CHECK_VERSION(2, 20, 0)
7341 t
->spinner
= gtk_spinner_new ();
7343 t
->label
= gtk_label_new(title
);
7344 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7345 gtk_widget_set_size_request(t
->label
, 100, 0);
7346 gtk_widget_set_size_request(b
, 130, 0);
7348 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7349 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7350 #if GTK_CHECK_VERSION(2, 20, 0)
7351 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7355 if (browser_mode
== XT_BM_KIOSK
)
7356 t
->toolbar
= create_kiosk_toolbar(t
);
7358 t
->toolbar
= create_toolbar(t
);
7360 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7363 t
->browser_win
= create_browser(t
);
7364 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7366 /* oops message for user feedback */
7367 t
->oops
= gtk_entry_new();
7368 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7369 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7370 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7371 gdk_color_parse(XT_COLOR_RED
, &color
);
7372 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7373 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7376 t
->cmd
= gtk_entry_new();
7377 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7378 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7379 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7382 t
->statusbar
= gtk_entry_new();
7383 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
7384 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
7385 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
7386 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7387 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7388 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7389 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7390 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
7392 /* xtp meaning is normal by default */
7393 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7395 /* set empty favicon */
7396 xt_icon_from_name(t
, "text-html");
7398 /* and show it all */
7399 gtk_widget_show_all(b
);
7400 gtk_widget_show_all(t
->vbox
);
7402 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7406 id
= gtk_notebook_get_current_page(notebook
);
7407 TAILQ_FOREACH(tt
, &tabs
, entry
) {
7408 if (tt
->tab_id
== id
) {
7410 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
7411 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
7421 #if GTK_CHECK_VERSION(2, 20, 0)
7422 /* turn spinner off if we are a new tab without uri */
7424 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7425 gtk_widget_hide(t
->spinner
);
7428 /* make notebook tabs reorderable */
7429 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7431 g_object_connect(G_OBJECT(t
->cmd
),
7432 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7433 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7434 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7435 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7438 /* reuse wv_button_cb to hide oops */
7439 g_object_connect(G_OBJECT(t
->oops
),
7440 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7443 g_object_connect(G_OBJECT(t
->wv
),
7444 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7445 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7446 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7447 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7448 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7449 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7450 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7451 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7452 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7453 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7454 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7455 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7456 #if WEBKIT_CHECK_VERSION(1, 1, 18)
7457 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7459 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7461 g_signal_connect(t
->wv
,
7462 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7463 g_signal_connect(t
->wv
,
7464 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7466 /* hijack the unused keys as if we were the browser */
7467 g_object_connect(G_OBJECT(t
->toolbar
),
7468 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7471 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7472 G_CALLBACK(tab_close_cb
), t
);
7477 url_set_visibility();
7478 statusbar_set_visibility();
7481 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7482 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7487 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7491 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7496 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7497 /* restore the tab's history */
7498 if (u
&& u
->history
) {
7502 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7503 items
= g_list_next(items
);
7506 item
= g_list_nth_data(u
->history
, u
->back
);
7508 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7511 g_list_free(u
->history
);
7513 webkit_web_back_forward_list_clear(t
->bfl
);
7519 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
7525 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7527 TAILQ_FOREACH(t
, &tabs
, entry
) {
7528 if (t
->tab_id
== pn
) {
7529 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7532 uri
= webkit_web_view_get_title(t
->wv
);
7535 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7547 menuitem_response(struct tab
*t
)
7549 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7553 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7555 GtkWidget
*menu
, *menu_items
;
7556 GdkEventButton
*bevent
;
7560 if (event
->type
== GDK_BUTTON_PRESS
) {
7561 bevent
= (GdkEventButton
*) event
;
7562 menu
= gtk_menu_new();
7564 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7565 if ((uri
= get_uri(ti
->wv
)) == NULL
)
7566 /* XXX make sure there is something to print */
7567 /* XXX add gui pages in here to look purdy */
7569 menu_items
= gtk_menu_item_new_with_label(uri
);
7570 gtk_menu_append(GTK_MENU (menu
), menu_items
);
7571 gtk_widget_show(menu_items
);
7573 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
7574 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
7578 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7579 bevent
->button
, bevent
->time
);
7581 /* unref object so it'll free itself when popped down */
7582 g_object_ref_sink(menu
);
7583 g_object_unref(menu
);
7585 return (TRUE
/* eat event */);
7588 return (FALSE
/* propagate */);
7592 icon_size_map(int icon_size
)
7594 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7595 icon_size
> GTK_ICON_SIZE_DIALOG
)
7596 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7602 create_button(char *name
, char *stockid
, int size
)
7604 GtkWidget
*button
, *image
;
7607 rcstring
= g_strdup_printf(
7608 "style \"%s-style\"\n"
7610 " GtkWidget::focus-padding = 0\n"
7611 " GtkWidget::focus-line-width = 0\n"
7615 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
7616 gtk_rc_parse_string(rcstring
);
7618 button
= gtk_button_new();
7619 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7620 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
7622 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7623 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7624 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7625 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7626 gtk_widget_set_name(button
, name
);
7627 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7628 gtk_widget_set_tooltip_text(button
, name
);
7634 button_set_stockid(GtkWidget
*button
, char *stockid
)
7638 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7639 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7640 gtk_button_set_image(GTK_BUTTON(button
), image
);
7649 char file
[PATH_MAX
];
7652 vbox
= gtk_vbox_new(FALSE
, 0);
7653 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7654 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7655 gtk_notebook_set_tab_hborder(notebook
, 0);
7656 gtk_notebook_set_tab_vborder(notebook
, 0);
7657 gtk_notebook_set_scrollable(notebook
, TRUE
);
7658 notebook_tab_set_visibility(notebook
);
7659 gtk_notebook_set_show_border(notebook
, FALSE
);
7660 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7662 abtn
= gtk_button_new();
7663 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7664 gtk_widget_set_size_request(arrow
, -1, -1);
7665 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7666 gtk_widget_set_size_request(abtn
, -1, 20);
7667 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7669 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7670 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7671 gtk_widget_set_size_request(vbox
, -1, -1);
7673 g_object_connect(G_OBJECT(notebook
),
7674 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7676 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7677 G_CALLBACK(arrow_cb
), NULL
);
7679 main_window
= create_window();
7680 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7681 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7684 for (i
= 0; i
< LENGTH(icons
); i
++) {
7685 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7686 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7687 l
= g_list_append(l
, pb
);
7689 gtk_window_set_default_icon_list(l
);
7691 gtk_widget_show_all(abtn
);
7692 gtk_widget_show_all(main_window
);
7696 set_hook(void **hook
, char *name
)
7699 errx(1, "set_hook");
7701 if (*hook
== NULL
) {
7702 *hook
= dlsym(RTLD_NEXT
, name
);
7704 errx(1, "can't hook %s", name
);
7708 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7710 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7712 g_return_val_if_fail(cookie1
, FALSE
);
7713 g_return_val_if_fail(cookie2
, FALSE
);
7715 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7716 !strcmp (cookie1
->value
, cookie2
->value
) &&
7717 !strcmp (cookie1
->path
, cookie2
->path
) &&
7718 !strcmp (cookie1
->domain
, cookie2
->domain
));
7722 transfer_cookies(void)
7725 SoupCookie
*sc
, *pc
;
7727 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7729 for (;cf
; cf
= cf
->next
) {
7731 sc
= soup_cookie_copy(pc
);
7732 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7735 soup_cookies_free(cf
);
7739 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7744 print_cookie("soup_cookie_jar_delete_cookie", c
);
7746 if (cookies_enabled
== 0)
7749 if (jar
== NULL
|| c
== NULL
)
7752 /* find and remove from persistent jar */
7753 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7755 for (;cf
; cf
= cf
->next
) {
7757 if (soup_cookie_equal(ci
, c
)) {
7758 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7763 soup_cookies_free(cf
);
7765 /* delete from session jar */
7766 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7770 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7772 struct domain
*d
= NULL
;
7776 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7777 jar
, p_cookiejar
, s_cookiejar
);
7779 if (cookies_enabled
== 0)
7782 /* see if we are up and running */
7783 if (p_cookiejar
== NULL
) {
7784 _soup_cookie_jar_add_cookie(jar
, cookie
);
7787 /* disallow p_cookiejar adds, shouldn't happen */
7788 if (jar
== p_cookiejar
)
7792 if (jar
== NULL
|| cookie
== NULL
)
7795 if (enable_cookie_whitelist
&&
7796 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7798 DNPRINTF(XT_D_COOKIE
,
7799 "soup_cookie_jar_add_cookie: reject %s\n",
7801 if (save_rejected_cookies
) {
7802 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7803 show_oops_s("can't open reject cookie file");
7806 fseek(r_cookie_f
, 0, SEEK_END
);
7807 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7808 cookie
->http_only
? "#HttpOnly_" : "",
7810 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7812 cookie
->secure
? "TRUE" : "FALSE",
7814 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7821 if (!allow_volatile_cookies
)
7825 if (cookie
->expires
== NULL
&& session_timeout
) {
7826 soup_cookie_set_expires(cookie
,
7827 soup_date_new_from_now(session_timeout
));
7828 print_cookie("modified add cookie", cookie
);
7831 /* see if we are white listed for persistence */
7832 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7833 /* add to persistent jar */
7834 c
= soup_cookie_copy(cookie
);
7835 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7836 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7839 /* add to session jar */
7840 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7841 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7847 char file
[PATH_MAX
];
7849 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7850 "soup_cookie_jar_add_cookie");
7851 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7852 "soup_cookie_jar_delete_cookie");
7854 if (cookies_enabled
== 0)
7858 * the following code is intricate due to overriding several libsoup
7860 * do not alter order of these operations.
7863 /* rejected cookies */
7864 if (save_rejected_cookies
)
7865 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
7867 /* persistent cookies */
7868 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
7869 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7871 /* session cookies */
7872 s_cookiejar
= soup_cookie_jar_new();
7873 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7874 cookie_policy
, (void *)NULL
);
7877 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7881 setup_proxy(char *uri
)
7884 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7885 soup_uri_free(proxy_uri
);
7889 if (http_proxy
!= uri
) {
7896 http_proxy
= g_strdup(uri
);
7897 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7898 proxy_uri
= soup_uri_new(http_proxy
);
7899 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7904 send_cmd_to_socket(char *cmd
)
7907 struct sockaddr_un sa
;
7909 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7910 warnx("%s: socket", __func__
);
7914 sa
.sun_family
= AF_UNIX
;
7915 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7916 work_dir
, XT_SOCKET_FILE
);
7919 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7920 warnx("%s: connect", __func__
);
7924 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
7925 warnx("%s: send", __func__
);
7936 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7939 char str
[XT_MAX_URL_LENGTH
];
7940 socklen_t t
= sizeof(struct sockaddr_un
);
7941 struct sockaddr_un sa
;
7947 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7952 if (getpeereid(s
, &uid
, &gid
) == -1) {
7956 if (uid
!= getuid() || gid
!= getgid()) {
7957 warnx("unauthorized user");
7963 warnx("not a valid user");
7967 n
= recv(s
, str
, sizeof(str
), 0);
7971 tt
= TAILQ_LAST(&tabs
, tab_list
);
7972 cmd_execute(tt
, str
);
7979 struct sockaddr_un sa
;
7981 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7982 warn("is_running: socket");
7986 sa
.sun_family
= AF_UNIX
;
7987 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7988 work_dir
, XT_SOCKET_FILE
);
7991 /* connect to see if there is a listener */
7992 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7993 rv
= 0; /* not running */
7995 rv
= 1; /* already running */
8006 struct sockaddr_un sa
;
8008 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8009 warn("build_socket: socket");
8013 sa
.sun_family
= AF_UNIX
;
8014 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8015 work_dir
, XT_SOCKET_FILE
);
8018 /* connect to see if there is a listener */
8019 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8020 /* no listener so we will */
8021 unlink(sa
.sun_path
);
8023 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8024 warn("build_socket: bind");
8028 if (listen(s
, 1) == -1) {
8029 warn("build_socket: listen");
8042 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8043 GtkTreeIter
*iter
, struct tab
*t
)
8047 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8054 completion_add_uri(const gchar
*uri
)
8058 /* add uri to list_store */
8059 gtk_list_store_append(completion_model
, &iter
);
8060 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8064 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8065 GtkTreeIter
*iter
, gpointer user_data
)
8068 gboolean match
= FALSE
;
8070 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8076 match
= match_uri(value
, key
);
8083 completion_add(struct tab
*t
)
8085 /* enable completion for tab */
8086 t
->completion
= gtk_entry_completion_new();
8087 gtk_entry_completion_set_text_column(t
->completion
, 0);
8088 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8089 gtk_entry_completion_set_model(t
->completion
,
8090 GTK_TREE_MODEL(completion_model
));
8091 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8093 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8094 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8095 G_CALLBACK(completion_select_cb
), t
);
8102 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8107 main(int argc
, char *argv
[])
8110 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8111 char conf
[PATH_MAX
] = { '\0' };
8112 char file
[PATH_MAX
];
8113 char *env_proxy
= NULL
;
8116 struct sigaction sact
;
8120 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8122 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8131 errx(0 , "Version: %s", version
);
8134 strlcpy(conf
, optarg
, sizeof(conf
));
8137 strlcpy(named_session
, optarg
, sizeof(named_session
));
8158 RB_INIT(&downloads
);
8162 TAILQ_INIT(&aliases
);
8168 gnutls_global_init();
8170 /* generate session keys for xtp pages */
8171 generate_xtp_session_key(&dl_session_key
);
8172 generate_xtp_session_key(&hl_session_key
);
8173 generate_xtp_session_key(&cl_session_key
);
8174 generate_xtp_session_key(&fl_session_key
);
8177 gtk_init(&argc
, &argv
);
8178 if (!g_thread_supported())
8179 g_thread_init(NULL
);
8182 bzero(&sact
, sizeof(sact
));
8183 sigemptyset(&sact
.sa_mask
);
8184 sact
.sa_handler
= sigchild
;
8185 sact
.sa_flags
= SA_NOCLDSTOP
;
8186 sigaction(SIGCHLD
, &sact
, NULL
);
8188 /* set download dir */
8189 pwd
= getpwuid(getuid());
8191 errx(1, "invalid user %d", getuid());
8192 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8194 /* set default string settings */
8195 home
= g_strdup("http://www.peereboom.us");
8196 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8197 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8198 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
8200 /* read config file */
8201 if (strlen(conf
) == 0)
8202 snprintf(conf
, sizeof conf
, "%s/.%s",
8203 pwd
->pw_dir
, XT_CONF_FILE
);
8204 config_parse(conf
, 0);
8206 /* working directory */
8207 if (strlen(work_dir
) == 0)
8208 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8209 pwd
->pw_dir
, XT_DIR
);
8210 if (stat(work_dir
, &sb
)) {
8211 if (mkdir(work_dir
, S_IRWXU
) == -1)
8212 err(1, "mkdir work_dir");
8213 if (stat(work_dir
, &sb
))
8214 err(1, "stat work_dir");
8216 if (S_ISDIR(sb
.st_mode
) == 0)
8217 errx(1, "%s not a dir", work_dir
);
8218 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8219 warnx("fixing invalid permissions on %s", work_dir
);
8220 if (chmod(work_dir
, S_IRWXU
) == -1)
8224 /* icon cache dir */
8225 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8226 if (stat(cache_dir
, &sb
)) {
8227 if (mkdir(cache_dir
, S_IRWXU
) == -1)
8228 err(1, "mkdir cache_dir");
8229 if (stat(cache_dir
, &sb
))
8230 err(1, "stat cache_dir");
8232 if (S_ISDIR(sb
.st_mode
) == 0)
8233 errx(1, "%s not a dir", cache_dir
);
8234 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8235 warnx("fixing invalid permissions on %s", cache_dir
);
8236 if (chmod(cache_dir
, S_IRWXU
) == -1)
8241 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8242 if (stat(certs_dir
, &sb
)) {
8243 if (mkdir(certs_dir
, S_IRWXU
) == -1)
8244 err(1, "mkdir certs_dir");
8245 if (stat(certs_dir
, &sb
))
8246 err(1, "stat certs_dir");
8248 if (S_ISDIR(sb
.st_mode
) == 0)
8249 errx(1, "%s not a dir", certs_dir
);
8250 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8251 warnx("fixing invalid permissions on %s", certs_dir
);
8252 if (chmod(certs_dir
, S_IRWXU
) == -1)
8257 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8258 work_dir
, XT_SESSIONS_DIR
);
8259 if (stat(sessions_dir
, &sb
)) {
8260 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
8261 err(1, "mkdir sessions_dir");
8262 if (stat(sessions_dir
, &sb
))
8263 err(1, "stat sessions_dir");
8265 if (S_ISDIR(sb
.st_mode
) == 0)
8266 errx(1, "%s not a dir", sessions_dir
);
8267 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8268 warnx("fixing invalid permissions on %s", sessions_dir
);
8269 if (chmod(sessions_dir
, S_IRWXU
) == -1)
8272 /* runtime settings that can override config file */
8273 if (runtime_settings
[0] != '\0')
8274 config_parse(runtime_settings
, 1);
8277 if (!strcmp(download_dir
, pwd
->pw_dir
))
8278 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8279 if (stat(download_dir
, &sb
)) {
8280 if (mkdir(download_dir
, S_IRWXU
) == -1)
8281 err(1, "mkdir download_dir");
8282 if (stat(download_dir
, &sb
))
8283 err(1, "stat download_dir");
8285 if (S_ISDIR(sb
.st_mode
) == 0)
8286 errx(1, "%s not a dir", download_dir
);
8287 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8288 warnx("fixing invalid permissions on %s", download_dir
);
8289 if (chmod(download_dir
, S_IRWXU
) == -1)
8293 /* favorites file */
8294 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8295 if (stat(file
, &sb
)) {
8296 warnx("favorites file doesn't exist, creating it");
8297 if ((f
= fopen(file
, "w")) == NULL
)
8298 err(1, "favorites");
8303 session
= webkit_get_default_session();
8308 if (stat(ssl_ca_file
, &sb
)) {
8309 warn("no CA file: %s", ssl_ca_file
);
8310 g_free(ssl_ca_file
);
8313 g_object_set(session
,
8314 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8315 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8320 env_proxy
= getenv("http_proxy");
8322 setup_proxy(env_proxy
);
8324 setup_proxy(http_proxy
);
8327 send_cmd_to_socket(argv
[0]);
8331 /* see if there is already an xxxterm running */
8332 if (single_instance
&& is_running()) {
8334 warnx("already running");
8340 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8341 send_cmd_to_socket(cmd
);
8351 /* uri completion */
8352 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8357 if (save_global_history
)
8358 restore_global_history();
8360 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8361 restore_saved_tabs();
8363 a
.s
= named_session
;
8364 a
.i
= XT_SES_DONOTHING
;
8365 open_tabs(NULL
, &a
);
8369 create_new_tab(argv
[0], NULL
, focus
);
8376 if (TAILQ_EMPTY(&tabs
))
8377 create_new_tab(home
, NULL
, 1);
8380 if ((s
= build_socket()) != -1)
8381 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
8385 gnutls_global_deinit();