3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
8 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * create privacy browsing
26 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #include <bsd/stdlib.h>
49 # if !defined(sane_libbsd_headers)
50 void arc4random_buf(void *, size_t);
52 #elif defined(__FreeBSD__)
54 #include "freebsd/util.h"
60 #include <sys/queue.h>
61 #include <sys/resource.h>
62 #include <sys/socket.h>
68 #include <gdk/gdkkeysyms.h>
70 #if GTK_CHECK_VERSION(3,0,0)
71 /* we still use GDK_* instead of GDK_KEY_* */
72 #include <gdk/gdkkeysyms-compat.h>
75 #include <webkit/webkit.h>
76 #include <libsoup/soup.h>
77 #include <JavaScriptCore/JavaScript.h>
78 #include <gnutls/gnutls.h>
79 #include <gnutls/x509.h>
82 #include "javascript.h"
84 /* comment if you don't want to use threads */
90 GCRY_THREAD_OPTION_PTHREAD_IMPL
;
95 javascript.h borrowed from vimprobable2 under the following license:
97 Copyright (c) 2009 Leon Winter
98 Copyright (c) 2009 Hannes Schueller
99 Copyright (c) 2009 Matto Fransen
101 Permission is hereby granted, free of charge, to any person obtaining a copy
102 of this software and associated documentation files (the "Software"), to deal
103 in the Software without restriction, including without limitation the rights
104 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
105 copies of the Software, and to permit persons to whom the Software is
106 furnished to do so, subject to the following conditions:
108 The above copyright notice and this permission notice shall be included in
109 all copies or substantial portions of the Software.
111 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
112 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
113 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
114 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
115 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
116 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
120 static char *version
= XXXTERM_VERSION
;
122 /* hooked functions */
123 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
124 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
129 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
130 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
131 #define XT_D_MOVE 0x0001
132 #define XT_D_KEY 0x0002
133 #define XT_D_TAB 0x0004
134 #define XT_D_URL 0x0008
135 #define XT_D_CMD 0x0010
136 #define XT_D_NAV 0x0020
137 #define XT_D_DOWNLOAD 0x0040
138 #define XT_D_CONFIG 0x0080
139 #define XT_D_JS 0x0100
140 #define XT_D_FAVORITE 0x0200
141 #define XT_D_PRINTING 0x0400
142 #define XT_D_COOKIE 0x0800
143 #define XT_D_KEYBINDING 0x1000
144 #define XT_D_CLIP 0x2000
145 #define XT_D_BUFFERCMD 0x4000
146 u_int32_t swm_debug
= 0
164 #define DPRINTF(x...)
165 #define DNPRINTF(n,x...)
168 #define LENGTH(x) (sizeof x / sizeof x[0])
169 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
170 ~(GDK_BUTTON1_MASK) & \
171 ~(GDK_BUTTON2_MASK) & \
172 ~(GDK_BUTTON3_MASK) & \
173 ~(GDK_BUTTON4_MASK) & \
176 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
187 TAILQ_ENTRY(tab
) entry
;
189 GtkWidget
*tab_content
;
198 GtkWidget
*uri_entry
;
199 GtkWidget
*search_entry
;
201 GtkWidget
*browser_win
;
202 GtkWidget
*statusbar_box
;
204 GtkWidget
*statusbar
;
205 GtkWidget
*buffercmd
;
216 GtkWidget
*js_toggle
;
217 GtkEntryCompletion
*completion
;
221 WebKitWebHistoryItem
*item
;
222 WebKitWebBackForwardList
*bfl
;
225 WebKitNetworkRequest
*icon_request
;
226 WebKitDownload
*icon_download
;
227 gchar
*icon_dest_uri
;
229 /* adjustments for browser */
232 GtkAdjustment
*adjust_h
;
233 GtkAdjustment
*adjust_v
;
239 int xtp_meaning
; /* identifies dls/favorites */
241 int popup
; /* 1 if cmd_entry has popup visible */
243 /* https thread stuff */
249 #define XT_HINT_NONE (0)
250 #define XT_HINT_NUMERICAL (1)
251 #define XT_HINT_ALPHANUM (2)
255 /* custom stylesheet */
265 WebKitWebSettings
*settings
;
269 double mark
[XT_NOMARKS
];
271 TAILQ_HEAD(tab_list
, tab
);
274 RB_ENTRY(history
) entry
;
278 RB_HEAD(history_list
, history
);
281 TAILQ_ENTRY(session
) entry
;
284 TAILQ_HEAD(session_list
, session
);
287 RB_ENTRY(download
) entry
;
289 WebKitDownload
*download
;
292 RB_HEAD(download_list
, download
);
295 RB_ENTRY(domain
) entry
;
297 int handy
; /* app use */
299 RB_HEAD(domain_list
, domain
);
302 TAILQ_ENTRY(undo
) entry
;
305 int back
; /* Keeps track of how many back
306 * history items there are. */
308 TAILQ_HEAD(undo_tailq
, undo
);
312 TAILQ_ENTRY(sp
) entry
;
314 TAILQ_HEAD(sp_list
, sp
);
316 struct command_entry
{
318 TAILQ_ENTRY(command_entry
) entry
;
320 TAILQ_HEAD(command_list
, command_entry
);
322 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
323 int next_download_id
= 1;
332 #define XT_NAME ("XXXTerm")
333 #define XT_DIR (".xxxterm")
334 #define XT_CACHE_DIR ("cache")
335 #define XT_CERT_DIR ("certs/")
336 #define XT_SESSIONS_DIR ("sessions/")
337 #define XT_CONF_FILE ("xxxterm.conf")
338 #define XT_FAVS_FILE ("favorites")
339 #define XT_QMARKS_FILE ("quickmarks")
340 #define XT_SAVED_TABS_FILE ("main_session")
341 #define XT_RESTART_TABS_FILE ("restart_tabs")
342 #define XT_SOCKET_FILE ("socket")
343 #define XT_HISTORY_FILE ("history")
344 #define XT_REJECT_FILE ("rejected.txt")
345 #define XT_COOKIE_FILE ("cookies.txt")
346 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
347 #define XT_SEARCH_FILE ("search_history")
348 #define XT_COMMAND_FILE ("command_history")
349 #define XT_CB_HANDLED (TRUE)
350 #define XT_CB_PASSTHROUGH (FALSE)
351 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
352 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
353 #define XT_DLMAN_REFRESH "10"
354 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
355 "td{overflow: hidden;" \
356 " padding: 2px 2px 2px 2px;" \
357 " border: 1px solid black;" \
358 " vertical-align:top;" \
359 " word-wrap: break-word}\n" \
360 "tr:hover{background: #ffff99}\n" \
361 "th{background-color: #cccccc;" \
362 " border: 1px solid black}\n" \
363 "table{width: 100%%;" \
364 " border: 1px black solid;" \
365 " border-collapse:collapse}\n" \
367 "border: 1px solid black;" \
370 ".progress-inner{float: left;" \
372 " background: green}\n" \
373 ".dlstatus{font-size: small;" \
374 " text-align: center}\n" \
376 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
377 #define XT_MAX_UNDO_CLOSE_TAB (32)
378 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
379 #define XT_PRINT_EXTRA_MARGIN 10
380 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
381 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
384 #define XT_COLOR_RED "#cc0000"
385 #define XT_COLOR_YELLOW "#ffff66"
386 #define XT_COLOR_BLUE "lightblue"
387 #define XT_COLOR_GREEN "#99ff66"
388 #define XT_COLOR_WHITE "white"
389 #define XT_COLOR_BLACK "black"
391 #define XT_COLOR_CT_BACKGROUND "#000000"
392 #define XT_COLOR_CT_INACTIVE "#dddddd"
393 #define XT_COLOR_CT_ACTIVE "#bbbb00"
394 #define XT_COLOR_CT_SEPARATOR "#555555"
396 #define XT_COLOR_SB_SEPARATOR "#555555"
398 #define XT_PROTO_DELIM "://"
401 * xxxterm "protocol" (xtp)
402 * We use this for managing stuff like downloads and favorites. They
403 * make magical HTML pages in memory which have xxxt:// links in order
404 * to communicate with xxxterm's internals. These links take the format:
405 * xxxt://class/session_key/action/arg
407 * Don't begin xtp class/actions as 0. atoi returns that on error.
409 * Typically we have not put addition of items in this framework, as
410 * adding items is either done via an ex-command or via a keybinding instead.
413 #define XT_XTP_STR "xxxt://"
415 /* XTP classes (xxxt://<class>) */
416 #define XT_XTP_INVALID 0 /* invalid */
417 #define XT_XTP_DL 1 /* downloads */
418 #define XT_XTP_HL 2 /* history */
419 #define XT_XTP_CL 3 /* cookies */
420 #define XT_XTP_FL 4 /* favorites */
422 /* XTP download actions */
423 #define XT_XTP_DL_LIST 1
424 #define XT_XTP_DL_CANCEL 2
425 #define XT_XTP_DL_REMOVE 3
427 /* XTP history actions */
428 #define XT_XTP_HL_LIST 1
429 #define XT_XTP_HL_REMOVE 2
431 /* XTP cookie actions */
432 #define XT_XTP_CL_LIST 1
433 #define XT_XTP_CL_REMOVE 2
435 /* XTP cookie actions */
436 #define XT_XTP_FL_LIST 1
437 #define XT_XTP_FL_REMOVE 2
440 #define XT_MOVE_INVALID (0)
441 #define XT_MOVE_DOWN (1)
442 #define XT_MOVE_UP (2)
443 #define XT_MOVE_BOTTOM (3)
444 #define XT_MOVE_TOP (4)
445 #define XT_MOVE_PAGEDOWN (5)
446 #define XT_MOVE_PAGEUP (6)
447 #define XT_MOVE_HALFDOWN (7)
448 #define XT_MOVE_HALFUP (8)
449 #define XT_MOVE_LEFT (9)
450 #define XT_MOVE_FARLEFT (10)
451 #define XT_MOVE_RIGHT (11)
452 #define XT_MOVE_FARRIGHT (12)
453 #define XT_MOVE_PERCENT (13)
455 #define XT_QMARK_SET (0)
456 #define XT_QMARK_OPEN (1)
457 #define XT_QMARK_TAB (2)
459 #define XT_MARK_SET (0)
460 #define XT_MARK_GOTO (1)
462 #define XT_TAB_LAST (-4)
463 #define XT_TAB_FIRST (-3)
464 #define XT_TAB_PREV (-2)
465 #define XT_TAB_NEXT (-1)
466 #define XT_TAB_INVALID (0)
467 #define XT_TAB_NEW (1)
468 #define XT_TAB_DELETE (2)
469 #define XT_TAB_DELQUIT (3)
470 #define XT_TAB_OPEN (4)
471 #define XT_TAB_UNDO_CLOSE (5)
472 #define XT_TAB_SHOW (6)
473 #define XT_TAB_HIDE (7)
474 #define XT_TAB_NEXTSTYLE (8)
476 #define XT_NAV_INVALID (0)
477 #define XT_NAV_BACK (1)
478 #define XT_NAV_FORWARD (2)
479 #define XT_NAV_RELOAD (3)
481 #define XT_FOCUS_INVALID (0)
482 #define XT_FOCUS_URI (1)
483 #define XT_FOCUS_SEARCH (2)
485 #define XT_SEARCH_INVALID (0)
486 #define XT_SEARCH_NEXT (1)
487 #define XT_SEARCH_PREV (2)
489 #define XT_PASTE_CURRENT_TAB (0)
490 #define XT_PASTE_NEW_TAB (1)
492 #define XT_ZOOM_IN (-1)
493 #define XT_ZOOM_OUT (-2)
494 #define XT_ZOOM_NORMAL (100)
496 #define XT_URL_SHOW (1)
497 #define XT_URL_HIDE (2)
499 #define XT_WL_TOGGLE (1<<0)
500 #define XT_WL_ENABLE (1<<1)
501 #define XT_WL_DISABLE (1<<2)
502 #define XT_WL_FQDN (1<<3) /* default */
503 #define XT_WL_TOPLEVEL (1<<4)
504 #define XT_WL_PERSISTENT (1<<5)
505 #define XT_WL_SESSION (1<<6)
506 #define XT_WL_RELOAD (1<<7)
508 #define XT_SHOW (1<<7)
509 #define XT_DELETE (1<<8)
510 #define XT_SAVE (1<<9)
511 #define XT_OPEN (1<<10)
513 #define XT_CMD_OPEN (0)
514 #define XT_CMD_OPEN_CURRENT (1)
515 #define XT_CMD_TABNEW (2)
516 #define XT_CMD_TABNEW_CURRENT (3)
518 #define XT_STATUS_NOTHING (0)
519 #define XT_STATUS_LINK (1)
520 #define XT_STATUS_URI (2)
521 #define XT_STATUS_LOADING (3)
523 #define XT_SES_DONOTHING (0)
524 #define XT_SES_CLOSETABS (1)
526 #define XT_BM_NORMAL (0)
527 #define XT_BM_WHITELIST (1)
528 #define XT_BM_KIOSK (2)
530 #define XT_PREFIX (1<<0)
531 #define XT_USERARG (1<<1)
532 #define XT_URLARG (1<<2)
533 #define XT_INTARG (1<<3)
534 #define XT_SESSARG (1<<4)
535 #define XT_SETARG (1<<5)
537 #define XT_TABS_NORMAL 0
538 #define XT_TABS_COMPACT 1
540 #define XT_BUFCMD_SZ (8)
548 TAILQ_ENTRY(mime_type
) entry
;
550 TAILQ_HEAD(mime_type_list
, mime_type
);
556 TAILQ_ENTRY(alias
) entry
;
558 TAILQ_HEAD(alias_list
, alias
);
560 /* settings that require restart */
561 int tabless
= 0; /* allow only 1 tab */
562 int enable_socket
= 0;
563 int single_instance
= 0; /* only allow one xxxterm to run */
564 int fancy_bar
= 1; /* fancy toolbar */
565 int browser_mode
= XT_BM_NORMAL
;
566 int enable_localstorage
= 1;
567 char *statusbar_elems
= NULL
;
569 /* runtime settings */
570 int show_tabs
= 1; /* show tabs on notebook */
571 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
572 int show_url
= 1; /* show url toolbar on notebook */
573 int show_statusbar
= 0; /* vimperator style status bar */
574 int ctrl_click_focus
= 0; /* ctrl click gets focus */
575 int cookies_enabled
= 1; /* enable cookies */
576 int read_only_cookies
= 0; /* enable to not write cookies */
577 int enable_scripts
= 1;
578 int enable_plugins
= 1;
579 gfloat default_zoom_level
= 1.0;
580 char default_script
[PATH_MAX
];
581 int window_height
= 768;
582 int window_width
= 1024;
583 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
584 int refresh_interval
= 10; /* download refresh interval */
585 int enable_plugin_whitelist
= 0;
586 int enable_cookie_whitelist
= 0;
587 int enable_js_whitelist
= 0;
588 int session_timeout
= 3600; /* cookie session timeout */
589 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
590 char *ssl_ca_file
= NULL
;
591 char *resource_dir
= NULL
;
592 gboolean ssl_strict_certs
= FALSE
;
593 int append_next
= 1; /* append tab after current tab */
595 char *search_string
= NULL
;
596 char *http_proxy
= NULL
;
597 char download_dir
[PATH_MAX
];
598 char runtime_settings
[PATH_MAX
]; /* override of settings */
599 int allow_volatile_cookies
= 0;
600 int save_global_history
= 0; /* save global history to disk */
601 char *user_agent
= NULL
;
602 int save_rejected_cookies
= 0;
603 int session_autosave
= 0;
604 int guess_search
= 0;
605 int dns_prefetch
= FALSE
;
606 gint max_connections
= 25;
607 gint max_host_connections
= 5;
608 gint enable_spell_checking
= 0;
609 char *spell_check_languages
= NULL
;
610 int xterm_workaround
= 0;
611 char *url_regex
= NULL
;
612 int history_autosave
= 0;
613 char search_file
[PATH_MAX
];
614 char command_file
[PATH_MAX
];
615 char *encoding
= NULL
;
617 char *cmd_font_name
= NULL
;
618 char *oops_font_name
= NULL
;
619 char *statusbar_font_name
= NULL
;
620 char *tabbar_font_name
= NULL
;
621 PangoFontDescription
*cmd_font
;
622 PangoFontDescription
*oops_font
;
623 PangoFontDescription
*statusbar_font
;
624 PangoFontDescription
*tabbar_font
;
625 char *qmarks
[XT_NOMARKS
];
627 int btn_down
; /* M1 down in any wv */
628 regex_t url_re
; /* guess_search regex */
632 int set_browser_mode(struct settings
*, char *);
633 int set_cookie_policy(struct settings
*, char *);
634 int set_download_dir(struct settings
*, char *);
635 int set_default_script(struct settings
*, char *);
636 int set_runtime_dir(struct settings
*, char *);
637 int set_tab_style(struct settings
*, char *);
638 int set_work_dir(struct settings
*, char *);
639 int add_alias(struct settings
*, char *);
640 int add_mime_type(struct settings
*, char *);
641 int add_cookie_wl(struct settings
*, char *);
642 int add_js_wl(struct settings
*, char *);
643 int add_pl_wl(struct settings
*, char *);
644 int add_kb(struct settings
*, char *);
645 void button_set_stockid(GtkWidget
*, char *);
646 GtkWidget
* create_button(char *, char *, int);
648 char *get_browser_mode(struct settings
*);
649 char *get_cookie_policy(struct settings
*);
650 char *get_download_dir(struct settings
*);
651 char *get_default_script(struct settings
*);
652 char *get_runtime_dir(struct settings
*);
653 char *get_tab_style(struct settings
*);
654 char *get_work_dir(struct settings
*);
655 void startpage_add(const char *, ...);
657 void walk_alias(struct settings
*, void (*)(struct settings
*,
658 char *, void *), void *);
659 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
660 char *, void *), void *);
661 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
662 char *, void *), void *);
663 void walk_pl_wl(struct settings
*, void (*)(struct settings
*,
664 char *, void *), void *);
665 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
667 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
668 char *, void *), void *);
670 void recalc_tabs(void);
671 void recolor_compact_tabs(void);
672 void set_current_tab(int page_num
);
673 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
674 void marks_clear(struct tab
*t
);
676 int set_http_proxy(char *);
679 int (*set
)(struct settings
*, char *);
680 char *(*get
)(struct settings
*);
681 void (*walk
)(struct settings
*,
682 void (*cb
)(struct settings
*, char *, void *),
686 struct special s_browser_mode
= {
692 struct special s_cookie
= {
698 struct special s_alias
= {
704 struct special s_mime
= {
710 struct special s_js
= {
716 struct special s_pl
= {
722 struct special s_kb
= {
728 struct special s_cookie_wl
= {
734 struct special s_default_script
= {
740 struct special s_download_dir
= {
746 struct special s_work_dir
= {
752 struct special s_tab_style
= {
761 #define XT_S_INVALID (0)
764 #define XT_S_FLOAT (3)
766 #define XT_SF_RESTART (1<<0)
767 #define XT_SF_RUNTIME (1<<1)
772 int (*activate
)(char *);
774 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
775 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
776 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
777 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
778 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
779 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
780 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
781 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
782 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
783 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
784 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
785 { "enable_plugin_whitelist", XT_S_INT
, 0, &enable_plugin_whitelist
, NULL
, NULL
},
786 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
787 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
788 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
789 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
790 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
791 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
792 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
793 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
794 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
795 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
796 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
797 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
798 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
799 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
800 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
801 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
802 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
803 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
804 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
805 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
806 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
807 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
808 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
809 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
810 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
811 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
812 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
813 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
814 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
815 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
816 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
817 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
818 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
819 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
820 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
821 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
822 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
825 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
826 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
827 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
828 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
830 /* runtime settings */
831 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
832 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
833 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
834 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
835 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
836 { "pl_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_pl
},
839 int about(struct tab
*, struct karg
*);
840 int blank(struct tab
*, struct karg
*);
841 int ca_cmd(struct tab
*, struct karg
*);
842 int cookie_show_wl(struct tab
*, struct karg
*);
843 int js_show_wl(struct tab
*, struct karg
*);
844 int pl_show_wl(struct tab
*, struct karg
*);
845 int help(struct tab
*, struct karg
*);
846 int set(struct tab
*, struct karg
*);
847 int stats(struct tab
*, struct karg
*);
848 int marco(struct tab
*, struct karg
*);
849 int startpage(struct tab
*, struct karg
*);
850 const char * marco_message(int *);
851 int xtp_page_cl(struct tab
*, struct karg
*);
852 int xtp_page_dl(struct tab
*, struct karg
*);
853 int xtp_page_fl(struct tab
*, struct karg
*);
854 int xtp_page_hl(struct tab
*, struct karg
*);
855 void xt_icon_from_file(struct tab
*, char *);
856 const gchar
*get_uri(struct tab
*);
857 const gchar
*get_title(struct tab
*, bool);
859 #define XT_URI_ABOUT ("about:")
860 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
861 #define XT_URI_ABOUT_ABOUT ("about")
862 #define XT_URI_ABOUT_BLANK ("blank")
863 #define XT_URI_ABOUT_CERTS ("certs")
864 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
865 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
866 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
867 #define XT_URI_ABOUT_FAVORITES ("favorites")
868 #define XT_URI_ABOUT_HELP ("help")
869 #define XT_URI_ABOUT_HISTORY ("history")
870 #define XT_URI_ABOUT_JSWL ("jswl")
871 #define XT_URI_ABOUT_PLUGINWL ("plwl")
872 #define XT_URI_ABOUT_SET ("set")
873 #define XT_URI_ABOUT_STATS ("stats")
874 #define XT_URI_ABOUT_MARCO ("marco")
875 #define XT_URI_ABOUT_STARTPAGE ("startpage")
879 int (*func
)(struct tab
*, struct karg
*);
881 { XT_URI_ABOUT_ABOUT
, about
},
882 { XT_URI_ABOUT_BLANK
, blank
},
883 { XT_URI_ABOUT_CERTS
, ca_cmd
},
884 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
885 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
886 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
887 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
888 { XT_URI_ABOUT_HELP
, help
},
889 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
890 { XT_URI_ABOUT_JSWL
, js_show_wl
},
891 { XT_URI_ABOUT_SET
, set
},
892 { XT_URI_ABOUT_STATS
, stats
},
893 { XT_URI_ABOUT_MARCO
, marco
},
894 { XT_URI_ABOUT_STARTPAGE
, startpage
},
895 { XT_URI_ABOUT_PLUGINWL
, pl_show_wl
},
898 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
899 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
900 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
901 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
902 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
903 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
904 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
907 extern char *__progname
;
910 GtkWidget
*main_window
;
911 GtkNotebook
*notebook
;
913 GtkWidget
*arrow
, *abtn
;
914 struct tab_list tabs
;
915 struct history_list hl
;
916 struct session_list sessions
;
917 struct download_list downloads
;
918 struct domain_list c_wl
;
919 struct domain_list js_wl
;
920 struct domain_list pl_wl
;
921 struct undo_tailq undos
;
922 struct keybinding_list kbl
;
924 struct command_list chl
;
925 struct command_list shl
;
926 struct command_entry
*history_at
;
927 struct command_entry
*search_at
;
929 int updating_dl_tabs
= 0;
930 int updating_hl_tabs
= 0;
931 int updating_cl_tabs
= 0;
932 int updating_fl_tabs
= 0;
933 int cmd_history_count
= 0;
934 int search_history_count
= 0;
936 long long unsigned int blocked_cookies
= 0;
937 char named_session
[PATH_MAX
];
938 GtkListStore
*completion_model
;
939 GtkListStore
*buffers_store
;
941 void xxx_dir(char *);
942 int icon_size_map(int);
943 void completion_add(struct tab
*);
944 void completion_add_uri(const gchar
*);
945 void show_oops(struct tab
*, const char *, ...);
948 history_delete(struct command_list
*l
, int *counter
)
950 struct command_entry
*c
;
952 if (l
== NULL
|| counter
== NULL
)
955 c
= TAILQ_LAST(l
, command_list
);
959 TAILQ_REMOVE(l
, c
, entry
);
966 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
968 struct command_entry
*c
;
971 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
974 /* don't add the same line */
975 c
= TAILQ_FIRST(list
);
977 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
980 c
= g_malloc0(sizeof *c
);
981 c
->line
= g_strdup_printf(" %s", l
);
984 TAILQ_INSERT_HEAD(list
, c
, entry
);
987 history_delete(list
, counter
);
989 if (history_autosave
&& file
) {
990 f
= fopen(file
, "w");
992 show_oops(NULL
, "couldn't write history %s", file
);
996 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
998 fprintf(f
, "%s\n", c
->line
);
1006 history_read(struct command_list
*list
, char *file
, int *counter
)
1009 char *s
, line
[65536];
1011 if (list
== NULL
|| file
== NULL
)
1014 f
= fopen(file
, "r");
1016 startpage_add("couldn't open history file %s", file
);
1021 s
= fgets(line
, sizeof line
, f
);
1022 if (s
== NULL
|| feof(f
) || ferror(f
))
1024 if ((s
= strchr(line
, '\n')) == NULL
) {
1025 startpage_add("invalid history file %s", file
);
1031 history_add(list
, NULL
, line
+ 1, counter
);
1039 /* marks and quickmarks array storage.
1040 * first a-z, then A-Z, then 0-9 */
1047 if (i
>= 0 && i
<= 'z' - 'a')
1051 if (i
>= 0 && i
<= 'Z' - 'A')
1066 if (m
>= 'a' && m
<= 'z')
1067 return ret
+ m
- 'a';
1069 ret
+= 'z' - 'a' + 1;
1070 if (m
>= 'A' && m
<= 'Z')
1071 return ret
+ m
- 'A';
1073 ret
+= 'Z' - 'A' + 1;
1074 if (m
>= '0' && m
<= '9')
1075 return ret
+ m
- '0';
1084 int saved_errno
, status
;
1087 saved_errno
= errno
;
1089 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1093 if (errno
!= ECHILD
) {
1095 clog_warn("sigchild: waitpid:");
1101 if (WIFEXITED(status
)) {
1102 if (WEXITSTATUS(status
) != 0) {
1104 clog_warnx("sigchild: child exit status: %d",
1105 WEXITSTATUS(status));
1110 clog_warnx("sigchild: child is terminated abnormally");
1115 errno
= saved_errno
;
1119 is_g_object_setting(GObject
*o
, char *str
)
1121 guint n_props
= 0, i
;
1122 GParamSpec
**proplist
;
1124 if (! G_IS_OBJECT(o
))
1127 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1130 for (i
=0; i
< n_props
; i
++) {
1131 if (! strcmp(proplist
[i
]->name
, str
))
1138 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1142 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1144 "<title>%s</title>\n"
1153 addstyles
? XT_PAGE_STYLE
: "",
1162 * Display a web page from a HTML string in memory, rather than from a URL
1165 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1167 char file
[PATH_MAX
];
1170 /* we set this to indicate we want to manually do navaction */
1172 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1174 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1176 /* set t->xtp_meaning */
1177 for (i
= 0; i
< LENGTH(about_list
); i
++)
1178 if (!strcmp(title
, about_list
[i
].name
)) {
1183 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
1185 #if GTK_CHECK_VERSION(2, 20, 0)
1186 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1187 gtk_widget_hide(t
->spinner
);
1189 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1190 xt_icon_from_file(t
, file
);
1195 get_current_tab(void)
1199 TAILQ_FOREACH(t
, &tabs
, entry
) {
1200 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1204 warnx("%s: no current tab", __func__
);
1210 set_status(struct tab
*t
, gchar
*s
, int status
)
1218 case XT_STATUS_LOADING
:
1219 type
= g_strdup_printf("Loading: %s", s
);
1222 case XT_STATUS_LINK
:
1223 type
= g_strdup_printf("Link: %s", s
);
1225 t
->status
= g_strdup(gtk_entry_get_text(
1226 GTK_ENTRY(t
->sbe
.statusbar
)));
1230 type
= g_strdup_printf("%s", s
);
1232 t
->status
= g_strdup(type
);
1236 t
->status
= g_strdup(s
);
1238 case XT_STATUS_NOTHING
:
1243 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1249 hide_cmd(struct tab
*t
)
1251 history_at
= NULL
; /* just in case */
1252 search_at
= NULL
; /* just in case */
1253 gtk_widget_hide(t
->cmd
);
1257 show_cmd(struct tab
*t
)
1261 gtk_widget_hide(t
->oops
);
1262 gtk_widget_show(t
->cmd
);
1266 hide_buffers(struct tab
*t
)
1268 gtk_widget_hide(t
->buffers
);
1269 gtk_list_store_clear(buffers_store
);
1279 sort_tabs_by_page_num(struct tab
***stabs
)
1284 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1286 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1288 TAILQ_FOREACH(t
, &tabs
, entry
)
1289 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1295 buffers_make_list(void)
1298 const gchar
*title
= NULL
;
1300 struct tab
**stabs
= NULL
;
1302 num_tabs
= sort_tabs_by_page_num(&stabs
);
1304 for (i
= 0; i
< num_tabs
; i
++)
1306 gtk_list_store_append(buffers_store
, &iter
);
1307 title
= get_title(stabs
[i
], FALSE
);
1308 gtk_list_store_set(buffers_store
, &iter
,
1309 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1319 show_buffers(struct tab
*t
)
1321 buffers_make_list();
1322 gtk_widget_show(t
->buffers
);
1323 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1327 toggle_buffers(struct tab
*t
)
1329 if (gtk_widget_get_visible(t
->buffers
))
1336 buffers(struct tab
*t
, struct karg
*args
)
1344 hide_oops(struct tab
*t
)
1346 gtk_widget_hide(t
->oops
);
1350 show_oops(struct tab
*at
, const char *fmt
, ...)
1354 struct tab
*t
= NULL
;
1360 if ((t
= get_current_tab()) == NULL
)
1366 if (vasprintf(&msg
, fmt
, ap
) == -1)
1367 errx(1, "show_oops failed");
1370 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1371 gtk_widget_hide(t
->cmd
);
1372 gtk_widget_show(t
->oops
);
1379 get_as_string(struct settings
*s
)
1390 warnx("get_as_string skip %s\n", s
->name
);
1391 } else if (s
->type
== XT_S_INT
)
1392 r
= g_strdup_printf("%d", *s
->ival
);
1393 else if (s
->type
== XT_S_STR
)
1394 r
= g_strdup(*s
->sval
);
1395 else if (s
->type
== XT_S_FLOAT
)
1396 r
= g_strdup_printf("%f", *s
->fval
);
1398 r
= g_strdup_printf("INVALID TYPE");
1404 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1409 for (i
= 0; i
< LENGTH(rs
); i
++) {
1410 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1411 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1413 s
= get_as_string(&rs
[i
]);
1414 cb(&rs
[i
], s
, cb_args
);
1421 set_browser_mode(struct settings
*s
, char *val
)
1423 if (!strcmp(val
, "whitelist")) {
1424 browser_mode
= XT_BM_WHITELIST
;
1425 allow_volatile_cookies
= 0;
1426 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1427 cookies_enabled
= 1;
1428 enable_cookie_whitelist
= 1;
1429 enable_plugin_whitelist
= 1;
1431 read_only_cookies
= 0;
1432 save_rejected_cookies
= 0;
1433 session_timeout
= 3600;
1435 enable_js_whitelist
= 1;
1436 enable_localstorage
= 0;
1437 } else if (!strcmp(val
, "normal")) {
1438 browser_mode
= XT_BM_NORMAL
;
1439 allow_volatile_cookies
= 0;
1440 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1441 cookies_enabled
= 1;
1442 enable_cookie_whitelist
= 0;
1443 enable_plugin_whitelist
= 0;
1445 read_only_cookies
= 0;
1446 save_rejected_cookies
= 0;
1447 session_timeout
= 3600;
1449 enable_js_whitelist
= 0;
1450 enable_localstorage
= 1;
1451 } else if (!strcmp(val
, "kiosk")) {
1452 browser_mode
= XT_BM_KIOSK
;
1453 allow_volatile_cookies
= 0;
1454 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1455 cookies_enabled
= 1;
1456 enable_cookie_whitelist
= 0;
1457 enable_plugin_whitelist
= 0;
1459 read_only_cookies
= 0;
1460 save_rejected_cookies
= 0;
1461 session_timeout
= 3600;
1463 enable_js_whitelist
= 0;
1464 enable_localstorage
= 1;
1474 get_browser_mode(struct settings
*s
)
1478 if (browser_mode
== XT_BM_WHITELIST
)
1479 r
= g_strdup("whitelist");
1480 else if (browser_mode
== XT_BM_NORMAL
)
1481 r
= g_strdup("normal");
1482 else if (browser_mode
== XT_BM_KIOSK
)
1483 r
= g_strdup("kiosk");
1491 set_cookie_policy(struct settings
*s
, char *val
)
1493 if (!strcmp(val
, "no3rdparty"))
1494 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1495 else if (!strcmp(val
, "accept"))
1496 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1497 else if (!strcmp(val
, "reject"))
1498 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1506 get_cookie_policy(struct settings
*s
)
1510 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1511 r
= g_strdup("no3rdparty");
1512 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1513 r
= g_strdup("accept");
1514 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1515 r
= g_strdup("reject");
1523 get_default_script(struct settings
*s
)
1525 if (default_script
[0] == '\0')
1527 return (g_strdup(default_script
));
1531 set_default_script(struct settings
*s
, char *val
)
1534 snprintf(default_script
, sizeof default_script
, "%s/%s",
1535 pwd
->pw_dir
, &val
[1]);
1537 strlcpy(default_script
, val
, sizeof default_script
);
1543 get_download_dir(struct settings
*s
)
1545 if (download_dir
[0] == '\0')
1547 return (g_strdup(download_dir
));
1551 set_download_dir(struct settings
*s
, char *val
)
1554 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1555 pwd
->pw_dir
, &val
[1]);
1557 strlcpy(download_dir
, val
, sizeof download_dir
);
1564 * We use these to prevent people putting xxxt:// URLs on
1565 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1567 #define XT_XTP_SES_KEY_SZ 8
1568 #define XT_XTP_SES_KEY_HEX_FMT \
1569 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1570 char *dl_session_key
; /* downloads */
1571 char *hl_session_key
; /* history list */
1572 char *cl_session_key
; /* cookie list */
1573 char *fl_session_key
; /* favorites list */
1575 char work_dir
[PATH_MAX
];
1576 char certs_dir
[PATH_MAX
];
1577 char cache_dir
[PATH_MAX
];
1578 char sessions_dir
[PATH_MAX
];
1579 char cookie_file
[PATH_MAX
];
1580 SoupURI
*proxy_uri
= NULL
;
1581 SoupSession
*session
;
1582 SoupCookieJar
*s_cookiejar
;
1583 SoupCookieJar
*p_cookiejar
;
1584 char rc_fname
[PATH_MAX
];
1586 struct mime_type_list mtl
;
1587 struct alias_list aliases
;
1590 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1591 void delete_tab(struct tab
*);
1592 void setzoom_webkit(struct tab
*, int);
1593 int run_script(struct tab
*, char *);
1594 int download_rb_cmp(struct download
*, struct download
*);
1595 gboolean
cmd_execute(struct tab
*t
, char *str
);
1598 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1600 return (strcmp(h1
->uri
, h2
->uri
));
1602 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1605 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1607 return (strcmp(d1
->d
, d2
->d
));
1609 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1612 get_work_dir(struct settings
*s
)
1614 if (work_dir
[0] == '\0')
1616 return (g_strdup(work_dir
));
1620 set_work_dir(struct settings
*s
, char *val
)
1623 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1624 pwd
->pw_dir
, &val
[1]);
1626 strlcpy(work_dir
, val
, sizeof work_dir
);
1632 get_tab_style(struct settings
*s
)
1634 if (tab_style
== XT_TABS_NORMAL
)
1635 return (g_strdup("normal"));
1637 return (g_strdup("compact"));
1641 set_tab_style(struct settings
*s
, char *val
)
1643 if (!strcmp(val
, "normal"))
1644 tab_style
= XT_TABS_NORMAL
;
1645 else if (!strcmp(val
, "compact"))
1646 tab_style
= XT_TABS_COMPACT
;
1654 * generate a session key to secure xtp commands.
1655 * pass in a ptr to the key in question and it will
1656 * be modified in place.
1659 generate_xtp_session_key(char **key
)
1661 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1667 /* make a new one */
1668 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1669 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1670 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1671 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1673 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1677 * validate a xtp session key.
1681 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1683 if (strcmp(trusted
, untrusted
) != 0) {
1684 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1693 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1695 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1697 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1699 struct valid_url_types
{
1710 valid_url_type(char *url
)
1714 for (i
= 0; i
< LENGTH(vut
); i
++)
1715 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1722 print_cookie(char *msg
, SoupCookie
*c
)
1728 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1729 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1730 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1731 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1732 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1733 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1734 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1735 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1736 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1737 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1741 walk_alias(struct settings
*s
,
1742 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1747 if (s
== NULL
|| cb
== NULL
) {
1748 show_oops(NULL
, "walk_alias invalid parameters");
1752 TAILQ_FOREACH(a
, &aliases
, entry
) {
1753 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1754 cb(s
, str
, cb_args
);
1760 match_alias(char *url_in
)
1764 char *url_out
= NULL
, *search
, *enc_arg
;
1766 search
= g_strdup(url_in
);
1768 if (strsep(&arg
, " \t") == NULL
) {
1769 show_oops(NULL
, "match_alias: NULL URL");
1773 TAILQ_FOREACH(a
, &aliases
, entry
) {
1774 if (!strcmp(search
, a
->a_name
))
1779 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1782 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1783 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1786 url_out
= g_strdup_printf(a
->a_uri
, "");
1794 guess_url_type(char *url_in
)
1797 char *url_out
= NULL
, *enc_search
= NULL
;
1800 /* substitute aliases */
1801 url_out
= match_alias(url_in
);
1802 if (url_out
!= NULL
)
1805 /* see if we are an about page */
1806 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1807 for (i
= 0; i
< LENGTH(about_list
); i
++)
1808 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1809 about_list
[i
].name
)) {
1810 url_out
= g_strdup(url_in
);
1814 if (guess_search
&& url_regex
&&
1815 !(g_str_has_prefix(url_in
, "http://") ||
1816 g_str_has_prefix(url_in
, "https://"))) {
1817 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1818 /* invalid URI so search instead */
1819 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1820 url_out
= g_strdup_printf(search_string
, enc_search
);
1826 /* XXX not sure about this heuristic */
1827 if (stat(url_in
, &sb
) == 0)
1828 url_out
= g_strdup_printf("file://%s", url_in
);
1830 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1832 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1838 load_uri(struct tab
*t
, gchar
*uri
)
1841 gchar
*newuri
= NULL
;
1847 /* Strip leading spaces. */
1848 while (*uri
&& isspace(*uri
))
1851 if (strlen(uri
) == 0) {
1856 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1858 if (valid_url_type(uri
)) {
1859 newuri
= guess_url_type(uri
);
1863 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1864 for (i
= 0; i
< LENGTH(about_list
); i
++)
1865 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1866 bzero(&args
, sizeof args
);
1867 about_list
[i
].func(t
, &args
);
1868 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1872 show_oops(t
, "invalid about page");
1876 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1878 webkit_web_view_load_uri(t
->wv
, uri
);
1885 get_uri(struct tab
*t
)
1887 const gchar
*uri
= NULL
;
1889 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1891 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1892 uri
= webkit_web_view_get_uri(t
->wv
);
1894 /* use tmp_uri to make sure it is g_freed */
1897 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1898 about_list
[t
->xtp_meaning
].name
);
1905 get_title(struct tab
*t
, bool window
)
1907 const gchar
*set
= NULL
, *title
= NULL
;
1908 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1910 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1911 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1914 title
= webkit_web_view_get_title(t
->wv
);
1915 if ((set
= title
? title
: get_uri(t
)))
1919 set
= window
? XT_NAME
: "(untitled)";
1925 add_alias(struct settings
*s
, char *line
)
1928 struct alias
*a
= NULL
;
1930 if (s
== NULL
|| line
== NULL
) {
1931 show_oops(NULL
, "add_alias invalid parameters");
1936 a
= g_malloc(sizeof(*a
));
1938 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1939 show_oops(NULL
, "add_alias: incomplete alias definition");
1942 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1943 show_oops(NULL
, "add_alias: invalid alias definition");
1947 a
->a_name
= g_strdup(alias
);
1948 a
->a_uri
= g_strdup(l
);
1950 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1952 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1962 add_mime_type(struct settings
*s
, char *line
)
1966 struct mime_type
*m
= NULL
;
1967 int downloadfirst
= 0;
1969 /* XXX this could be smarter */
1971 if (line
== NULL
|| strlen(line
) == 0) {
1972 show_oops(NULL
, "add_mime_type invalid parameters");
1981 m
= g_malloc(sizeof(*m
));
1983 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1984 show_oops(NULL
, "add_mime_type: invalid mime_type");
1987 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1988 mime_type
[strlen(mime_type
) - 1] = '\0';
1993 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1994 show_oops(NULL
, "add_mime_type: invalid mime_type");
1998 m
->mt_type
= g_strdup(mime_type
);
1999 m
->mt_action
= g_strdup(l
);
2000 m
->mt_download
= downloadfirst
;
2002 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
2003 m
->mt_type
, m
->mt_action
, m
->mt_default
);
2005 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
2015 find_mime_type(char *mime_type
)
2017 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
2019 TAILQ_FOREACH(m
, &mtl
, entry
) {
2020 if (m
->mt_default
&&
2021 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
2024 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
2037 walk_mime_type(struct settings
*s
,
2038 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2040 struct mime_type
*m
;
2043 if (s
== NULL
|| cb
== NULL
) {
2044 show_oops(NULL
, "walk_mime_type invalid parameters");
2048 TAILQ_FOREACH(m
, &mtl
, entry
) {
2049 str
= g_strdup_printf("%s%s --> %s",
2051 m
->mt_default
? "*" : "",
2053 cb(s
, str
, cb_args
);
2059 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2065 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2068 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2070 /* treat *.moo.com the same as .moo.com */
2071 if (str
[0] == '*' && str
[1] == '.')
2073 else if (str
[0] == '.')
2078 /* slice off port number */
2079 p
= g_strrstr(str
, ":");
2083 d
= g_malloc(sizeof *d
);
2085 d
->d
= g_strdup_printf(".%s", str
);
2087 d
->d
= g_strdup(str
);
2090 if (RB_INSERT(domain_list
, wl
, d
))
2093 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2104 add_cookie_wl(struct settings
*s
, char *entry
)
2106 wl_add(entry
, &c_wl
, 1);
2111 walk_cookie_wl(struct settings
*s
,
2112 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2116 if (s
== NULL
|| cb
== NULL
) {
2117 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2121 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2122 cb(s
, d
->d
, cb_args
);
2126 walk_js_wl(struct settings
*s
,
2127 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2131 if (s
== NULL
|| cb
== NULL
) {
2132 show_oops(NULL
, "walk_js_wl invalid parameters");
2136 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2137 cb(s
, d
->d
, cb_args
);
2141 add_js_wl(struct settings
*s
, char *entry
)
2143 wl_add(entry
, &js_wl
, 1 /* persistent */);
2148 walk_pl_wl(struct settings
*s
,
2149 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2153 if (s
== NULL
|| cb
== NULL
) {
2154 show_oops(NULL
, "walk_pl_wl invalid parameters");
2158 RB_FOREACH_REVERSE(d
, domain_list
, &pl_wl
)
2159 cb(s
, d
->d
, cb_args
);
2163 add_pl_wl(struct settings
*s
, char *entry
)
2165 wl_add(entry
, &pl_wl
, 1 /* persistent */);
2170 wl_find(const gchar
*search
, struct domain_list
*wl
)
2173 struct domain
*d
= NULL
, dfind
;
2176 if (search
== NULL
|| wl
== NULL
)
2178 if (strlen(search
) < 2)
2181 if (search
[0] != '.')
2182 s
= g_strdup_printf(".%s", search
);
2184 s
= g_strdup(search
);
2186 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2189 d
= RB_FIND(domain_list
, wl
, &dfind
);
2203 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2209 if (s
== NULL
|| wl
== NULL
)
2212 if (!strncmp(s
, "http://", strlen("http://")))
2213 s
= &s
[strlen("http://")];
2214 else if (!strncmp(s
, "https://", strlen("https://")))
2215 s
= &s
[strlen("https://")];
2220 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2221 /* chop string at first slash */
2222 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2225 r
= wl_find(ss
, wl
);
2234 settings_add(char *var
, char *val
)
2241 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2242 if (strcmp(var
, rs
[i
].name
))
2246 if (rs
[i
].s
->set(&rs
[i
], val
))
2247 errx(1, "invalid value for %s: %s", var
, val
);
2251 switch (rs
[i
].type
) {
2260 errx(1, "invalid sval for %s",
2274 errx(1, "invalid type for %s", var
);
2283 config_parse(char *filename
, int runtime
)
2286 char *line
, *cp
, *var
, *val
;
2287 size_t len
, lineno
= 0;
2289 char file
[PATH_MAX
];
2292 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2294 if (filename
== NULL
)
2297 if (runtime
&& runtime_settings
[0] != '\0') {
2298 snprintf(file
, sizeof file
, "%s/%s",
2299 work_dir
, runtime_settings
);
2300 if (stat(file
, &sb
)) {
2301 warnx("runtime file doesn't exist, creating it");
2302 if ((f
= fopen(file
, "w")) == NULL
)
2304 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2308 strlcpy(file
, filename
, sizeof file
);
2310 if ((config
= fopen(file
, "r")) == NULL
) {
2311 warn("config_parse: cannot open %s", filename
);
2316 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2317 if (feof(config
) || ferror(config
))
2321 cp
+= (long)strspn(cp
, WS
);
2322 if (cp
[0] == '\0') {
2328 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2329 startpage_add("invalid configuration file entry: %s",
2332 cp
+= (long)strspn(cp
, WS
);
2334 if ((val
= strsep(&cp
, "\0")) == NULL
)
2337 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",
2339 handled
= settings_add(var
, val
);
2342 startpage_add("invalid configuration file entry"
2343 ": %s=%s", var
, val
);
2353 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2359 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2363 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2366 JSStringGetUTF8CString(jsref
, s
, l
);
2367 JSStringRelease(jsref
);
2373 disable_hints(struct tab
*t
)
2375 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2376 bzero(t
->hint_num
, sizeof t
->hint_num
);
2377 run_script(t
, "vimprobable_clear()");
2379 t
->hint_mode
= XT_HINT_NONE
;
2383 enable_hints(struct tab
*t
)
2385 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2386 run_script(t
, "vimprobable_show_hints()");
2388 t
->hint_mode
= XT_HINT_NONE
;
2391 #define XT_JS_OPEN ("open;")
2392 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2393 #define XT_JS_FIRE ("fire;")
2394 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2395 #define XT_JS_FOUND ("found;")
2396 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2399 run_script(struct tab
*t
, char *s
)
2401 JSGlobalContextRef ctx
;
2402 WebKitWebFrame
*frame
;
2404 JSValueRef val
, exception
;
2407 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2408 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2410 frame
= webkit_web_view_get_main_frame(t
->wv
);
2411 ctx
= webkit_web_frame_get_global_context(frame
);
2413 str
= JSStringCreateWithUTF8CString(s
);
2414 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2415 NULL
, 0, &exception
);
2416 JSStringRelease(str
);
2418 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2420 es
= js_ref_to_string(ctx
, exception
);
2421 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2425 es
= js_ref_to_string(ctx
, val
);
2426 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2428 /* handle return value right here */
2429 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2432 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2435 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2436 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2437 &es
[XT_JS_FIRE_LEN
]);
2442 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2443 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2454 hint(struct tab
*t
, struct karg
*args
)
2457 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2459 if (t
->hints_on
== 0)
2468 apply_style(struct tab
*t
)
2470 g_object_set(G_OBJECT(t
->settings
),
2471 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2475 userstyle(struct tab
*t
, struct karg
*args
)
2477 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2481 g_object_set(G_OBJECT(t
->settings
),
2482 "user-stylesheet-uri", NULL
, (char *)NULL
);
2491 * Doesn't work fully, due to the following bug:
2492 * https://bugs.webkit.org/show_bug.cgi?id=51747
2495 restore_global_history(void)
2497 char file
[PATH_MAX
];
2502 const char delim
[3] = {'\\', '\\', '\0'};
2504 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2506 if ((f
= fopen(file
, "r")) == NULL
) {
2507 warnx("%s: fopen", __func__
);
2512 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2513 if (feof(f
) || ferror(f
))
2516 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2517 if (feof(f
) || ferror(f
)) {
2519 warnx("%s: broken history file\n", __func__
);
2523 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2524 webkit_web_history_item_new_with_data(uri
, title
);
2525 h
= g_malloc(sizeof(struct history
));
2526 h
->uri
= g_strdup(uri
);
2527 h
->title
= g_strdup(title
);
2528 RB_INSERT(history_list
, &hl
, h
);
2529 completion_add_uri(h
->uri
);
2531 warnx("%s: failed to restore history\n", __func__
);
2547 save_global_history_to_disk(struct tab
*t
)
2549 char file
[PATH_MAX
];
2553 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2555 if ((f
= fopen(file
, "w")) == NULL
) {
2556 show_oops(t
, "%s: global history file: %s",
2557 __func__
, strerror(errno
));
2561 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2562 if (h
->uri
&& h
->title
)
2563 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2572 quit(struct tab
*t
, struct karg
*args
)
2574 if (save_global_history
)
2575 save_global_history_to_disk(t
);
2583 restore_sessions_list(void)
2586 struct dirent
*dp
= NULL
;
2589 sdir
= opendir(sessions_dir
);
2591 while ((dp
= readdir(sdir
)) != NULL
)
2592 if (dp
->d_type
== DT_REG
) {
2593 s
= g_malloc(sizeof(struct session
));
2594 s
->name
= g_strdup(dp
->d_name
);
2595 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2602 open_tabs(struct tab
*t
, struct karg
*a
)
2604 char file
[PATH_MAX
];
2608 struct tab
*ti
, *tt
;
2613 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2614 if ((f
= fopen(file
, "r")) == NULL
)
2617 ti
= TAILQ_LAST(&tabs
, tab_list
);
2620 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2621 if (feof(f
) || ferror(f
))
2624 /* retrieve session name */
2625 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2626 strlcpy(named_session
,
2627 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2628 sizeof named_session
);
2632 if (uri
&& strlen(uri
))
2633 create_new_tab(uri
, NULL
, 1, -1);
2639 /* close open tabs */
2640 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2642 tt
= TAILQ_FIRST(&tabs
);
2662 restore_saved_tabs(void)
2664 char file
[PATH_MAX
];
2665 int unlink_file
= 0;
2670 snprintf(file
, sizeof file
, "%s/%s",
2671 sessions_dir
, XT_RESTART_TABS_FILE
);
2672 if (stat(file
, &sb
) == -1)
2673 a
.s
= XT_SAVED_TABS_FILE
;
2676 a
.s
= XT_RESTART_TABS_FILE
;
2679 a
.i
= XT_SES_DONOTHING
;
2680 rv
= open_tabs(NULL
, &a
);
2689 save_tabs(struct tab
*t
, struct karg
*a
)
2691 char file
[PATH_MAX
];
2693 int num_tabs
= 0, i
;
2694 struct tab
**stabs
= NULL
;
2699 snprintf(file
, sizeof file
, "%s/%s",
2700 sessions_dir
, named_session
);
2702 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2704 if ((f
= fopen(file
, "w")) == NULL
) {
2705 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2709 /* save session name */
2710 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2712 /* Save tabs, in the order they are arranged in the notebook. */
2713 num_tabs
= sort_tabs_by_page_num(&stabs
);
2715 for (i
= 0; i
< num_tabs
; i
++)
2717 if (get_uri(stabs
[i
]) != NULL
)
2718 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2719 else if (gtk_entry_get_text(GTK_ENTRY(
2720 stabs
[i
]->uri_entry
)))
2721 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2722 stabs
[i
]->uri_entry
)));
2727 /* try and make sure this gets to disk NOW. XXX Backup first? */
2728 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2729 show_oops(t
, "May not have managed to save session: %s",
2739 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2751 run_page_script(struct tab
*t
, struct karg
*args
)
2754 char *tmp
, script
[PATH_MAX
];
2756 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2757 if (tmp
[0] == '\0') {
2758 show_oops(t
, "no script specified");
2762 if ((uri
= get_uri(t
)) == NULL
) {
2763 show_oops(t
, "tab is empty, not running script");
2768 snprintf(script
, sizeof script
, "%s/%s",
2769 pwd
->pw_dir
, &tmp
[1]);
2771 strlcpy(script
, tmp
, sizeof script
);
2775 show_oops(t
, "can't fork to run script");
2785 execlp(script
, script
, uri
, (void *)NULL
);
2795 yank_uri(struct tab
*t
, struct karg
*args
)
2798 GtkClipboard
*clipboard
;
2800 if ((uri
= get_uri(t
)) == NULL
)
2803 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2804 gtk_clipboard_set_text(clipboard
, uri
, -1);
2810 paste_uri(struct tab
*t
, struct karg
*args
)
2812 GtkClipboard
*clipboard
;
2813 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2815 gchar
*p
= NULL
, *uri
;
2817 /* try primary clipboard first */
2818 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2819 p
= gtk_clipboard_wait_for_text(clipboard
);
2821 /* if it failed get whatever text is in cut_buffer0 */
2822 if (p
== NULL
&& xterm_workaround
)
2823 if (gdk_property_get(gdk_get_default_root_window(),
2825 gdk_atom_intern("STRING", FALSE
),
2827 1024 * 1024 /* picked out of my butt */,
2833 /* yes sir, we need to NUL the string */
2839 while (*uri
&& isspace(*uri
))
2841 if (strlen(uri
) == 0) {
2842 show_oops(t
, "empty paste buffer");
2845 if (guess_search
== 0 && valid_url_type(uri
)) {
2846 /* we can be clever and paste this in search box */
2847 show_oops(t
, "not a valid URL");
2851 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2853 else if (args
->i
== XT_PASTE_NEW_TAB
)
2854 create_new_tab(uri
, NULL
, 1, -1);
2865 find_domain(const gchar
*s
, int toplevel
)
2873 uri
= soup_uri_new(s
);
2875 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2879 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2880 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2881 while(--p
>= uri
->host
&& *p
!= '.');
2888 ret
= g_strdup_printf(".%s", p
);
2896 toggle_cwl(struct tab
*t
, struct karg
*args
)
2907 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2909 if (uri
== NULL
|| dom
== NULL
||
2910 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2911 show_oops(t
, "Can't toggle domain in cookie white list");
2914 d
= wl_find(dom
, &c_wl
);
2921 if (args
->i
& XT_WL_TOGGLE
)
2923 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2925 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2929 /* enable cookies for domain */
2930 wl_add(dom
, &c_wl
, 0);
2932 /* disable cookies for domain */
2933 RB_REMOVE(domain_list
, &c_wl
, d
);
2935 if (args
->i
& XT_WL_RELOAD
)
2936 webkit_web_view_reload(t
->wv
);
2944 toggle_js(struct tab
*t
, struct karg
*args
)
2954 g_object_get(G_OBJECT(t
->settings
),
2955 "enable-scripts", &es
, (char *)NULL
);
2956 if (args
->i
& XT_WL_TOGGLE
)
2958 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2960 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2966 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2968 if (uri
== NULL
|| dom
== NULL
||
2969 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2970 show_oops(t
, "Can't toggle domain in JavaScript white list");
2975 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2976 wl_add(dom
, &js_wl
, 0 /* session */);
2978 d
= wl_find(dom
, &js_wl
);
2980 RB_REMOVE(domain_list
, &js_wl
, d
);
2981 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2983 g_object_set(G_OBJECT(t
->settings
),
2984 "enable-scripts", es
, (char *)NULL
);
2985 g_object_set(G_OBJECT(t
->settings
),
2986 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2987 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2989 if (args
->i
& XT_WL_RELOAD
)
2990 webkit_web_view_reload(t
->wv
);
2998 toggle_pl(struct tab
*t
, struct karg
*args
)
3008 g_object_get(G_OBJECT(t
->settings
),
3009 "enable-plugins", &es
, (char *)NULL
);
3010 if (args
->i
& XT_WL_TOGGLE
)
3012 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
3014 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
3020 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3022 if (uri
== NULL
|| dom
== NULL
||
3023 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3024 show_oops(t
, "Can't toggle domain in plugins white list");
3029 wl_add(dom
, &pl_wl
, 0 /* session */);
3031 d
= wl_find(dom
, &pl_wl
);
3033 RB_REMOVE(domain_list
, &pl_wl
, d
);
3035 g_object_set(G_OBJECT(t
->settings
),
3036 "enable-plugins", es
, (char *)NULL
);
3037 webkit_web_view_set_settings(t
->wv
, t
->settings
);
3039 if (args
->i
& XT_WL_RELOAD
)
3040 webkit_web_view_reload(t
->wv
);
3048 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
3053 g_object_get(G_OBJECT(t
->settings
),
3054 "enable-scripts", &es
, (char *)NULL
);
3059 set
= XT_WL_DISABLE
;
3061 a
.i
= set
| XT_WL_TOPLEVEL
;
3064 a
.i
= set
| XT_WL_TOPLEVEL
;
3067 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
3072 toggle_src(struct tab
*t
, struct karg
*args
)
3079 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
3080 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
3081 webkit_web_view_reload(t
->wv
);
3087 focus_webview(struct tab
*t
)
3092 /* only grab focus if we are visible */
3093 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
3094 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
3098 focus(struct tab
*t
, struct karg
*args
)
3100 if (t
== NULL
|| args
== NULL
)
3106 if (args
->i
== XT_FOCUS_URI
)
3107 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
3108 else if (args
->i
== XT_FOCUS_SEARCH
)
3109 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
3115 stats(struct tab
*t
, struct karg
*args
)
3117 char *page
, *body
, *s
, line
[64 * 1024];
3118 long long unsigned int line_count
= 0;
3122 show_oops(NULL
, "stats invalid parameters");
3125 if (save_rejected_cookies
) {
3126 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
3128 s
= fgets(line
, sizeof line
, r_cookie_f
);
3129 if (s
== NULL
|| feof(r_cookie_f
) ||
3135 snprintf(line
, sizeof line
,
3136 "<br/>Cookies blocked(*) total: %llu", line_count
);
3138 show_oops(t
, "Can't open blocked cookies file: %s",
3142 body
= g_strdup_printf(
3143 "Cookies blocked(*) this session: %llu"
3145 "<p><small><b>*</b> results vary based on settings</small></p>",
3149 page
= get_html_page("Statistics", body
, "", 0);
3152 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3159 marco(struct tab
*t
, struct karg
*args
)
3161 char *page
, line
[64 * 1024];
3165 show_oops(NULL
, "marco invalid parameters");
3168 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3170 page
= get_html_page("Marco Sez...", line
, "", 0);
3172 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3179 blank(struct tab
*t
, struct karg
*args
)
3182 show_oops(NULL
, "blank invalid parameters");
3184 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3190 about(struct tab
*t
, struct karg
*args
)
3195 show_oops(NULL
, "about invalid parameters");
3197 body
= g_strdup_printf("<b>Version: %s</b>"
3198 #ifdef XXXTERM_BUILDSTR
3199 "<br><b>Build: %s</b>"
3204 "<li>Marco Peereboom <marco@peereboom.us></li>"
3205 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3206 "<li>Edd Barrett <vext01@gmail.com> </li>"
3207 "<li>Todd T. Fries <todd@fries.net> </li>"
3208 "<li>Raphael Graf <r@undefined.ch> </li>"
3210 "Copyrights and licenses can be found on the XXXTerm "
3211 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3213 #ifdef XXXTERM_BUILDSTR
3214 version
, XXXTERM_BUILDSTR
3220 page
= get_html_page("About", body
, "", 0);
3223 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3230 help(struct tab
*t
, struct karg
*args
)
3232 char *page
, *head
, *body
;
3235 show_oops(NULL
, "help invalid parameters");
3237 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3238 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3240 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3241 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3242 "cgi-bin/man-cgi?xxxterm</a>";
3244 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3246 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3253 startpage(struct tab
*t
, struct karg
*args
)
3255 char *page
, *body
, *b
;
3259 show_oops(NULL
, "startpage invalid parameters");
3261 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3263 TAILQ_FOREACH(s
, &spl
, entry
) {
3265 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3269 page
= get_html_page("Startup Exception", body
, "", 0);
3272 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3279 startpage_add(const char *fmt
, ...)
3289 if (vasprintf(&msg
, fmt
, ap
) == -1)
3290 errx(1, "startpage_add failed");
3293 s
= g_malloc0(sizeof *s
);
3296 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3300 * update all favorite tabs apart from one. Pass NULL if
3301 * you want to update all.
3304 update_favorite_tabs(struct tab
*apart_from
)
3307 if (!updating_fl_tabs
) {
3308 updating_fl_tabs
= 1; /* stop infinite recursion */
3309 TAILQ_FOREACH(t
, &tabs
, entry
)
3310 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3311 && (t
!= apart_from
))
3312 xtp_page_fl(t
, NULL
);
3313 updating_fl_tabs
= 0;
3317 /* show a list of favorites (bookmarks) */
3319 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3321 char file
[PATH_MAX
];
3323 char *uri
= NULL
, *title
= NULL
;
3324 size_t len
, lineno
= 0;
3326 char *body
, *tmp
, *page
= NULL
;
3327 const char delim
[3] = {'\\', '\\', '\0'};
3329 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3332 warn("%s: bad param", __func__
);
3334 /* new session key */
3335 if (!updating_fl_tabs
)
3336 generate_xtp_session_key(&fl_session_key
);
3338 /* open favorites */
3339 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3340 if ((f
= fopen(file
, "r")) == NULL
) {
3341 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3346 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3347 "<th style='width: 40px'>#</th><th>Link</th>"
3348 "<th style='width: 40px'>Rm</th></tr>\n");
3351 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3353 if (strlen(title
) == 0 || title
[0] == '#') {
3359 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3360 if (feof(f
) || ferror(f
)) {
3361 show_oops(t
, "favorites file corrupt");
3367 body
= g_strdup_printf("%s<tr>"
3369 "<td><a href='%s'>%s</a></td>"
3370 "<td style='text-align: center'>"
3371 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3373 body
, i
, uri
, title
,
3374 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3386 /* if none, say so */
3389 body
= g_strdup_printf("%s<tr>"
3390 "<td colspan='3' style='text-align: center'>"
3391 "No favorites - To add one use the 'favadd' command."
3392 "</td></tr>", body
);
3397 body
= g_strdup_printf("%s</table>", body
);
3407 page
= get_html_page("Favorites", body
, "", 1);
3408 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3412 update_favorite_tabs(t
);
3421 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3422 size_t cert_count
, char *title
)
3424 gnutls_datum_t cinfo
;
3428 body
= g_strdup("");
3430 for (i
= 0; i
< cert_count
; i
++) {
3431 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3436 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3437 body
, i
, cinfo
.data
);
3438 gnutls_free(cinfo
.data
);
3442 tmp
= get_html_page(title
, body
, "", 0);
3445 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3450 ca_cmd(struct tab
*t
, struct karg
*args
)
3453 int rv
= 1, certs
= 0, certs_read
;
3456 gnutls_x509_crt_t
*c
= NULL
;
3457 char *certs_buf
= NULL
, *s
;
3459 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3460 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3464 if (fstat(fileno(f
), &sb
) == -1) {
3465 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3469 certs_buf
= g_malloc(sb
.st_size
+ 1);
3470 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3471 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3474 certs_buf
[sb
.st_size
] = '\0';
3477 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3479 s
+= strlen("BEGIN CERTIFICATE");
3482 bzero(&dt
, sizeof dt
);
3483 dt
.data
= (unsigned char *)certs_buf
;
3484 dt
.size
= sb
.st_size
;
3485 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3486 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3487 GNUTLS_X509_FMT_PEM
, 0);
3488 if (certs_read
<= 0) {
3489 show_oops(t
, "No cert(s) available");
3492 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3505 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
3509 struct addrinfo hints
, *res
= NULL
, *ai
;
3510 int rv
= -1, s
= -1, on
, error
;
3512 static gchar myerror
[256]; /* this is not thread safe */
3515 *error_str
= myerror
;
3516 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3517 *error_str
= "invalid URI";
3521 su
= soup_uri_new(uri
);
3523 *error_str
= "invalid soup URI";
3526 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3527 *error_str
= "invalid HTTPS URI";
3531 snprintf(port
, sizeof port
, "%d", su
->port
);
3532 bzero(&hints
, sizeof(struct addrinfo
));
3533 hints
.ai_flags
= AI_CANONNAME
;
3534 hints
.ai_family
= AF_UNSPEC
;
3535 hints
.ai_socktype
= SOCK_STREAM
;
3537 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3538 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
3539 gai_strerror(errno
));
3543 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3549 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3551 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3554 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3557 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3561 snprintf(myerror
, sizeof myerror
,
3562 "could not obtain certificates from: %s",
3568 strlcpy(domain
, su
->host
, domain_sz
);
3575 if (rv
== -1 && s
!= -1)
3582 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3585 gnutls_deinit(gsession
);
3587 gnutls_certificate_free_credentials(xcred
);
3593 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
3594 gnutls_certificate_credentials_t
*xc
)
3596 gnutls_certificate_credentials_t xcred
;
3597 gnutls_session_t gsession
;
3599 static gchar myerror
[1024]; /* this is not thread safe */
3601 if (gs
== NULL
|| xc
== NULL
)
3608 gnutls_certificate_allocate_credentials(&xcred
);
3609 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3610 GNUTLS_X509_FMT_PEM
);
3612 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3613 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3614 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3615 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3616 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3617 snprintf(myerror
, sizeof myerror
,
3618 "gnutls_handshake failed %d fatal %d %s",
3620 gnutls_error_is_fatal(rv
),
3621 gnutls_strerror_name(rv
));
3622 stop_tls(gsession
, xcred
);
3626 gnutls_credentials_type_t cred
;
3627 cred
= gnutls_auth_get_type(gsession
);
3628 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3629 snprintf(myerror
, sizeof myerror
,
3630 "gnutls_auth_get_type failed %d",
3632 stop_tls(gsession
, xcred
);
3640 *error_str
= myerror
;
3645 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3649 const gnutls_datum_t
*cl
;
3650 gnutls_x509_crt_t
*all_certs
;
3653 if (certs
== NULL
|| cert_count
== NULL
)
3655 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3657 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3661 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3662 for (i
= 0; i
< len
; i
++) {
3663 gnutls_x509_crt_init(&all_certs
[i
]);
3664 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3665 GNUTLS_X509_FMT_PEM
< 0)) {
3679 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3683 for (i
= 0; i
< cert_count
; i
++)
3684 gnutls_x509_crt_deinit(certs
[i
]);
3689 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3691 GdkColor c_text
, c_base
;
3693 gdk_color_parse(text
, &c_text
);
3694 gdk_color_parse(base
, &c_base
);
3696 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3697 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3698 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3699 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3701 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3702 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3703 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3704 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3708 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3709 size_t cert_count
, char *domain
)
3712 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3717 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3720 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3721 if ((f
= fopen(file
, "w")) == NULL
) {
3722 show_oops(t
, "Can't create cert file %s %s",
3723 file
, strerror(errno
));
3727 for (i
= 0; i
< cert_count
; i
++) {
3728 cert_buf_sz
= sizeof cert_buf
;
3729 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3730 cert_buf
, &cert_buf_sz
)) {
3731 show_oops(t
, "gnutls_x509_crt_export failed");
3734 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3735 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3740 /* not the best spot but oh well */
3741 gdk_color_parse("lightblue", &color
);
3742 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3743 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3756 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
3758 char domain
[8182], file
[PATH_MAX
];
3759 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3761 unsigned int error
= 0;
3763 size_t cert_buf_sz
, cert_count
;
3764 enum cert_trust rv
= CERT_UNTRUSTED
;
3765 static gchar serr
[80]; /* this isn't thread safe */
3766 gnutls_session_t gsession
;
3767 gnutls_x509_crt_t
*certs
;
3768 gnutls_certificate_credentials_t xcred
;
3770 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3774 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
3775 sizeof domain
)) == -1)
3778 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3781 if (start_tls(error_str
, s
, &gsession
, &xcred
))
3783 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3785 /* verify certs in case cert file doesn't exist */
3786 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3788 *error_str
= "Invalid certificates";
3793 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3794 *error_str
= "Can't get connection certificates";
3798 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3799 if ((f
= fopen(file
, "r")) == NULL
) {
3805 for (i
= 0; i
< cert_count
; i
++) {
3806 cert_buf_sz
= sizeof cert_buf
;
3807 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3808 cert_buf
, &cert_buf_sz
)) {
3811 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3812 rv
= CERT_BAD
; /* critical */
3815 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3816 rv
= CERT_BAD
; /* critical */
3825 free_connection_certs(certs
, cert_count
);
3827 /* we close the socket first for speed */
3831 /* only complain if we didn't save it locally */
3832 if (error
&& rv
!= CERT_LOCAL
) {
3833 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3834 if (error
& GNUTLS_CERT_INVALID
)
3835 strlcat(serr
, "invalid, ", sizeof serr
);
3836 if (error
& GNUTLS_CERT_REVOKED
)
3837 strlcat(serr
, "revoked, ", sizeof serr
);
3838 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3839 strlcat(serr
, "signer not found, ", sizeof serr
);
3840 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3841 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3842 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3843 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3844 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3845 strlcat(serr
, "not activated, ", sizeof serr
);
3846 if (error
& GNUTLS_CERT_EXPIRED
)
3847 strlcat(serr
, "expired, ", sizeof serr
);
3848 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3849 if (serr
[i
] == ',') {
3856 stop_tls(gsession
, xcred
);
3862 cert_cmd(struct tab
*t
, struct karg
*args
)
3864 const gchar
*uri
, *error_str
= NULL
;
3868 gnutls_session_t gsession
;
3869 gnutls_x509_crt_t
*certs
;
3870 gnutls_certificate_credentials_t xcred
;
3875 if (ssl_ca_file
== NULL
) {
3876 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3880 if ((uri
= get_uri(t
)) == NULL
) {
3881 show_oops(t
, "Invalid URI");
3885 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
3886 sizeof domain
)) == -1) {
3887 show_oops(t
, "%s", error_str
);
3892 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
3896 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3897 show_oops(t
, "get_connection_certs failed");
3901 if (args
->i
& XT_SHOW
)
3902 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3903 else if (args
->i
& XT_SAVE
)
3904 save_certs(t
, certs
, cert_count
, domain
);
3906 free_connection_certs(certs
, cert_count
);
3908 /* we close the socket first for speed */
3911 stop_tls(gsession
, xcred
);
3912 if (error_str
&& strlen(error_str
))
3913 show_oops(t
, "%s", error_str
);
3918 remove_cookie(int index
)
3924 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3926 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3928 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3932 print_cookie("remove cookie", c
);
3933 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3938 soup_cookies_free(cf
);
3944 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3949 body
= g_strdup("");
3952 if (args
->i
& XT_WL_PERSISTENT
) {
3954 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3956 RB_FOREACH(d
, domain_list
, wl
) {
3960 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3966 if (args
->i
& XT_WL_SESSION
) {
3968 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3970 RB_FOREACH(d
, domain_list
, wl
) {
3974 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3979 tmp
= get_html_page(title
, body
, "", 0);
3982 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3983 else if (wl
== &c_wl
)
3984 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3986 load_webkit_string(t
, tmp
, XT_URI_ABOUT_PLUGINWL
);
3991 #define XT_WL_INVALID (0)
3992 #define XT_WL_JAVASCRIPT (1)
3993 #define XT_WL_COOKIE (2)
3994 #define XT_WL_PLUGIN (3)
3996 wl_save(struct tab
*t
, struct karg
*args
, int list
)
3998 char file
[PATH_MAX
], *lst_str
= NULL
;
4000 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
4008 if (t
== NULL
|| args
== NULL
)
4011 if (runtime_settings
[0] == '\0')
4014 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
4015 if ((f
= fopen(file
, "r+")) == NULL
)
4019 case XT_WL_JAVASCRIPT
:
4020 lst_str
= "JavaScript";
4021 lt
= g_strdup_printf("js_wl=%s", dom
);
4025 lt
= g_strdup_printf("cookie_wl=%s", dom
);
4029 lt
= g_strdup_printf("pl_wl=%s", dom
);
4032 show_oops(t
, "Invalid list id: %d", list
);
4037 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
4038 if (uri
== NULL
|| dom
== NULL
||
4039 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
4040 show_oops(t
, "Can't add domain to %s white list", lst_str
);
4045 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
4048 if (!strcmp(line
, lt
))
4054 fprintf(f
, "%s\n", lt
);
4059 case XT_WL_JAVASCRIPT
:
4060 d
= wl_find(dom
, &js_wl
);
4062 settings_add("js_wl", dom
);
4063 d
= wl_find(dom
, &js_wl
);
4069 d
= wl_find(dom
, &c_wl
);
4071 settings_add("cookie_wl", dom
);
4072 d
= wl_find(dom
, &c_wl
);
4076 /* find and add to persistent jar */
4077 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
4078 for (;cf
; cf
= cf
->next
) {
4080 if (!strcmp(dom
, ci
->domain
) ||
4081 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
4082 c
= soup_cookie_copy(ci
);
4083 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
4086 soup_cookies_free(cf
);
4090 d
= wl_find(dom
, &pl_wl
);
4092 settings_add("pl_wl", dom
);
4093 d
= wl_find(dom
, &pl_wl
);
4098 abort(); /* can't happen */
4116 js_show_wl(struct tab
*t
, struct karg
*args
)
4118 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
4119 wl_show(t
, args
, "JavaScript White List", &js_wl
);
4125 cookie_show_wl(struct tab
*t
, struct karg
*args
)
4127 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
4128 wl_show(t
, args
, "Cookie White List", &c_wl
);
4134 cookie_cmd(struct tab
*t
, struct karg
*args
)
4136 if (args
->i
& XT_SHOW
)
4137 wl_show(t
, args
, "Cookie White List", &c_wl
);
4138 else if (args
->i
& XT_WL_TOGGLE
) {
4139 args
->i
|= XT_WL_RELOAD
;
4140 toggle_cwl(t
, args
);
4141 } else if (args
->i
& XT_SAVE
) {
4142 args
->i
|= XT_WL_RELOAD
;
4143 wl_save(t
, args
, XT_WL_COOKIE
);
4144 } else if (args
->i
& XT_DELETE
)
4145 show_oops(t
, "'cookie delete' currently unimplemented");
4151 js_cmd(struct tab
*t
, struct karg
*args
)
4153 if (args
->i
& XT_SHOW
)
4154 wl_show(t
, args
, "JavaScript White List", &js_wl
);
4155 else if (args
->i
& XT_SAVE
) {
4156 args
->i
|= XT_WL_RELOAD
;
4157 wl_save(t
, args
, XT_WL_JAVASCRIPT
);
4158 } else if (args
->i
& XT_WL_TOGGLE
) {
4159 args
->i
|= XT_WL_RELOAD
;
4161 } else if (args
->i
& XT_DELETE
)
4162 show_oops(t
, "'js delete' currently unimplemented");
4168 pl_show_wl(struct tab
*t
, struct karg
*args
)
4170 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
4171 wl_show(t
, args
, "Plugin White List", &pl_wl
);
4177 pl_cmd(struct tab
*t
, struct karg
*args
)
4179 if (args
->i
& XT_SHOW
)
4180 wl_show(t
, args
, "Plugin White List", &pl_wl
);
4181 else if (args
->i
& XT_SAVE
) {
4182 args
->i
|= XT_WL_RELOAD
;
4183 wl_save(t
, args
, XT_WL_PLUGIN
);
4184 } else if (args
->i
& XT_WL_TOGGLE
) {
4185 args
->i
|= XT_WL_RELOAD
;
4187 } else if (args
->i
& XT_DELETE
)
4188 show_oops(t
, "'plugin delete' currently unimplemented");
4194 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4196 js_toggle_cb(t
->js_toggle
, t
);
4202 add_favorite(struct tab
*t
, struct karg
*args
)
4204 char file
[PATH_MAX
];
4207 size_t urilen
, linelen
;
4208 const gchar
*uri
, *title
;
4213 /* don't allow adding of xtp pages to favorites */
4214 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4215 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4219 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4220 if ((f
= fopen(file
, "r+")) == NULL
) {
4221 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4225 title
= get_title(t
, FALSE
);
4228 if (title
== NULL
|| uri
== NULL
) {
4229 show_oops(t
, "can't add page to favorites");
4233 urilen
= strlen(uri
);
4236 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4237 if (feof(f
) || ferror(f
))
4240 if (linelen
== urilen
&& !strcmp(line
, uri
))
4247 fprintf(f
, "\n%s\n%s", title
, uri
);
4253 update_favorite_tabs(NULL
);
4259 can_go_back_for_real(struct tab
*t
)
4262 WebKitWebHistoryItem
*item
;
4268 /* rely on webkit to make sure we can go backward when on an about page */
4270 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4271 return (webkit_web_view_can_go_back(t
->wv
));
4273 /* the back/forwars list is stupid so help determine if we can go back */
4274 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4276 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4277 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4285 can_go_forward_for_real(struct tab
*t
)
4288 WebKitWebHistoryItem
*item
;
4294 /* rely on webkit to make sure we can go forward when on an about page */
4296 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4297 return (webkit_web_view_can_go_forward(t
->wv
));
4299 /* the back/forwars list is stupid so help selecting a different item */
4300 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4302 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4303 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4311 go_back_for_real(struct tab
*t
)
4314 WebKitWebHistoryItem
*item
;
4322 webkit_web_view_go_back(t
->wv
);
4325 /* the back/forwars list is stupid so help selecting a different item */
4326 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4328 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4329 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4330 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4337 go_forward_for_real(struct tab
*t
)
4340 WebKitWebHistoryItem
*item
;
4348 webkit_web_view_go_forward(t
->wv
);
4351 /* the back/forwars list is stupid so help selecting a different item */
4352 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4354 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4355 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4356 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4363 navaction(struct tab
*t
, struct karg
*args
)
4365 WebKitWebHistoryItem
*item
;
4366 WebKitWebFrame
*frame
;
4368 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4369 t
->tab_id
, args
->i
);
4371 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4373 if (args
->i
== XT_NAV_BACK
)
4374 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4376 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4378 return (XT_CB_PASSTHROUGH
);
4379 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4381 return (XT_CB_PASSTHROUGH
);
4387 go_back_for_real(t
);
4389 case XT_NAV_FORWARD
:
4391 go_forward_for_real(t
);
4394 frame
= webkit_web_view_get_main_frame(t
->wv
);
4395 webkit_web_frame_reload(frame
);
4398 return (XT_CB_PASSTHROUGH
);
4402 move(struct tab
*t
, struct karg
*args
)
4404 GtkAdjustment
*adjust
;
4405 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4411 case XT_MOVE_BOTTOM
:
4413 case XT_MOVE_PAGEDOWN
:
4414 case XT_MOVE_PAGEUP
:
4415 case XT_MOVE_HALFDOWN
:
4416 case XT_MOVE_HALFUP
:
4417 case XT_MOVE_PERCENT
:
4418 adjust
= t
->adjust_v
;
4421 adjust
= t
->adjust_h
;
4425 pos
= gtk_adjustment_get_value(adjust
);
4426 ps
= gtk_adjustment_get_page_size(adjust
);
4427 upper
= gtk_adjustment_get_upper(adjust
);
4428 lower
= gtk_adjustment_get_lower(adjust
);
4429 si
= gtk_adjustment_get_step_increment(adjust
);
4430 pi
= gtk_adjustment_get_page_increment(adjust
);
4433 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4434 "max %f si %f pi %f\n",
4435 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4436 pos
, ps
, upper
, lower
, max
, si
, pi
);
4442 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4447 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4449 case XT_MOVE_BOTTOM
:
4450 case XT_MOVE_FARRIGHT
:
4451 gtk_adjustment_set_value(adjust
, max
);
4454 case XT_MOVE_FARLEFT
:
4455 gtk_adjustment_set_value(adjust
, lower
);
4457 case XT_MOVE_PAGEDOWN
:
4459 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4461 case XT_MOVE_PAGEUP
:
4463 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4465 case XT_MOVE_HALFDOWN
:
4467 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4469 case XT_MOVE_HALFUP
:
4471 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4473 case XT_MOVE_PERCENT
:
4474 percent
= atoi(args
->s
) / 100.0;
4475 pos
= max
* percent
;
4476 if (pos
< 0.0 || pos
> max
)
4478 gtk_adjustment_set_value(adjust
, pos
);
4481 return (XT_CB_PASSTHROUGH
);
4484 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4486 return (XT_CB_HANDLED
);
4490 url_set_visibility(void)
4494 TAILQ_FOREACH(t
, &tabs
, entry
)
4495 if (show_url
== 0) {
4496 gtk_widget_hide(t
->toolbar
);
4499 gtk_widget_show(t
->toolbar
);
4503 notebook_tab_set_visibility(void)
4505 if (show_tabs
== 0) {
4506 gtk_widget_hide(tab_bar
);
4507 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4509 if (tab_style
== XT_TABS_NORMAL
) {
4510 gtk_widget_hide(tab_bar
);
4511 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4512 } else if (tab_style
== XT_TABS_COMPACT
) {
4513 gtk_widget_show(tab_bar
);
4514 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4520 statusbar_set_visibility(void)
4524 TAILQ_FOREACH(t
, &tabs
, entry
)
4525 if (show_statusbar
== 0) {
4526 gtk_widget_hide(t
->statusbar_box
);
4529 gtk_widget_show(t
->statusbar_box
);
4533 url_set(struct tab
*t
, int enable_url_entry
)
4538 show_url
= enable_url_entry
;
4540 if (enable_url_entry
) {
4541 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4542 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4543 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4545 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4546 GTK_ENTRY_ICON_PRIMARY
);
4548 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4549 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4550 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4551 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4557 fullscreen(struct tab
*t
, struct karg
*args
)
4559 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4562 return (XT_CB_PASSTHROUGH
);
4564 if (show_url
== 0) {
4572 url_set_visibility();
4573 notebook_tab_set_visibility();
4575 return (XT_CB_HANDLED
);
4579 statustoggle(struct tab
*t
, struct karg
*args
)
4581 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4583 if (show_statusbar
== 1) {
4585 statusbar_set_visibility();
4586 } else if (show_statusbar
== 0) {
4588 statusbar_set_visibility();
4590 return (XT_CB_HANDLED
);
4594 urlaction(struct tab
*t
, struct karg
*args
)
4596 int rv
= XT_CB_HANDLED
;
4598 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4601 return (XT_CB_PASSTHROUGH
);
4605 if (show_url
== 0) {
4607 url_set_visibility();
4611 if (show_url
== 1) {
4613 url_set_visibility();
4621 tabaction(struct tab
*t
, struct karg
*args
)
4623 int rv
= XT_CB_HANDLED
;
4624 char *url
= args
->s
;
4628 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4631 return (XT_CB_PASSTHROUGH
);
4635 if (strlen(url
) > 0)
4636 create_new_tab(url
, NULL
, 1, args
->precount
);
4638 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4641 if (args
->precount
< 0)
4644 TAILQ_FOREACH(tt
, &tabs
, entry
)
4645 if (tt
->tab_id
== args
->precount
- 1) {
4650 case XT_TAB_DELQUIT
:
4651 if (gtk_notebook_get_n_pages(notebook
) > 1)
4657 if (strlen(url
) > 0)
4660 rv
= XT_CB_PASSTHROUGH
;
4666 if (show_tabs
== 0) {
4668 notebook_tab_set_visibility();
4672 if (show_tabs
== 1) {
4674 notebook_tab_set_visibility();
4677 case XT_TAB_NEXTSTYLE
:
4678 if (tab_style
== XT_TABS_NORMAL
) {
4679 tab_style
= XT_TABS_COMPACT
;
4680 recolor_compact_tabs();
4683 tab_style
= XT_TABS_NORMAL
;
4684 notebook_tab_set_visibility();
4686 case XT_TAB_UNDO_CLOSE
:
4687 if (undo_count
== 0) {
4688 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4693 u
= TAILQ_FIRST(&undos
);
4694 create_new_tab(u
->uri
, u
, 1, -1);
4696 TAILQ_REMOVE(&undos
, u
, entry
);
4698 /* u->history is freed in create_new_tab() */
4703 rv
= XT_CB_PASSTHROUGH
;
4717 resizetab(struct tab
*t
, struct karg
*args
)
4719 if (t
== NULL
|| args
== NULL
) {
4720 show_oops(NULL
, "resizetab invalid parameters");
4721 return (XT_CB_PASSTHROUGH
);
4724 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4725 t
->tab_id
, args
->i
);
4727 setzoom_webkit(t
, args
->i
);
4729 return (XT_CB_HANDLED
);
4733 movetab(struct tab
*t
, struct karg
*args
)
4737 if (t
== NULL
|| args
== NULL
) {
4738 show_oops(NULL
, "movetab invalid parameters");
4739 return (XT_CB_PASSTHROUGH
);
4742 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4743 t
->tab_id
, args
->i
);
4745 if (args
->i
>= XT_TAB_INVALID
)
4746 return (XT_CB_PASSTHROUGH
);
4748 if (TAILQ_EMPTY(&tabs
))
4749 return (XT_CB_PASSTHROUGH
);
4751 n
= gtk_notebook_get_n_pages(notebook
);
4752 dest
= gtk_notebook_get_current_page(notebook
);
4756 if (args
->precount
< 0)
4757 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4759 dest
= args
->precount
- 1;
4763 if (args
->precount
< 0)
4766 dest
-= args
->precount
% n
;
4779 return (XT_CB_PASSTHROUGH
);
4782 if (dest
< 0 || dest
>= n
)
4783 return (XT_CB_PASSTHROUGH
);
4784 if (t
->tab_id
== dest
) {
4785 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4786 return (XT_CB_HANDLED
);
4789 set_current_tab(dest
);
4791 return (XT_CB_HANDLED
);
4797 command(struct tab
*t
, struct karg
*args
)
4799 char *s
= NULL
, *ss
= NULL
;
4803 if (t
== NULL
|| args
== NULL
) {
4804 show_oops(NULL
, "command invalid parameters");
4805 return (XT_CB_PASSTHROUGH
);
4816 if (cmd_prefix
== 0)
4819 ss
= g_strdup_printf(":%d", cmd_prefix
);
4830 case XT_CMD_OPEN_CURRENT
:
4833 case XT_CMD_TABNEW_CURRENT
:
4834 if (!s
) /* FALL THROUGH? */
4836 if ((uri
= get_uri(t
)) != NULL
) {
4837 ss
= g_strdup_printf("%s%s", s
, uri
);
4842 show_oops(t
, "command: invalid opcode %d", args
->i
);
4843 return (XT_CB_PASSTHROUGH
);
4846 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4848 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4849 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4850 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4852 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4853 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4858 return (XT_CB_HANDLED
);
4862 * Return a new string with a download row (in html)
4863 * appended. Old string is freed.
4866 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4869 WebKitDownloadStatus stat
;
4870 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4872 char cur_sz
[FMT_SCALED_STRSIZE
];
4873 char tot_sz
[FMT_SCALED_STRSIZE
];
4876 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4878 /* All actions wil take this form:
4879 * xxxt://class/seskey
4881 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4882 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4884 stat
= webkit_download_get_status(dl
->download
);
4887 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4888 status_html
= g_strdup_printf("Finished");
4889 cmd_html
= g_strdup_printf(
4890 "<a href='%s%d/%d'>Remove</a>",
4891 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4893 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4894 /* gather size info */
4895 progress
= 100 * webkit_download_get_progress(dl
->download
);
4898 webkit_download_get_current_size(dl
->download
), cur_sz
);
4900 webkit_download_get_total_size(dl
->download
), tot_sz
);
4902 status_html
= g_strdup_printf(
4903 "<div style='width: 100%%' align='center'>"
4904 "<div class='progress-outer'>"
4905 "<div class='progress-inner' style='width: %.2f%%'>"
4906 "</div></div></div>"
4907 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4908 progress
, cur_sz
, tot_sz
, progress
);
4910 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4911 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4915 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4916 status_html
= g_strdup_printf("Cancelled");
4917 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4918 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4920 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4921 status_html
= g_strdup_printf("Error!");
4922 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4923 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4925 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4926 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4927 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4928 status_html
= g_strdup_printf("Starting");
4931 show_oops(t
, "%s: unknown download status", __func__
);
4934 new_html
= g_strdup_printf(
4935 "%s\n<tr><td>%s</td><td>%s</td>"
4936 "<td style='text-align:center'>%s</td></tr>\n",
4937 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4938 status_html
, cmd_html
);
4942 g_free(status_html
);
4953 * update all download tabs apart from one. Pass NULL if
4954 * you want to update all.
4957 update_download_tabs(struct tab
*apart_from
)
4960 if (!updating_dl_tabs
) {
4961 updating_dl_tabs
= 1; /* stop infinite recursion */
4962 TAILQ_FOREACH(t
, &tabs
, entry
)
4963 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4964 && (t
!= apart_from
))
4965 xtp_page_dl(t
, NULL
);
4966 updating_dl_tabs
= 0;
4971 * update all cookie tabs apart from one. Pass NULL if
4972 * you want to update all.
4975 update_cookie_tabs(struct tab
*apart_from
)
4978 if (!updating_cl_tabs
) {
4979 updating_cl_tabs
= 1; /* stop infinite recursion */
4980 TAILQ_FOREACH(t
, &tabs
, entry
)
4981 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4982 && (t
!= apart_from
))
4983 xtp_page_cl(t
, NULL
);
4984 updating_cl_tabs
= 0;
4989 * update all history tabs apart from one. Pass NULL if
4990 * you want to update all.
4993 update_history_tabs(struct tab
*apart_from
)
4997 if (!updating_hl_tabs
) {
4998 updating_hl_tabs
= 1; /* stop infinite recursion */
4999 TAILQ_FOREACH(t
, &tabs
, entry
)
5000 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
5001 && (t
!= apart_from
))
5002 xtp_page_hl(t
, NULL
);
5003 updating_hl_tabs
= 0;
5007 /* cookie management XTP page */
5009 xtp_page_cl(struct tab
*t
, struct karg
*args
)
5011 char *body
, *page
, *tmp
;
5012 int i
= 1; /* all ids start 1 */
5013 GSList
*sc
, *pc
, *pc_start
;
5015 char *type
, *table_headers
, *last_domain
;
5017 DNPRINTF(XT_D_CMD
, "%s", __func__
);
5020 show_oops(NULL
, "%s invalid parameters", __func__
);
5024 /* Generate a new session key */
5025 if (!updating_cl_tabs
)
5026 generate_xtp_session_key(&cl_session_key
);
5029 table_headers
= g_strdup_printf("<table><tr>"
5032 "<th style='width:200px'>Value</th>"
5036 "<th>HTTP<br />only</th>"
5037 "<th style='width:40px'>Rm</th></tr>\n");
5039 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
5040 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
5044 last_domain
= strdup("");
5045 for (; sc
; sc
= sc
->next
) {
5048 if (strcmp(last_domain
, c
->domain
) != 0) {
5051 last_domain
= strdup(c
->domain
);
5055 body
= g_strdup_printf("%s</table>"
5057 body
, c
->domain
, table_headers
);
5061 body
= g_strdup_printf("<h2>%s</h2>%s\n",
5062 c
->domain
, table_headers
);
5067 for (pc
= pc_start
; pc
; pc
= pc
->next
)
5068 if (soup_cookie_equal(pc
->data
, c
)) {
5069 type
= "Session + Persistent";
5074 body
= g_strdup_printf(
5077 "<td style='word-wrap:normal'>%s</td>"
5079 " <textarea rows='4'>%s</textarea>"
5085 "<td style='text-align:center'>"
5086 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
5093 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
5108 soup_cookies_free(sc
);
5109 soup_cookies_free(pc
);
5111 /* small message if there are none */
5113 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5114 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
5117 body
= g_strdup_printf("%s</table>", body
);
5120 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
5122 g_free(table_headers
);
5123 g_free(last_domain
);
5125 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
5126 update_cookie_tabs(t
);
5134 xtp_page_hl(struct tab
*t
, struct karg
*args
)
5136 char *body
, *page
, *tmp
;
5138 int i
= 1; /* all ids start 1 */
5140 DNPRINTF(XT_D_CMD
, "%s", __func__
);
5143 show_oops(NULL
, "%s invalid parameters", __func__
);
5147 /* Generate a new session key */
5148 if (!updating_hl_tabs
)
5149 generate_xtp_session_key(&hl_session_key
);
5152 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
5153 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
5155 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
5157 body
= g_strdup_printf(
5159 "<td><a href='%s'>%s</a></td>"
5161 "<td style='text-align: center'>"
5162 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
5163 body
, h
->uri
, h
->uri
, h
->title
,
5164 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
5165 XT_XTP_HL_REMOVE
, i
);
5171 /* small message if there are none */
5174 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5175 "colspan='3'>No History</td></tr>\n", body
);
5180 body
= g_strdup_printf("%s</table>", body
);
5183 page
= get_html_page("History", body
, "", TRUE
);
5187 * update all history manager tabs as the xtp session
5188 * key has now changed. No need to update the current tab.
5189 * Already did that above.
5191 update_history_tabs(t
);
5193 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5200 * Generate a web page detailing the status of any downloads
5203 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5205 struct download
*dl
;
5206 char *body
, *page
, *tmp
;
5210 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5213 show_oops(NULL
, "%s invalid parameters", __func__
);
5218 * Generate a new session key for next page instance.
5219 * This only happens for the top level call to xtp_page_dl()
5220 * in which case updating_dl_tabs is 0.
5222 if (!updating_dl_tabs
)
5223 generate_xtp_session_key(&dl_session_key
);
5225 /* header - with refresh so as to update */
5226 if (refresh_interval
>= 1)
5227 ref
= g_strdup_printf(
5228 "<meta http-equiv='refresh' content='%u"
5229 ";url=%s%d/%s/%d' />\n",
5238 body
= g_strdup_printf("<div align='center'>"
5239 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5240 "</p><table><tr><th style='width: 60%%'>"
5241 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5242 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5244 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5245 body
= xtp_page_dl_row(t
, body
, dl
);
5249 /* message if no downloads in list */
5252 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5253 " style='text-align: center'>"
5254 "No downloads</td></tr>\n", body
);
5259 body
= g_strdup_printf("%s</table></div>", body
);
5262 page
= get_html_page("Downloads", body
, ref
, 1);
5267 * update all download manager tabs as the xtp session
5268 * key has now changed. No need to update the current tab.
5269 * Already did that above.
5271 update_download_tabs(t
);
5273 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5280 search(struct tab
*t
, struct karg
*args
)
5284 if (t
== NULL
|| args
== NULL
) {
5285 show_oops(NULL
, "search invalid parameters");
5290 case XT_SEARCH_NEXT
:
5291 d
= t
->search_forward
;
5293 case XT_SEARCH_PREV
:
5294 d
= !t
->search_forward
;
5297 return (XT_CB_PASSTHROUGH
);
5300 if (t
->search_text
== NULL
) {
5301 if (global_search
== NULL
)
5302 return (XT_CB_PASSTHROUGH
);
5304 d
= t
->search_forward
= TRUE
;
5305 t
->search_text
= g_strdup(global_search
);
5306 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5307 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5311 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5312 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5314 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5316 return (XT_CB_HANDLED
);
5319 struct settings_args
{
5325 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5328 struct settings_args
*sa
= cb_args
;
5333 if (s
->flags
& XT_SF_RUNTIME
)
5339 *sa
->body
= g_strdup_printf(
5341 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5342 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5354 set_show(struct tab
*t
, struct karg
*args
)
5356 char *body
, *page
, *tmp
;
5358 struct settings_args sa
;
5360 bzero(&sa
, sizeof sa
);
5364 body
= g_strdup_printf("<div align='center'><table><tr>"
5365 "<th align='left'>Setting</th>"
5366 "<th align='left'>Value</th></tr>\n");
5368 settings_walk(print_setting
, &sa
);
5371 /* small message if there are none */
5374 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5375 "colspan='2'>No settings</td></tr>\n", body
);
5380 body
= g_strdup_printf("%s</table></div>", body
);
5383 page
= get_html_page("Settings", body
, "", 0);
5387 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5391 return (XT_CB_PASSTHROUGH
);
5395 set(struct tab
*t
, struct karg
*args
)
5400 if (args
== NULL
|| args
->s
== NULL
)
5401 return (set_show(t
, args
));
5404 p
= g_strstrip(args
->s
);
5407 return (set_show(t
, args
));
5409 /* we got some sort of string */
5410 val
= g_strrstr(p
, "=");
5413 val
= g_strchomp(val
);
5416 for (i
= 0; i
< LENGTH(rs
); i
++) {
5417 if (strcmp(rs
[i
].name
, p
))
5420 if (rs
[i
].activate
) {
5421 if (rs
[i
].activate(val
))
5422 show_oops(t
, "%s invalid value %s",
5425 show_oops(t
, ":set %s = %s", p
, val
);
5428 show_oops(t
, "not a runtime option: %s", p
);
5432 show_oops(t
, "unknown option: %s", p
);
5436 for (i
= 0; i
< LENGTH(rs
); i
++) {
5437 if (strcmp(rs
[i
].name
, p
))
5440 /* XXX this could use some cleanup */
5441 switch (rs
[i
].type
) {
5444 show_oops(t
, "%s = %d",
5445 rs
[i
].name
, *rs
[i
].ival
);
5446 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5447 show_oops(t
, "%s = %s",
5449 rs
[i
].s
->get(&rs
[i
]));
5450 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5451 show_oops(t
, "%s = ...", rs
[i
].name
);
5453 show_oops(t
, "%s = ", rs
[i
].name
);
5457 show_oops(t
, "%s = %f",
5458 rs
[i
].name
, *rs
[i
].fval
);
5459 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5460 show_oops(t
, "%s = %s",
5462 rs
[i
].s
->get(&rs
[i
]));
5463 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5464 show_oops(t
, "%s = ...", rs
[i
].name
);
5466 show_oops(t
, "%s = ", rs
[i
].name
);
5469 if (rs
[i
].sval
&& *rs
[i
].sval
)
5470 show_oops(t
, "%s = %s",
5471 rs
[i
].name
, *rs
[i
].sval
);
5472 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5473 show_oops(t
, "%s = %s",
5475 rs
[i
].s
->get(&rs
[i
]));
5476 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5477 show_oops(t
, "%s = ...", rs
[i
].name
);
5479 show_oops(t
, "%s = ", rs
[i
].name
);
5482 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5488 show_oops(t
, "unknown option: %s", p
);
5491 return (XT_CB_PASSTHROUGH
);
5495 session_save(struct tab
*t
, char *filename
)
5501 if (strlen(filename
) == 0)
5504 if (filename
[0] == '.' || filename
[0] == '/')
5508 if (save_tabs(t
, &a
))
5510 strlcpy(named_session
, filename
, sizeof named_session
);
5512 /* add the new session to the list of sessions */
5513 s
= g_malloc(sizeof(struct session
));
5514 s
->name
= g_strdup(filename
);
5515 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5523 session_open(struct tab
*t
, char *filename
)
5528 if (strlen(filename
) == 0)
5531 if (filename
[0] == '.' || filename
[0] == '/')
5535 a
.i
= XT_SES_CLOSETABS
;
5536 if (open_tabs(t
, &a
))
5539 strlcpy(named_session
, filename
, sizeof named_session
);
5547 session_delete(struct tab
*t
, char *filename
)
5549 char file
[PATH_MAX
];
5553 if (strlen(filename
) == 0)
5556 if (filename
[0] == '.' || filename
[0] == '/')
5559 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5563 if (!strcmp(filename
, named_session
))
5564 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5565 sizeof named_session
);
5567 /* remove session from sessions list */
5568 TAILQ_FOREACH(s
, &sessions
, entry
) {
5569 if (!strcmp(s
->name
, filename
))
5574 TAILQ_REMOVE(&sessions
, s
, entry
);
5575 g_free((gpointer
) s
->name
);
5584 session_cmd(struct tab
*t
, struct karg
*args
)
5586 char *filename
= args
->s
;
5591 if (args
->i
& XT_SHOW
)
5592 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5593 XT_SAVED_TABS_FILE
: named_session
);
5594 else if (args
->i
& XT_SAVE
) {
5595 if (session_save(t
, filename
)) {
5596 show_oops(t
, "Can't save session: %s",
5597 filename
? filename
: "INVALID");
5600 } else if (args
->i
& XT_OPEN
) {
5601 if (session_open(t
, filename
)) {
5602 show_oops(t
, "Can't open session: %s",
5603 filename
? filename
: "INVALID");
5606 } else if (args
->i
& XT_DELETE
) {
5607 if (session_delete(t
, filename
)) {
5608 show_oops(t
, "Can't delete session: %s",
5609 filename
? filename
: "INVALID");
5614 return (XT_CB_PASSTHROUGH
);
5618 * Make a hardcopy of the page
5621 print_page(struct tab
*t
, struct karg
*args
)
5623 WebKitWebFrame
*frame
;
5625 GtkPrintOperation
*op
;
5626 GtkPrintOperationAction action
;
5627 GtkPrintOperationResult print_res
;
5628 GError
*g_err
= NULL
;
5629 int marg_l
, marg_r
, marg_t
, marg_b
;
5631 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5633 ps
= gtk_page_setup_new();
5634 op
= gtk_print_operation_new();
5635 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5636 frame
= webkit_web_view_get_main_frame(t
->wv
);
5638 /* the default margins are too small, so we will bump them */
5639 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5640 XT_PRINT_EXTRA_MARGIN
;
5641 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5642 XT_PRINT_EXTRA_MARGIN
;
5643 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5644 XT_PRINT_EXTRA_MARGIN
;
5645 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5646 XT_PRINT_EXTRA_MARGIN
;
5649 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5650 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5651 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5652 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5654 gtk_print_operation_set_default_page_setup(op
, ps
);
5656 /* this appears to free 'op' and 'ps' */
5657 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5659 /* check it worked */
5660 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5661 show_oops(NULL
, "can't print: %s", g_err
->message
);
5662 g_error_free (g_err
);
5670 go_home(struct tab
*t
, struct karg
*args
)
5677 set_encoding(struct tab
*t
, struct karg
*args
)
5681 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5682 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5684 e
= webkit_web_view_get_encoding(t
->wv
);
5685 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5687 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5693 restart(struct tab
*t
, struct karg
*args
)
5697 a
.s
= XT_RESTART_TABS_FILE
;
5699 execvp(start_argv
[0], start_argv
);
5705 #define CTRL GDK_CONTROL_MASK
5706 #define MOD1 GDK_MOD1_MASK
5707 #define SHFT GDK_SHIFT_MASK
5709 /* inherent to GTK not all keys will be caught at all times */
5710 /* XXX sort key bindings */
5711 struct key_binding
{
5716 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5718 { "cookiejar", MOD1
, 0, GDK_j
},
5719 { "downloadmgr", MOD1
, 0, GDK_d
},
5720 { "history", MOD1
, 0, GDK_h
},
5721 { "print", CTRL
, 0, GDK_p
},
5722 { "search", 0, 0, GDK_slash
},
5723 { "searchb", 0, 0, GDK_question
},
5724 { "statustoggle", CTRL
, 0, GDK_n
},
5725 { "command", 0, 0, GDK_colon
},
5726 { "qa", CTRL
, 0, GDK_q
},
5727 { "restart", MOD1
, 0, GDK_q
},
5728 { "js toggle", CTRL
, 0, GDK_j
},
5729 { "cookie toggle", MOD1
, 0, GDK_c
},
5730 { "togglesrc", CTRL
, 0, GDK_s
},
5731 { "yankuri", 0, 0, GDK_y
},
5732 { "pasteuricur", 0, 0, GDK_p
},
5733 { "pasteurinew", 0, 0, GDK_P
},
5734 { "toplevel toggle", 0, 0, GDK_F4
},
5735 { "help", 0, 0, GDK_F1
},
5736 { "run_script", MOD1
, 0, GDK_r
},
5739 { "searchnext", 0, 0, GDK_n
},
5740 { "searchprevious", 0, 0, GDK_N
},
5743 { "focusaddress", 0, 0, GDK_F6
},
5744 { "focussearch", 0, 0, GDK_F7
},
5747 { "hinting", 0, 0, GDK_f
},
5749 /* custom stylesheet */
5750 { "userstyle", 0, 0, GDK_i
},
5753 { "goback", 0, 0, GDK_BackSpace
},
5754 { "goback", MOD1
, 0, GDK_Left
},
5755 { "goforward", SHFT
, 0, GDK_BackSpace
},
5756 { "goforward", MOD1
, 0, GDK_Right
},
5757 { "reload", 0, 0, GDK_F5
},
5758 { "reload", CTRL
, 0, GDK_r
},
5759 { "reload", CTRL
, 0, GDK_l
},
5760 { "favorites", MOD1
, 1, GDK_f
},
5762 /* vertical movement */
5763 { "scrolldown", 0, 0, GDK_j
},
5764 { "scrolldown", 0, 0, GDK_Down
},
5765 { "scrollup", 0, 0, GDK_Up
},
5766 { "scrollup", 0, 0, GDK_k
},
5767 { "scrollbottom", 0, 0, GDK_G
},
5768 { "scrollbottom", 0, 0, GDK_End
},
5769 { "scrolltop", 0, 0, GDK_Home
},
5770 { "scrollpagedown", 0, 0, GDK_space
},
5771 { "scrollpagedown", CTRL
, 0, GDK_f
},
5772 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5773 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5774 { "scrollpageup", 0, 0, GDK_Page_Up
},
5775 { "scrollpageup", CTRL
, 0, GDK_b
},
5776 { "scrollhalfup", CTRL
, 0, GDK_u
},
5777 /* horizontal movement */
5778 { "scrollright", 0, 0, GDK_l
},
5779 { "scrollright", 0, 0, GDK_Right
},
5780 { "scrollleft", 0, 0, GDK_Left
},
5781 { "scrollleft", 0, 0, GDK_h
},
5782 { "scrollfarright", 0, 0, GDK_dollar
},
5783 { "scrollfarleft", 0, 0, GDK_0
},
5786 { "tabnew", CTRL
, 0, GDK_t
},
5787 { "999tabnew", CTRL
, 0, GDK_T
},
5788 { "tabclose", CTRL
, 1, GDK_w
},
5789 { "tabundoclose", 0, 0, GDK_U
},
5790 { "tabnext 1", CTRL
, 0, GDK_1
},
5791 { "tabnext 2", CTRL
, 0, GDK_2
},
5792 { "tabnext 3", CTRL
, 0, GDK_3
},
5793 { "tabnext 4", CTRL
, 0, GDK_4
},
5794 { "tabnext 5", CTRL
, 0, GDK_5
},
5795 { "tabnext 6", CTRL
, 0, GDK_6
},
5796 { "tabnext 7", CTRL
, 0, GDK_7
},
5797 { "tabnext 8", CTRL
, 0, GDK_8
},
5798 { "tabnext 9", CTRL
, 0, GDK_9
},
5799 { "tabfirst", CTRL
, 0, GDK_less
},
5800 { "tablast", CTRL
, 0, GDK_greater
},
5801 { "tabprevious", CTRL
, 0, GDK_Left
},
5802 { "tabnext", CTRL
, 0, GDK_Right
},
5803 { "focusout", CTRL
, 0, GDK_minus
},
5804 { "focusin", CTRL
, 0, GDK_plus
},
5805 { "focusin", CTRL
, 0, GDK_equal
},
5806 { "focusreset", CTRL
, 0, GDK_0
},
5808 /* command aliases (handy when -S flag is used) */
5809 { "promptopen", 0, 0, GDK_F9
},
5810 { "promptopencurrent", 0, 0, GDK_F10
},
5811 { "prompttabnew", 0, 0, GDK_F11
},
5812 { "prompttabnewcurrent",0, 0, GDK_F12
},
5814 TAILQ_HEAD(keybinding_list
, key_binding
);
5817 walk_kb(struct settings
*s
,
5818 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5820 struct key_binding
*k
;
5823 if (s
== NULL
|| cb
== NULL
) {
5824 show_oops(NULL
, "walk_kb invalid parameters");
5828 TAILQ_FOREACH(k
, &kbl
, entry
) {
5834 if (gdk_keyval_name(k
->key
) == NULL
)
5837 strlcat(str
, k
->cmd
, sizeof str
);
5838 strlcat(str
, ",", sizeof str
);
5840 if (k
->mask
& GDK_SHIFT_MASK
)
5841 strlcat(str
, "S-", sizeof str
);
5842 if (k
->mask
& GDK_CONTROL_MASK
)
5843 strlcat(str
, "C-", sizeof str
);
5844 if (k
->mask
& GDK_MOD1_MASK
)
5845 strlcat(str
, "M1-", sizeof str
);
5846 if (k
->mask
& GDK_MOD2_MASK
)
5847 strlcat(str
, "M2-", sizeof str
);
5848 if (k
->mask
& GDK_MOD3_MASK
)
5849 strlcat(str
, "M3-", sizeof str
);
5850 if (k
->mask
& GDK_MOD4_MASK
)
5851 strlcat(str
, "M4-", sizeof str
);
5852 if (k
->mask
& GDK_MOD5_MASK
)
5853 strlcat(str
, "M5-", sizeof str
);
5855 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5856 cb(s
, str
, cb_args
);
5861 init_keybindings(void)
5864 struct key_binding
*k
;
5866 for (i
= 0; i
< LENGTH(keys
); i
++) {
5867 k
= g_malloc0(sizeof *k
);
5868 k
->cmd
= keys
[i
].cmd
;
5869 k
->mask
= keys
[i
].mask
;
5870 k
->use_in_entry
= keys
[i
].use_in_entry
;
5871 k
->key
= keys
[i
].key
;
5872 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5874 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5875 k
->cmd
? k
->cmd
: "unnamed key");
5880 keybinding_clearall(void)
5882 struct key_binding
*k
, *next
;
5884 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5885 next
= TAILQ_NEXT(k
, entry
);
5889 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5890 k
->cmd
? k
->cmd
: "unnamed key");
5891 TAILQ_REMOVE(&kbl
, k
, entry
);
5897 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5899 struct key_binding
*k
;
5900 guint keyval
, mask
= 0;
5903 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5905 /* Keys which are to be used in entry have been prefixed with an
5906 * exclamation mark. */
5910 /* find modifier keys */
5911 if (strstr(key
, "S-"))
5912 mask
|= GDK_SHIFT_MASK
;
5913 if (strstr(key
, "C-"))
5914 mask
|= GDK_CONTROL_MASK
;
5915 if (strstr(key
, "M1-"))
5916 mask
|= GDK_MOD1_MASK
;
5917 if (strstr(key
, "M2-"))
5918 mask
|= GDK_MOD2_MASK
;
5919 if (strstr(key
, "M3-"))
5920 mask
|= GDK_MOD3_MASK
;
5921 if (strstr(key
, "M4-"))
5922 mask
|= GDK_MOD4_MASK
;
5923 if (strstr(key
, "M5-"))
5924 mask
|= GDK_MOD5_MASK
;
5927 for (i
= strlen(key
) - 1; i
> 0; i
--)
5931 /* validate keyname */
5932 keyval
= gdk_keyval_from_name(key
);
5933 if (keyval
== GDK_VoidSymbol
) {
5934 warnx("invalid keybinding name %s", key
);
5937 /* must run this test too, gtk+ doesn't handle 10 for example */
5938 if (gdk_keyval_name(keyval
) == NULL
) {
5939 warnx("invalid keybinding name %s", key
);
5943 /* Remove eventual dupes. */
5944 TAILQ_FOREACH(k
, &kbl
, entry
)
5945 if (k
->key
== keyval
&& k
->mask
== mask
) {
5946 TAILQ_REMOVE(&kbl
, k
, entry
);
5952 k
= g_malloc0(sizeof *k
);
5953 k
->cmd
= g_strdup(cmd
);
5955 k
->use_in_entry
= use_in_entry
;
5958 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5963 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5964 k
->cmd
, gdk_keyval_name(keyval
));
5966 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5972 add_kb(struct settings
*s
, char *entry
)
5976 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5978 /* clearall is special */
5979 if (!strcmp(entry
, "clearall")) {
5980 keybinding_clearall();
5984 kb
= strstr(entry
, ",");
5990 return (keybinding_add(entry
, key
, key
[0] == '!'));
5996 int (*func
)(struct tab
*, struct karg
*);
6000 { "command", 0, command
, ':', 0 },
6001 { "search", 0, command
, '/', 0 },
6002 { "searchb", 0, command
, '?', 0 },
6003 { "togglesrc", 0, toggle_src
, 0, 0 },
6005 /* yanking and pasting */
6006 { "yankuri", 0, yank_uri
, 0, 0 },
6007 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
6008 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
6009 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
6012 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
6013 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
6016 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
6017 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
6020 { "hinting", 0, hint
, 0, 0 },
6022 /* custom stylesheet */
6023 { "userstyle", 0, userstyle
, 0, 0 },
6026 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
6027 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
6028 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
6030 /* vertical movement */
6031 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
6032 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
6033 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
6034 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
6035 { "1", 0, move
, XT_MOVE_TOP
, 0 },
6036 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
6037 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
6038 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
6039 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
6040 /* horizontal movement */
6041 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
6042 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
6043 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
6044 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
6046 { "favorites", 0, xtp_page_fl
, 0, 0 },
6047 { "fav", 0, xtp_page_fl
, 0, 0 },
6048 { "favadd", 0, add_favorite
, 0, 0 },
6050 { "qall", 0, quit
, 0, 0 },
6051 { "quitall", 0, quit
, 0, 0 },
6052 { "w", 0, save_tabs
, 0, 0 },
6053 { "wq", 0, save_tabs_and_quit
, 0, 0 },
6054 { "help", 0, help
, 0, 0 },
6055 { "about", 0, about
, 0, 0 },
6056 { "stats", 0, stats
, 0, 0 },
6057 { "version", 0, about
, 0, 0 },
6060 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6061 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6062 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
6063 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6064 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6065 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6066 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
6067 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6068 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6069 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6070 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6072 /* cookie command */
6073 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6074 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6075 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
6076 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6077 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6078 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6079 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
6080 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6081 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6082 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6083 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6085 /* plugin command */
6086 { "plugin", 0, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6087 { "save", 1, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6088 { "domain", 2, pl_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
6089 { "fqdn", 2, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6090 { "show", 1, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6091 { "all", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6092 { "persistent", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
6093 { "session", 2, pl_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6094 { "toggle", 1, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6095 { "domain", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6096 { "fqdn", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6098 /* toplevel (domain) command */
6099 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6100 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6103 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
6106 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
6107 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
6108 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
6110 { "ca", 0, ca_cmd
, 0, 0 },
6111 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
6112 { "dl", 0, xtp_page_dl
, 0, 0 },
6113 { "h", 0, xtp_page_hl
, 0, 0 },
6114 { "history", 0, xtp_page_hl
, 0, 0 },
6115 { "home", 0, go_home
, 0, 0 },
6116 { "restart", 0, restart
, 0, 0 },
6117 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
6118 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
6119 { "statustoggle", 0, statustoggle
, 0, 0 },
6120 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
6122 { "print", 0, print_page
, 0, 0 },
6125 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
6126 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
6127 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
6128 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6129 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6130 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
6131 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
6132 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6133 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
6134 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
6135 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
6136 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6137 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
6138 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
6139 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
6140 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
6141 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
6142 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
6143 { "buffers", 0, buffers
, 0, 0 },
6144 { "ls", 0, buffers
, 0, 0 },
6145 { "tabs", 0, buffers
, 0, 0 },
6146 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
6148 /* command aliases (handy when -S flag is used) */
6149 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
6150 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
6151 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
6152 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
6155 { "set", 0, set
, 0, XT_SETARG
},
6157 { "fullscreen", 0, fullscreen
, 0, 0 },
6158 { "f", 0, fullscreen
, 0, 0 },
6161 { "session", 0, session_cmd
, XT_SHOW
, 0 },
6162 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
6163 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
6164 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
6165 { "show", 1, session_cmd
, XT_SHOW
, 0 },
6172 } cmd_status
= {-1, 0};
6175 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6178 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
6185 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6192 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6194 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
6200 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
6202 a
.i
= XT_NAV_FORWARD
;
6212 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6214 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6216 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6223 * cancel, remove, etc. downloads
6226 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6228 struct download find
, *d
= NULL
;
6230 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6232 /* some commands require a valid download id */
6233 if (cmd
!= XT_XTP_DL_LIST
) {
6234 /* lookup download in question */
6236 d
= RB_FIND(download_list
, &downloads
, &find
);
6239 show_oops(t
, "%s: no such download", __func__
);
6244 /* decide what to do */
6246 case XT_XTP_DL_CANCEL
:
6247 webkit_download_cancel(d
->download
);
6249 case XT_XTP_DL_REMOVE
:
6250 webkit_download_cancel(d
->download
); /* just incase */
6251 g_object_unref(d
->download
);
6252 RB_REMOVE(download_list
, &downloads
, d
);
6254 case XT_XTP_DL_LIST
:
6258 show_oops(t
, "%s: unknown command", __func__
);
6261 xtp_page_dl(t
, NULL
);
6265 * Actions on history, only does one thing for now, but
6266 * we provide the function for future actions
6269 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6271 struct history
*h
, *next
;
6275 case XT_XTP_HL_REMOVE
:
6276 /* walk backwards, as listed in reverse */
6277 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6278 next
= RB_PREV(history_list
, &hl
, h
);
6280 RB_REMOVE(history_list
, &hl
, h
);
6281 g_free((gpointer
) h
->title
);
6282 g_free((gpointer
) h
->uri
);
6289 case XT_XTP_HL_LIST
:
6290 /* Nothing - just xtp_page_hl() below */
6293 show_oops(t
, "%s: unknown command", __func__
);
6297 xtp_page_hl(t
, NULL
);
6300 /* remove a favorite */
6302 remove_favorite(struct tab
*t
, int index
)
6304 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6305 char *new_favs
, *tmp
;
6310 /* open favorites */
6311 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6313 if ((f
= fopen(file
, "r")) == NULL
) {
6314 show_oops(t
, "%s: can't open favorites: %s",
6315 __func__
, strerror(errno
));
6319 /* build a string which will become the new favroites file */
6320 new_favs
= g_strdup("");
6323 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6324 if (feof(f
) || ferror(f
))
6326 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6333 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6334 if (feof(f
) || ferror(f
)) {
6335 show_oops(t
, "%s: can't parse favorites %s",
6336 __func__
, strerror(errno
));
6341 /* as long as this isn't the one we are deleting add to file */
6344 new_favs
= g_strdup_printf("%s%s\n%s\n",
6345 new_favs
, title
, uri
);
6357 /* write back new favorites file */
6358 if ((f
= fopen(file
, "w")) == NULL
) {
6359 show_oops(t
, "%s: can't open favorites: %s",
6360 __func__
, strerror(errno
));
6364 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
6365 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
6378 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6381 case XT_XTP_FL_LIST
:
6382 /* nothing, just the below call to xtp_page_fl() */
6384 case XT_XTP_FL_REMOVE
:
6385 remove_favorite(t
, arg
);
6388 show_oops(t
, "%s: invalid favorites command", __func__
);
6392 xtp_page_fl(t
, NULL
);
6396 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6399 case XT_XTP_CL_LIST
:
6400 /* nothing, just xtp_page_cl() */
6402 case XT_XTP_CL_REMOVE
:
6406 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6410 xtp_page_cl(t
, NULL
);
6413 /* link an XTP class to it's session key and handler function */
6414 struct xtp_despatch
{
6417 void (*handle_func
)(struct tab
*, uint8_t, int);
6420 struct xtp_despatch xtp_despatches
[] = {
6421 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6422 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6423 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6424 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6425 { XT_XTP_INVALID
, NULL
, NULL
}
6429 * is the url xtp protocol? (xxxt://)
6430 * if so, parse and despatch correct bahvior
6433 parse_xtp_url(struct tab
*t
, const char *url
)
6435 char *dup
= NULL
, *p
, *last
= NULL
;
6436 uint8_t n_tokens
= 0;
6437 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6438 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6443 * tokens array meaning:
6445 * tokens[1] = session key
6446 * tokens[2] = action
6447 * tokens[3] = optional argument
6450 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6452 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6455 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6457 /* split out the url */
6458 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6459 (p
= strtok_r(NULL
, "/", &last
))) {
6461 tokens
[n_tokens
++] = p
;
6464 /* should be atleast three fields 'class/seskey/command/arg' */
6468 dsp
= xtp_despatches
;
6469 req_class
= atoi(tokens
[0]);
6470 while (dsp
->xtp_class
) {
6471 if (dsp
->xtp_class
== req_class
) {
6478 /* did we find one atall? */
6479 if (dsp_match
== NULL
) {
6480 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6484 /* check session key and call despatch function */
6485 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6486 ret
= TRUE
; /* all is well, this was a valid xtp request */
6487 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6500 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6502 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6504 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6507 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6512 show_oops(t
, "activate_uri_entry_cb no uri");
6516 uri
+= strspn(uri
, "\t ");
6518 /* if xxxt:// treat specially */
6519 if (parse_xtp_url(t
, uri
))
6522 /* otherwise continue to load page normally */
6523 load_uri(t
, (gchar
*)uri
);
6528 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6530 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6531 char *newuri
= NULL
;
6534 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6537 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6541 if (search_string
== NULL
) {
6542 show_oops(t
, "no search_string");
6546 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6548 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6549 newuri
= g_strdup_printf(search_string
, enc_search
);
6553 webkit_web_view_load_uri(t
->wv
, newuri
);
6561 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6563 struct domain
*d
= NULL
;
6566 if (uri
== NULL
|| t
== NULL
)
6569 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6574 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6575 es
? "enable" : "disable", uri
);
6577 g_object_set(G_OBJECT(t
->settings
),
6578 "enable-html5-local-storage", es
, (char *)NULL
);
6579 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6583 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6585 struct domain
*d
= NULL
;
6588 if (uri
== NULL
|| t
== NULL
)
6591 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6596 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6597 es
? "enable" : "disable", uri
);
6599 g_object_set(G_OBJECT(t
->settings
),
6600 "enable-scripts", es
, (char *)NULL
);
6601 g_object_set(G_OBJECT(t
->settings
),
6602 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6603 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6605 button_set_stockid(t
->js_toggle
,
6606 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6610 check_and_set_pl(const gchar
*uri
, struct tab
*t
)
6612 struct domain
*d
= NULL
;
6615 if (uri
== NULL
|| t
== NULL
)
6618 if ((d
= wl_find_uri(uri
, &pl_wl
)) == NULL
)
6623 DNPRINTF(XT_D_JS
, "check_and_set_pl: %s %s\n",
6624 es
? "enable" : "disable", uri
);
6626 g_object_set(G_OBJECT(t
->settings
),
6627 "enable-plugins", es
, (char *)NULL
);
6628 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6632 color_address_bar(gpointer p
)
6635 struct tab
*tt
, *t
= p
;
6636 gchar
*col_str
= XT_COLOR_WHITE
;
6637 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
6640 gdk_threads_enter();
6642 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6644 /* make sure t still exists */
6647 TAILQ_FOREACH(tt
, &tabs
, entry
)
6653 if ((uri
= get_uri(t
)) == NULL
)
6658 gdk_threads_leave();
6661 col_str
= XT_COLOR_YELLOW
;
6662 switch (load_compare_cert(u
, &error_str
)) {
6664 col_str
= XT_COLOR_BLUE
;
6667 col_str
= XT_COLOR_GREEN
;
6669 case CERT_UNTRUSTED
:
6670 col_str
= XT_COLOR_YELLOW
;
6673 col_str
= XT_COLOR_RED
;
6678 gdk_threads_enter();
6680 /* make sure t isn't deleted */
6681 TAILQ_FOREACH(tt
, &tabs
, entry
)
6688 /* test to see if the user navigated away and canceled the thread */
6689 if (t
->thread
!= g_thread_self())
6691 if ((uri
= get_uri(t
)) == NULL
) {
6695 if (strcmp(uri
, u
)) {
6696 /* make sure we are still the same url */
6702 gdk_color_parse(col_str
, &color
);
6703 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6705 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6706 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6708 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6710 if (error_str
&& error_str
[0] != '\0')
6711 show_oops(t
, "%s", error_str
);
6716 /* t is invalid at this point */
6718 g_free((gpointer
)u
);
6720 gdk_threads_leave();
6725 show_ca_status(struct tab
*t
, const char *uri
)
6728 gchar
*col_str
= XT_COLOR_WHITE
;
6730 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6731 ssl_strict_certs
, ssl_ca_file
, uri
);
6738 if (ssl_ca_file
== NULL
) {
6739 if (g_str_has_prefix(uri
, "http://"))
6741 if (g_str_has_prefix(uri
, "https://")) {
6742 col_str
= XT_COLOR_RED
;
6747 if (g_str_has_prefix(uri
, "http://") ||
6748 !g_str_has_prefix(uri
, "https://"))
6752 * It is not necessary to see if the thread is already running.
6753 * If the thread is in progress setting it to something else aborts it
6757 /* thread the coloring of the address bar */
6758 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
6760 color_address_bar(t
);
6766 gdk_color_parse(col_str
, &color
);
6767 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6769 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6770 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6772 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6777 free_favicon(struct tab
*t
)
6779 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6780 __func__
, t
->icon_download
, t
->icon_request
);
6782 if (t
->icon_request
)
6783 g_object_unref(t
->icon_request
);
6784 if (t
->icon_dest_uri
)
6785 g_free(t
->icon_dest_uri
);
6787 t
->icon_request
= NULL
;
6788 t
->icon_dest_uri
= NULL
;
6792 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6794 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6795 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6797 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6798 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6800 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6801 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6805 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6807 GdkPixbuf
*pb_scaled
;
6809 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6810 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6811 GDK_INTERP_BILINEAR
);
6815 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6816 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6818 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6819 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6821 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6822 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6824 if (pb_scaled
!= pb
)
6825 g_object_unref(pb_scaled
);
6829 xt_icon_from_file(struct tab
*t
, char *file
)
6833 if (g_str_has_prefix(file
, "file://"))
6834 file
+= strlen("file://");
6836 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6838 xt_icon_from_pixbuf(t
, pb
);
6841 xt_icon_from_name(t
, "text-html");
6845 is_valid_icon(char *file
)
6848 const char *mime_type
;
6852 gf
= g_file_new_for_path(file
);
6853 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6855 mime_type
= g_file_info_get_content_type(fi
);
6856 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6857 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6858 g_strcmp0(mime_type
, "image/png") == 0 ||
6859 g_strcmp0(mime_type
, "image/gif") == 0 ||
6860 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6868 set_favicon_from_file(struct tab
*t
, char *file
)
6872 if (t
== NULL
|| file
== NULL
)
6875 if (g_str_has_prefix(file
, "file://"))
6876 file
+= strlen("file://");
6877 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6879 if (!stat(file
, &sb
)) {
6880 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6881 /* corrupt icon so trash it */
6882 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6885 /* no need to set icon to default here */
6889 xt_icon_from_file(t
, file
);
6893 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6896 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6897 struct tab
*tt
= NULL
, *t
= NULL
;
6900 * find the webview instead of passing in the tab as it could have been
6901 * deleted from underneath us.
6903 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6912 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6913 __func__
, t
->tab_id
, status
);
6916 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6918 t
->icon_download
= NULL
;
6921 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6924 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6927 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6929 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6930 __func__
, t
->tab_id
);
6931 t
->icon_download
= NULL
;
6934 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6937 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6938 __func__
, t
->icon_dest_uri
);
6939 set_favicon_from_file(t
, t
->icon_dest_uri
);
6940 /* these will be freed post callback */
6941 t
->icon_request
= NULL
;
6942 t
->icon_download
= NULL
;
6950 abort_favicon_download(struct tab
*t
)
6952 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6954 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6955 if (t
->icon_download
) {
6956 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6957 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6958 webkit_download_cancel(t
->icon_download
);
6959 t
->icon_download
= NULL
;
6964 xt_icon_from_name(t
, "text-html");
6968 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6970 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6972 if (uri
== NULL
|| t
== NULL
)
6975 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6976 /* take icon from WebKitIconDatabase */
6979 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6981 xt_icon_from_pixbuf(t
, pb
);
6984 xt_icon_from_name(t
, "text-html");
6985 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6986 /* download icon to cache dir */
6987 gchar
*name_hash
, file
[PATH_MAX
];
6990 if (t
->icon_request
) {
6991 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6995 /* check to see if we got the icon in cache */
6996 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6997 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
7000 if (!stat(file
, &sb
)) {
7001 if (sb
.st_size
> 0) {
7002 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
7004 set_favicon_from_file(t
, file
);
7008 /* corrupt icon so trash it */
7009 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
7014 /* create download for icon */
7015 t
->icon_request
= webkit_network_request_new(uri
);
7016 if (t
->icon_request
== NULL
) {
7017 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
7022 t
->icon_download
= webkit_download_new(t
->icon_request
);
7023 if (t
->icon_download
== NULL
)
7026 /* we have to free icon_dest_uri later */
7027 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
7028 webkit_download_set_destination_uri(t
->icon_download
,
7031 if (webkit_download_get_status(t
->icon_download
) ==
7032 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7033 g_object_unref(t
->icon_request
);
7034 g_free(t
->icon_dest_uri
);
7035 t
->icon_request
= NULL
;
7036 t
->icon_dest_uri
= NULL
;
7040 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
7041 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
7043 webkit_download_start(t
->icon_download
);
7048 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
7050 const gchar
*uri
= NULL
, *title
= NULL
;
7051 struct history
*h
, find
;
7055 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
7056 webkit_web_view_get_load_status(wview
),
7057 get_uri(t
) ? get_uri(t
) : "NOTHING");
7060 show_oops(NULL
, "notify_load_status_cb invalid parameters");
7064 switch (webkit_web_view_get_load_status(wview
)) {
7065 case WEBKIT_LOAD_PROVISIONAL
:
7067 abort_favicon_download(t
);
7068 #if GTK_CHECK_VERSION(2, 20, 0)
7069 gtk_widget_show(t
->spinner
);
7070 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
7072 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
7074 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
7076 /* assume we are a new address */
7077 gdk_color_parse("white", &color
);
7078 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
7079 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
7081 /* take focus if we are visible */
7085 /* kill color thread */
7090 case WEBKIT_LOAD_COMMITTED
:
7095 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
7101 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
7103 /* check if js white listing is enabled */
7104 if (enable_plugin_whitelist
)
7105 check_and_set_pl(uri
, t
);
7106 if (enable_cookie_whitelist
)
7107 check_and_set_cookie(uri
, t
);
7108 if (enable_js_whitelist
)
7109 check_and_set_js(uri
, t
);
7115 /* we know enough to autosave the session */
7116 if (session_autosave
) {
7121 show_ca_status(t
, uri
);
7124 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
7128 case WEBKIT_LOAD_FINISHED
:
7134 if (!strncmp(uri
, "http://", strlen("http://")) ||
7135 !strncmp(uri
, "https://", strlen("https://")) ||
7136 !strncmp(uri
, "file://", strlen("file://"))) {
7138 h
= RB_FIND(history_list
, &hl
, &find
);
7140 title
= get_title(t
, FALSE
);
7141 h
= g_malloc(sizeof *h
);
7142 h
->uri
= g_strdup(uri
);
7143 h
->title
= g_strdup(title
);
7144 RB_INSERT(history_list
, &hl
, h
);
7145 completion_add_uri(h
->uri
);
7146 update_history_tabs(NULL
);
7150 set_status(t
, (char *)uri
, XT_STATUS_URI
);
7151 #if WEBKIT_CHECK_VERSION(1, 1, 18)
7152 case WEBKIT_LOAD_FAILED
:
7155 #if GTK_CHECK_VERSION(2, 20, 0)
7156 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7157 gtk_widget_hide(t
->spinner
);
7160 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
7165 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
7167 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
7168 can_go_back_for_real(t
));
7170 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
7171 can_go_forward_for_real(t
));
7175 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
7177 const gchar
*title
= NULL
, *win_title
= NULL
;
7179 title
= get_title(t
, FALSE
);
7180 win_title
= get_title(t
, TRUE
);
7181 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
7182 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
7183 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
7184 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
7188 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7190 run_script(t
, JS_HINTING
);
7194 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
7196 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
7197 progress
== 100 ? 0 : (double)progress
/ 100);
7198 if (show_url
== 0) {
7199 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
7200 progress
== 100 ? 0 : (double)progress
/ 100);
7203 update_statusbar_position(NULL
, NULL
);
7207 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
7208 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
7209 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
7212 WebKitWebNavigationReason reason
;
7213 struct domain
*d
= NULL
;
7216 show_oops(NULL
, "webview_npd_cb invalid parameters");
7220 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
7222 webkit_network_request_get_uri(request
));
7224 uri
= (char *)webkit_network_request_get_uri(request
);
7226 /* if this is an xtp url, we don't load anything else */
7227 if (parse_xtp_url(t
, uri
))
7230 if (t
->ctrl_click
) {
7232 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
7233 webkit_web_policy_decision_ignore(pd
);
7234 return (TRUE
); /* we made the decission */
7238 * This is a little hairy but it comes down to this:
7239 * when we run in whitelist mode we have to assist the browser in
7240 * opening the URL that it would have opened in a new tab.
7242 reason
= webkit_web_navigation_action_get_reason(na
);
7243 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
7244 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7245 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
7246 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7248 webkit_web_policy_decision_use(pd
);
7249 return (TRUE
); /* we made the decision */
7256 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7259 struct domain
*d
= NULL
;
7261 WebKitWebView
*webview
= NULL
;
7263 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
7264 webkit_web_view_get_uri(wv
));
7267 /* open in current tab */
7269 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7270 uri
= webkit_web_view_get_uri(wv
);
7271 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7274 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7276 } else if (enable_scripts
== 1) {
7277 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7285 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
7288 struct domain
*d
= NULL
;
7290 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7292 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7293 uri
= webkit_web_view_get_uri(wv
);
7294 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7298 } else if (enable_scripts
== 1)
7305 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7307 /* we can not eat the event without throwing gtk off so defer it */
7309 /* catch middle click */
7310 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7315 /* catch ctrl click */
7316 if (e
->type
== GDK_BUTTON_RELEASE
&&
7317 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7322 return (XT_CB_PASSTHROUGH
);
7326 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7328 struct mime_type
*m
;
7330 m
= find_mime_type(mime_type
);
7338 show_oops(t
, "can't fork mime handler");
7348 execlp(m
->mt_action
, m
->mt_action
,
7349 webkit_network_request_get_uri(request
), (void *)NULL
);
7358 get_mime_type(const char *file
)
7361 char *mime_type
= NULL
;
7365 if (g_str_has_prefix(file
, "file://"))
7366 file
+= strlen("file://");
7368 gf
= g_file_new_for_path(file
);
7369 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7371 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
7372 mime_type
= g_strdup(m
);
7380 run_download_mimehandler(char *mime_type
, char *file
)
7382 struct mime_type
*m
;
7384 m
= find_mime_type(mime_type
);
7390 show_oops(NULL
, "can't fork download mime handler");
7400 if (g_str_has_prefix(file
, "file://"))
7401 file
+= strlen("file://");
7402 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7411 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7414 WebKitDownloadStatus status
;
7415 const char *file
= NULL
;
7418 if (download
== NULL
)
7420 status
= webkit_download_get_status(download
);
7421 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7424 file
= webkit_download_get_destination_uri(download
);
7427 mime
= get_mime_type(file
);
7431 run_download_mimehandler((char *)mime
, (char *)file
);
7436 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7437 WebKitNetworkRequest
*request
, char *mime_type
,
7438 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7441 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7445 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7446 t
->tab_id
, mime_type
);
7448 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7449 webkit_web_policy_decision_ignore(decision
);
7454 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7455 webkit_web_policy_decision_download(decision
);
7463 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7467 const gchar
*suggested_name
;
7468 gchar
*filename
= NULL
;
7470 struct download
*download_entry
;
7473 if (wk_download
== NULL
|| t
== NULL
) {
7474 show_oops(NULL
, "%s invalid parameters", __func__
);
7478 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7479 if (suggested_name
== NULL
)
7480 return (FALSE
); /* abort download */
7491 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7493 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7494 filename
: suggested_name
);
7496 } while (!stat(uri
+ strlen("file://"), &sb
));
7498 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7499 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7501 webkit_download_set_destination_uri(wk_download
, uri
);
7503 if (webkit_download_get_status(wk_download
) ==
7504 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7505 show_oops(t
, "%s: download failed to start", __func__
);
7507 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7509 /* connect "download first" mime handler */
7510 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7511 G_CALLBACK(download_status_changed_cb
), NULL
);
7513 download_entry
= g_malloc(sizeof(struct download
));
7514 download_entry
->download
= wk_download
;
7515 download_entry
->tab
= t
;
7516 download_entry
->id
= next_download_id
++;
7517 RB_INSERT(download_list
, &downloads
, download_entry
);
7518 /* get from history */
7519 g_object_ref(wk_download
);
7520 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7521 show_oops(t
, "Download of '%s' started...",
7522 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7531 /* sync other download manager tabs */
7532 update_download_tabs(NULL
);
7535 * NOTE: never redirect/render the current tab before this
7536 * function returns. This will cause the download to never start.
7538 return (ret
); /* start download */
7542 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7544 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7547 show_oops(NULL
, "webview_hover_cb");
7552 set_status(t
, uri
, XT_STATUS_LINK
);
7555 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7560 mark(struct tab
*t
, struct karg
*arg
)
7566 if ((index
= marktoindex(mark
)) == -1)
7569 if (arg
->i
== XT_MARK_SET
)
7570 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7571 else if (arg
->i
== XT_MARK_GOTO
) {
7572 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7573 show_oops(t
, "mark '%c' does not exist", mark
);
7576 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7577 something changes the document size */
7578 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7585 marks_clear(struct tab
*t
)
7589 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7590 t
->mark
[i
] = XT_INVALID_MARK
;
7596 char file
[PATH_MAX
];
7597 char *line
= NULL
, *p
;
7602 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7603 if ((f
= fopen(file
, "r+")) == NULL
) {
7604 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7608 for (i
= 1; ; i
++) {
7609 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7611 if (strlen(line
) == 0 || line
[0] == '#') {
7617 p
= strtok(line
, " \t");
7619 if (p
== NULL
|| strlen(p
) != 1 ||
7620 (index
= marktoindex(*p
)) == -1) {
7621 warnx("corrupt quickmarks file, line %d", i
);
7625 p
= strtok(NULL
, " \t");
7626 if (qmarks
[index
] != NULL
)
7627 g_free(qmarks
[index
]);
7628 qmarks
[index
] = g_strdup(p
);
7639 char file
[PATH_MAX
];
7643 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7644 if ((f
= fopen(file
, "r+")) == NULL
) {
7645 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7649 for (i
= 0; i
< XT_NOMARKS
; i
++)
7650 if (qmarks
[i
] != NULL
)
7651 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7659 qmark(struct tab
*t
, struct karg
*arg
)
7664 mark
= arg
->s
[strlen(arg
->s
)-1];
7665 index
= marktoindex(mark
);
7671 if (qmarks
[index
] != NULL
)
7672 g_free(qmarks
[index
]);
7674 qmarks_load(); /* sync if multiple instances */
7675 qmarks
[index
] = g_strdup(get_uri(t
));
7679 if (qmarks
[index
] != NULL
)
7680 load_uri(t
, qmarks
[index
]);
7682 show_oops(t
, "quickmark \"%c\" does not exist",
7688 if (qmarks
[index
] != NULL
)
7689 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7691 show_oops(t
, "quickmark \"%c\" does not exist",
7702 go_up(struct tab
*t
, struct karg
*args
)
7708 levels
= atoi(args
->s
);
7712 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7713 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7715 tmp
+= strlen(XT_PROTO_DELIM
);
7717 /* if an uri starts with a slash, leave it alone (for file:///) */
7724 p
= strrchr(tmp
, '/');
7738 gototab(struct tab
*t
, struct karg
*args
)
7741 struct karg arg
= {0, NULL
, -1};
7743 tab
= atoi(args
->s
);
7745 arg
.i
= XT_TAB_NEXT
;
7754 zoom_amount(struct tab
*t
, struct karg
*arg
)
7756 struct karg narg
= {0, NULL
, -1};
7758 narg
.i
= atoi(arg
->s
);
7759 resizetab(t
, &narg
);
7765 flip_colon(struct tab
*t
, struct karg
*arg
)
7767 struct karg narg
= {0, NULL
, -1};
7770 if (t
== NULL
|| arg
== NULL
)
7773 p
= strstr(arg
->s
, ":");
7785 /* buffer commands receive the regex that triggered them in arg.s */
7786 char bcmd
[XT_BUFCMD_SZ
];
7790 #define XT_PRE_NO (0)
7791 #define XT_PRE_YES (1)
7792 #define XT_PRE_MAYBE (2)
7794 int (*func
)(struct tab
*, struct karg
*);
7798 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7799 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7800 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7801 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7802 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7803 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7804 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7805 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7806 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7807 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7808 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7809 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7810 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7811 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7812 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7813 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7814 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7815 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7819 buffercmd_init(void)
7823 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7824 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7825 REG_EXTENDED
| REG_NOSUB
))
7826 startpage_add("invalid buffercmd regex %s",
7827 buffercmds
[i
].regex
);
7831 buffercmd_abort(struct tab
*t
)
7835 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7836 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7839 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7840 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7844 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7846 struct karg arg
= {0, NULL
, -1};
7849 arg
.s
= g_strdup(bcmd
);
7851 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7852 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7862 buffercmd_addkey(struct tab
*t
, guint keyval
)
7865 char s
[XT_BUFCMD_SZ
];
7867 if (keyval
== GDK_Escape
) {
7869 return (XT_CB_HANDLED
);
7872 /* key with modifier or non-ascii character */
7873 if (!isascii(keyval
))
7874 return (XT_CB_PASSTHROUGH
);
7876 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7877 "to buffer \"%s\"\n", keyval
, bcmd
);
7879 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7880 if (bcmd
[i
] == '\0') {
7885 /* buffer full, ignore input */
7886 if (i
>= LENGTH(bcmd
) -1) {
7887 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7889 return (XT_CB_HANDLED
);
7892 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7894 /* find exact match */
7895 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7896 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7897 (size_t) 0, NULL
, 0) == 0) {
7898 buffercmd_execute(t
, &buffercmds
[i
]);
7902 /* find non exact matches to see if we need to abort ot not */
7903 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7904 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7907 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7908 if (isdigit(bcmd
[0])) {
7909 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7913 if (sscanf(bcmd
, "%s", s
) == 0)
7916 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7917 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7920 if (sscanf(bcmd
, "%s", s
) == 0)
7923 if (c
== -1 && buffercmds
[i
].precount
)
7925 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7928 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7929 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7932 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7937 return (XT_CB_HANDLED
);
7941 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7943 struct key_binding
*k
;
7945 /* handle keybindings if buffercmd is empty.
7946 if not empty, allow commands like C-n */
7947 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7948 TAILQ_FOREACH(k
, &kbl
, entry
)
7949 if (e
->keyval
== k
->key
7950 && (entry
? k
->use_in_entry
: 1)) {
7952 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7953 return (cmd_execute(t
, k
->cmd
));
7954 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7955 return (cmd_execute(t
, k
->cmd
));
7959 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7960 return buffercmd_addkey(t
, e
->keyval
);
7962 return (XT_CB_PASSTHROUGH
);
7966 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7968 char s
[2], buf
[128];
7969 const char *errstr
= NULL
;
7971 /* don't use w directly; use t->whatever instead */
7974 show_oops(NULL
, "wv_keypress_after_cb");
7975 return (XT_CB_PASSTHROUGH
);
7978 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7979 e
->keyval
, e
->state
, t
);
7983 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7985 return (XT_CB_HANDLED
);
7989 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7991 /* we have a string */
7993 /* we have a number */
7994 snprintf(buf
, sizeof buf
,
7995 "vimprobable_fire(%s)", t
->hint_num
);
8002 /* XXX unfuck this */
8003 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
8004 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
8005 /* last input was numerical */
8007 l
= strlen(t
->hint_num
);
8014 t
->hint_num
[l
] = '\0';
8018 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
8019 /* last input was alphanumerical */
8021 l
= strlen(t
->hint_buf
);
8028 t
->hint_buf
[l
] = '\0';
8038 /* numerical input */
8039 if (CLEAN(e
->state
) == 0 &&
8040 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
8041 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
8042 snprintf(s
, sizeof s
, "%c", e
->keyval
);
8043 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
8044 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
8048 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
8049 "invalid link number\n");
8052 snprintf(buf
, sizeof buf
,
8053 "vimprobable_update_hints(%s)",
8055 t
->hint_mode
= XT_HINT_NUMERICAL
;
8059 /* empty the counter buffer */
8060 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
8061 return (XT_CB_HANDLED
);
8064 /* alphanumerical input */
8065 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
8066 e
->keyval
<= GDK_z
) ||
8067 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
8068 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
8069 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
8070 e
->keyval
<= GDK_9
) ||
8071 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
8072 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
8073 snprintf(s
, sizeof s
, "%c", e
->keyval
);
8074 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
8075 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
8076 " %s\n", t
->hint_buf
);
8078 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
8081 snprintf(buf
, sizeof buf
,
8082 "vimprobable_show_hints('%s')", t
->hint_buf
);
8083 t
->hint_mode
= XT_HINT_ALPHANUM
;
8086 /* empty the counter buffer */
8087 bzero(t
->hint_num
, sizeof t
->hint_num
);
8088 return (XT_CB_HANDLED
);
8091 return (XT_CB_HANDLED
);
8094 snprintf(s
, sizeof s
, "%c", e
->keyval
);
8095 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
8096 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
8099 return (handle_keypress(t
, e
, 0));
8103 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8107 /* Hide buffers, if they are visible, with escape. */
8108 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
8109 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
8110 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8112 return (XT_CB_HANDLED
);
8115 return (XT_CB_PASSTHROUGH
);
8119 search_continue(struct tab
*t
)
8121 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
8122 gboolean rv
= FALSE
;
8126 if (strlen(c
) == 1) {
8127 webkit_web_view_unmark_text_matches(t
->wv
);
8132 t
->search_forward
= TRUE
;
8133 else if (c
[0] == '?')
8134 t
->search_forward
= FALSE
;
8144 search_cb(struct tab
*t
)
8146 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
8149 if (search_continue(t
) == FALSE
)
8153 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
8155 /* not found, mark red */
8156 gdk_color_parse(XT_COLOR_RED
, &color
);
8157 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8158 /* unmark and remove selection */
8159 webkit_web_view_unmark_text_matches(t
->wv
);
8160 /* my kingdom for a way to unselect text in webview */
8162 /* found, highlight all */
8163 webkit_web_view_unmark_text_matches(t
->wv
);
8164 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
8165 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
8166 gdk_color_parse(XT_COLOR_WHITE
, &color
);
8167 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8175 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8177 const gchar
*c
= gtk_entry_get_text(w
);
8180 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
8181 return (XT_CB_PASSTHROUGH
);
8184 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
8185 e
->keyval
, e
->state
, t
);
8187 if (search_continue(t
) == FALSE
)
8190 /* if search length is > 4 then no longer play timeout games */
8191 if (strlen(c
) > 4) {
8193 g_source_remove(t
->search_id
);
8200 /* reestablish a new timer if the user types fast */
8202 g_source_remove(t
->search_id
);
8203 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
8206 return (XT_CB_PASSTHROUGH
);
8210 match_uri(const gchar
*uri
, const gchar
*key
) {
8213 gboolean match
= FALSE
;
8217 if (!strncmp(key
, uri
, len
))
8220 voffset
= strstr(uri
, "/") + 2;
8221 if (!strncmp(key
, voffset
, len
))
8223 else if (g_str_has_prefix(voffset
, "www.")) {
8224 voffset
= voffset
+ strlen("www.");
8225 if (!strncmp(key
, voffset
, len
))
8234 match_session(const gchar
*name
, const gchar
*key
) {
8237 sub
= strcasestr(name
, key
);
8243 cmd_getlist(int id
, char *key
)
8250 if (cmds
[id
].type
& XT_URLARG
) {
8251 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
8252 if (match_uri(h
->uri
, key
)) {
8253 cmd_status
.list
[c
] = (char *)h
->uri
;
8259 } else if (cmds
[id
].type
& XT_SESSARG
) {
8260 TAILQ_FOREACH(s
, &sessions
, entry
)
8261 if (match_session(s
->name
, key
)) {
8262 cmd_status
.list
[c
] = (char *)s
->name
;
8268 } else if (cmds
[id
].type
& XT_SETARG
) {
8269 for (i
= 0; i
< LENGTH(rs
); i
++)
8270 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
8271 cmd_status
.list
[c
++] = rs
[i
].name
;
8277 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
8279 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
8280 if (cmds
[i
].level
< dep
)
8282 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
8283 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
8284 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
8292 cmd_getnext(int dir
)
8294 cmd_status
.index
+= dir
;
8296 if (cmd_status
.index
< 0)
8297 cmd_status
.index
= cmd_status
.len
- 1;
8298 else if (cmd_status
.index
>= cmd_status
.len
)
8299 cmd_status
.index
= 0;
8301 return cmd_status
.list
[cmd_status
.index
];
8305 cmd_tokenize(char *s
, char *tokens
[])
8308 char *tok
, *last
= NULL
;
8309 size_t len
= strlen(s
);
8312 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8313 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8314 tok
= strtok_r(NULL
, " ", &last
), i
++)
8324 cmd_complete(struct tab
*t
, char *str
, int dir
)
8326 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8327 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8329 char *tok
, *match
, *s
= g_strdup(str
);
8331 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8334 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8337 for (i
= 0; isdigit(s
[i
]); i
++)
8340 for (; isspace(s
[i
]); i
++)
8345 levels
= cmd_tokenize(s
, tokens
);
8347 for (i
= 0; i
< levels
- 1; i
++) {
8350 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8351 if (cmds
[j
].level
< dep
)
8353 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8357 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8364 if (matchcount
== 1) {
8365 strlcat(res
, tok
, sizeof res
);
8366 strlcat(res
, " ", sizeof res
);
8376 if (cmd_status
.index
== -1)
8377 cmd_getlist(parent
, tokens
[i
]);
8379 if (cmd_status
.len
> 0) {
8380 match
= cmd_getnext(dir
);
8381 strlcat(res
, match
, sizeof res
);
8382 gtk_entry_set_text(w
, res
);
8383 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8390 cmd_execute(struct tab
*t
, char *str
)
8392 struct cmd
*cmd
= NULL
;
8393 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
8395 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8396 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8397 struct karg arg
= {0, NULL
, -1};
8402 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8408 while (isspace(s
[0]))
8411 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8412 prefix
= atoi(prefixstr
);
8416 for (tok
= strtok_r(s
, " ", &last
); tok
;
8417 tok
= strtok_r(NULL
, " ", &last
)) {
8419 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8420 if (cmds
[j
].level
< dep
)
8422 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8424 if (cmds
[j
].level
== dep
&&
8425 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8429 if (len
== strlen(cmds
[j
].cmd
)) {
8435 if (matchcount
== 1) {
8440 show_oops(t
, "Invalid command: %s", str
);
8446 show_oops(t
, "Empty command");
8452 arg
.precount
= prefix
;
8453 else if (cmd_prefix
> 0)
8454 arg
.precount
= cmd_prefix
;
8456 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8457 show_oops(t
, "No prefix allowed: %s", str
);
8461 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8462 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8463 if (arg
.s
== NULL
) {
8464 show_oops(t
, "Invalid command");
8467 arg
.precount
= atoi(arg
.s
);
8468 if (arg
.precount
<= 0) {
8469 if (arg
.s
[0] == '0')
8470 show_oops(t
, "Zero count");
8472 show_oops(t
, "Trailing characters");
8477 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8478 __func__
, arg
.precount
, arg
.s
);
8494 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8497 show_oops(NULL
, "entry_key_cb invalid parameters");
8498 return (XT_CB_PASSTHROUGH
);
8501 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8502 e
->keyval
, e
->state
, t
);
8506 if (e
->keyval
== GDK_Escape
) {
8507 /* don't use focus_webview(t) because we want to type :cmds */
8508 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8511 return (handle_keypress(t
, e
, 1));
8514 struct command_entry
*
8515 history_prev(struct command_list
*l
, struct command_entry
*at
)
8518 at
= TAILQ_LAST(l
, command_list
);
8520 at
= TAILQ_PREV(at
, command_list
, entry
);
8522 at
= TAILQ_LAST(l
, command_list
);
8528 struct command_entry
*
8529 history_next(struct command_list
*l
, struct command_entry
*at
)
8532 at
= TAILQ_FIRST(l
);
8534 at
= TAILQ_NEXT(at
, entry
);
8536 at
= TAILQ_FIRST(l
);
8543 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8545 int rv
= XT_CB_HANDLED
;
8546 const gchar
*c
= gtk_entry_get_text(w
);
8549 show_oops(NULL
, "cmd_keypress_cb parameters");
8550 return (XT_CB_PASSTHROUGH
);
8553 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8554 e
->keyval
, e
->state
, t
);
8558 e
->keyval
= GDK_Escape
;
8559 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8560 e
->keyval
= GDK_Escape
;
8562 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8563 e
->keyval
!= GDK_ISO_Left_Tab
)
8564 cmd_status
.index
= -1;
8566 switch (e
->keyval
) {
8569 cmd_complete(t
, (char *)&c
[1], 1);
8571 case GDK_ISO_Left_Tab
:
8573 cmd_complete(t
, (char *)&c
[1], -1);
8578 if ((search_at
= history_next(&shl
, search_at
))) {
8579 search_at
->line
[0] = c
[0];
8580 gtk_entry_set_text(w
, search_at
->line
);
8581 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8584 if ((history_at
= history_prev(&chl
, history_at
))) {
8585 history_at
->line
[0] = c
[0];
8586 gtk_entry_set_text(w
, history_at
->line
);
8587 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8594 if ((search_at
= history_next(&shl
, search_at
))) {
8595 search_at
->line
[0] = c
[0];
8596 gtk_entry_set_text(w
, search_at
->line
);
8597 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8600 if ((history_at
= history_next(&chl
, history_at
))) {
8601 history_at
->line
[0] = c
[0];
8602 gtk_entry_set_text(w
, history_at
->line
);
8603 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8609 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8617 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8618 webkit_web_view_unmark_text_matches(t
->wv
);
8622 rv
= XT_CB_PASSTHROUGH
;
8628 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8630 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8634 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8636 /* popup menu enabled */
8641 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8644 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8645 return (XT_CB_PASSTHROUGH
);
8648 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8649 t
->tab_id
, t
->popup
);
8651 /* if popup is enabled don't lose focus */
8654 return (XT_CB_PASSTHROUGH
);
8660 if (show_url
== 0 || t
->focus_wv
)
8663 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8665 return (XT_CB_PASSTHROUGH
);
8669 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8672 const gchar
*c
= gtk_entry_get_text(entry
);
8675 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8679 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8686 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8692 if (c
[0] == '/' || c
[0] == '?') {
8693 /* see if there is a timer pending */
8695 g_source_remove(t
->search_id
);
8700 if (t
->search_text
) {
8701 g_free(t
->search_text
);
8702 t
->search_text
= NULL
;
8705 t
->search_text
= g_strdup(s
);
8707 g_free(global_search
);
8708 global_search
= g_strdup(s
);
8709 t
->search_forward
= c
[0] == '/';
8711 history_add(&shl
, search_file
, s
, &search_history_count
);
8715 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8722 backward_cb(GtkWidget
*w
, struct tab
*t
)
8727 show_oops(NULL
, "backward_cb invalid parameters");
8731 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8738 forward_cb(GtkWidget
*w
, struct tab
*t
)
8743 show_oops(NULL
, "forward_cb invalid parameters");
8747 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8749 a
.i
= XT_NAV_FORWARD
;
8754 home_cb(GtkWidget
*w
, struct tab
*t
)
8757 show_oops(NULL
, "home_cb invalid parameters");
8761 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8767 stop_cb(GtkWidget
*w
, struct tab
*t
)
8769 WebKitWebFrame
*frame
;
8772 show_oops(NULL
, "stop_cb invalid parameters");
8776 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8778 frame
= webkit_web_view_get_main_frame(t
->wv
);
8779 if (frame
== NULL
) {
8780 show_oops(t
, "stop_cb: no frame");
8784 webkit_web_frame_stop_loading(frame
);
8785 abort_favicon_download(t
);
8789 setup_webkit(struct tab
*t
)
8791 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8792 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8793 FALSE
, (char *)NULL
);
8795 warnx("webkit does not have \"enable-dns-prefetching\" property");
8796 g_object_set(G_OBJECT(t
->settings
),
8797 "user-agent", t
->user_agent
, (char *)NULL
);
8798 g_object_set(G_OBJECT(t
->settings
),
8799 "enable-scripts", enable_scripts
, (char *)NULL
);
8800 g_object_set(G_OBJECT(t
->settings
),
8801 "enable-plugins", enable_plugins
, (char *)NULL
);
8802 g_object_set(G_OBJECT(t
->settings
),
8803 "javascript-can-open-windows-automatically", enable_scripts
,
8805 g_object_set(G_OBJECT(t
->settings
),
8806 "enable-html5-database", FALSE
, (char *)NULL
);
8807 g_object_set(G_OBJECT(t
->settings
),
8808 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8809 g_object_set(G_OBJECT(t
->settings
),
8810 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8811 g_object_set(G_OBJECT(t
->settings
),
8812 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8813 g_object_set(G_OBJECT(t
->wv
),
8814 "full-content-zoom", TRUE
, (char *)NULL
);
8816 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8820 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8822 struct tab
*ti
, *t
= NULL
;
8823 gdouble view_size
, value
, max
;
8826 TAILQ_FOREACH(ti
, &tabs
, entry
)
8827 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8835 if (adjustment
== NULL
)
8836 adjustment
= gtk_scrolled_window_get_vadjustment(
8837 GTK_SCROLLED_WINDOW(t
->browser_win
));
8839 view_size
= gtk_adjustment_get_page_size(adjustment
);
8840 value
= gtk_adjustment_get_value(adjustment
);
8841 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8844 position
= g_strdup("All");
8845 else if (value
== max
)
8846 position
= g_strdup("Bot");
8847 else if (value
== 0)
8848 position
= g_strdup("Top");
8850 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8852 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8859 create_browser(struct tab
*t
)
8863 GtkAdjustment
*adjustment
;
8866 show_oops(NULL
, "create_browser invalid parameters");
8870 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8871 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8872 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8873 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8875 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8876 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8877 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8879 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8880 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8883 t
->settings
= webkit_web_settings_new();
8885 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
8887 if (user_agent
== NULL
) {
8888 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8890 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8893 t
->user_agent
= g_strdup(user_agent
);
8895 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8898 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8899 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8900 G_CALLBACK(update_statusbar_position
), NULL
);
8912 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8913 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8914 gtk_widget_set_name(w
, "xxxterm");
8915 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8916 g_signal_connect(G_OBJECT(w
), "delete_event",
8917 G_CALLBACK (gtk_main_quit
), NULL
);
8923 create_kiosk_toolbar(struct tab
*t
)
8925 GtkWidget
*toolbar
= NULL
, *b
;
8927 b
= gtk_hbox_new(FALSE
, 0);
8929 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8931 /* backward button */
8932 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8933 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8934 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8935 G_CALLBACK(backward_cb
), t
);
8936 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8938 /* forward button */
8939 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8940 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8941 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8942 G_CALLBACK(forward_cb
), t
);
8943 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8946 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8947 gtk_widget_set_sensitive(t
->gohome
, true);
8948 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8949 G_CALLBACK(home_cb
), t
);
8950 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8952 /* create widgets but don't use them */
8953 t
->uri_entry
= gtk_entry_new();
8954 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8955 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8956 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8962 create_toolbar(struct tab
*t
)
8964 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8966 b
= gtk_hbox_new(FALSE
, 0);
8968 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8970 /* backward button */
8971 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8972 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8973 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8974 G_CALLBACK(backward_cb
), t
);
8975 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8977 /* forward button */
8978 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8979 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8980 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8981 G_CALLBACK(forward_cb
), t
);
8982 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8986 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8987 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8988 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8989 G_CALLBACK(stop_cb
), t
);
8990 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8994 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8995 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8996 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8997 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8998 G_CALLBACK(js_toggle_cb
), t
);
8999 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
9001 t
->uri_entry
= gtk_entry_new();
9002 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
9003 G_CALLBACK(activate_uri_entry_cb
), t
);
9004 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
9005 G_CALLBACK(entry_key_cb
), t
);
9007 eb1
= gtk_hbox_new(FALSE
, 0);
9008 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
9009 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
9010 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
9013 if (search_string
) {
9015 t
->search_entry
= gtk_entry_new();
9016 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
9017 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
9018 G_CALLBACK(activate_search_entry_cb
), t
);
9019 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
9020 G_CALLBACK(entry_key_cb
), t
);
9021 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
9022 eb2
= gtk_hbox_new(FALSE
, 0);
9023 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
9024 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
9026 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
9033 create_buffers(struct tab
*t
)
9035 GtkCellRenderer
*renderer
;
9038 view
= gtk_tree_view_new();
9040 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
9042 renderer
= gtk_cell_renderer_text_new();
9043 gtk_tree_view_insert_column_with_attributes
9044 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
9046 renderer
= gtk_cell_renderer_text_new();
9047 gtk_tree_view_insert_column_with_attributes
9048 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
9051 gtk_tree_view_set_model
9052 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
9058 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
9059 GtkTreeViewColumn
*col
, struct tab
*t
)
9064 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9066 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
9069 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
9070 set_current_tab(id
- 1);
9076 /* after tab reordering/creation/removal */
9083 TAILQ_FOREACH(t
, &tabs
, entry
) {
9084 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
9085 if (t
->tab_id
> maxid
)
9088 gtk_widget_show(t
->tab_elems
.sep
);
9091 TAILQ_FOREACH(t
, &tabs
, entry
) {
9092 if (t
->tab_id
== maxid
) {
9093 gtk_widget_hide(t
->tab_elems
.sep
);
9099 /* after active tab change */
9101 recolor_compact_tabs(void)
9107 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9108 TAILQ_FOREACH(t
, &tabs
, entry
)
9109 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
9112 curid
= gtk_notebook_get_current_page(notebook
);
9113 TAILQ_FOREACH(t
, &tabs
, entry
)
9114 if (t
->tab_id
== curid
) {
9115 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
9116 gtk_widget_modify_fg(t
->tab_elems
.label
,
9117 GTK_STATE_NORMAL
, &color
);
9123 set_current_tab(int page_num
)
9125 buffercmd_abort(get_current_tab());
9126 gtk_notebook_set_current_page(notebook
, page_num
);
9127 recolor_compact_tabs();
9131 undo_close_tab_save(struct tab
*t
)
9135 struct undo
*u1
, *u2
;
9137 WebKitWebHistoryItem
*item
;
9139 if ((uri
= get_uri(t
)) == NULL
)
9142 u1
= g_malloc0(sizeof(struct undo
));
9143 u1
->uri
= g_strdup(uri
);
9145 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9147 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
9148 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
9151 /* forward history */
9152 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
9156 u1
->history
= g_list_prepend(u1
->history
,
9157 webkit_web_history_item_copy(item
));
9158 items
= g_list_next(items
);
9163 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
9164 u1
->history
= g_list_prepend(u1
->history
,
9165 webkit_web_history_item_copy(item
));
9169 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
9173 u1
->history
= g_list_prepend(u1
->history
,
9174 webkit_web_history_item_copy(item
));
9175 items
= g_list_next(items
);
9178 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
9180 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
9181 u2
= TAILQ_LAST(&undos
, undo_tailq
);
9182 TAILQ_REMOVE(&undos
, u2
, entry
);
9184 g_list_free(u2
->history
);
9193 delete_tab(struct tab
*t
)
9197 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
9203 * no need to join thread here because it won't access t on completion
9207 TAILQ_REMOVE(&tabs
, t
, entry
);
9209 /* Halt all webkit activity. */
9210 abort_favicon_download(t
);
9211 webkit_web_view_stop_loading(t
->wv
);
9213 /* Save the tab, so we can undo the close. */
9214 undo_close_tab_save(t
);
9216 if (browser_mode
== XT_BM_KIOSK
) {
9217 gtk_widget_destroy(t
->uri_entry
);
9218 gtk_widget_destroy(t
->stop
);
9219 gtk_widget_destroy(t
->js_toggle
);
9222 gtk_widget_destroy(t
->tab_elems
.eventbox
);
9223 gtk_widget_destroy(t
->vbox
);
9227 g_source_remove(t
->search_id
);
9229 g_free(t
->user_agent
);
9230 g_free(t
->stylesheet
);
9234 if (TAILQ_EMPTY(&tabs
)) {
9235 if (browser_mode
== XT_BM_KIOSK
)
9236 create_new_tab(home
, NULL
, 1, -1);
9238 create_new_tab(NULL
, NULL
, 1, -1);
9241 /* recreate session */
9242 if (session_autosave
) {
9248 recolor_compact_tabs();
9252 update_statusbar_zoom(struct tab
*t
)
9255 char s
[16] = { '\0' };
9257 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9258 if ((zoom
<= 0.99 || zoom
>= 1.01))
9259 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
9260 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
9264 setzoom_webkit(struct tab
*t
, int adjust
)
9266 #define XT_ZOOMPERCENT 0.04
9271 show_oops(NULL
, "setzoom_webkit invalid parameters");
9275 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9276 if (adjust
== XT_ZOOM_IN
)
9277 zoom
+= XT_ZOOMPERCENT
;
9278 else if (adjust
== XT_ZOOM_OUT
)
9279 zoom
-= XT_ZOOMPERCENT
;
9280 else if (adjust
> 0)
9281 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
9283 show_oops(t
, "setzoom_webkit invalid zoom value");
9287 if (zoom
< XT_ZOOMPERCENT
)
9288 zoom
= XT_ZOOMPERCENT
;
9289 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
9290 update_statusbar_zoom(t
);
9294 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
9296 struct tab
*t
= (struct tab
*) data
;
9298 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
9300 switch (event
->button
) {
9302 set_current_tab(t
->tab_id
);
9313 append_tab(struct tab
*t
)
9318 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9319 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9323 create_sbe(int width
)
9327 sbe
= gtk_entry_new();
9328 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9329 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9330 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9331 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9332 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9333 gtk_widget_set_size_request(sbe
, width
, -1);
9339 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9344 WebKitWebHistoryItem
*item
;
9348 int sbe_p
= 0, sbe_b
= 0,
9351 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9353 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9354 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9358 t
= g_malloc0(sizeof *t
);
9360 if (title
== NULL
) {
9361 title
= "(untitled)";
9365 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9367 /* label + button for tab */
9368 b
= gtk_hbox_new(FALSE
, 0);
9371 #if GTK_CHECK_VERSION(2, 20, 0)
9372 t
->spinner
= gtk_spinner_new();
9374 t
->label
= gtk_label_new(title
);
9375 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9376 gtk_widget_set_size_request(t
->label
, 100, 0);
9377 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9378 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9379 gtk_widget_set_size_request(b
, 130, 0);
9381 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9382 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9383 #if GTK_CHECK_VERSION(2, 20, 0)
9384 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9388 if (browser_mode
== XT_BM_KIOSK
) {
9389 t
->toolbar
= create_kiosk_toolbar(t
);
9390 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9393 t
->toolbar
= create_toolbar(t
);
9395 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9403 t
->browser_win
= create_browser(t
);
9404 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9406 /* oops message for user feedback */
9407 t
->oops
= gtk_entry_new();
9408 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9409 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9410 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9411 gdk_color_parse(XT_COLOR_RED
, &color
);
9412 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9413 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9414 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9417 t
->cmd
= gtk_entry_new();
9418 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9419 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9420 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9421 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9424 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9426 t
->sbe
.statusbar
= gtk_entry_new();
9427 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9428 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9429 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9430 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9432 /* create these widgets only if specified in statusbar_elems */
9434 t
->sbe
.position
= create_sbe(40);
9435 t
->sbe
.zoom
= create_sbe(40);
9436 t
->sbe
.buffercmd
= create_sbe(60);
9438 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9440 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9443 /* gtk widgets cannot be added to a box twice. sbe_* variables
9444 make sure of this */
9445 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9449 GtkWidget
*sep
= gtk_vseparator_new();
9451 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9452 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9453 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9454 FALSE
, FALSE
, FALSE
);
9459 warnx("flag \"%c\" specified more than "
9460 "once in statusbar_elems\n", *p
);
9464 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9465 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9469 warnx("flag \"%c\" specified more than "
9470 "once in statusbar_elems\n", *p
);
9474 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9475 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9479 warnx("flag \"%c\" specified more than "
9480 "once in statusbar_elems\n", *p
);
9484 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9485 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9488 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9493 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9496 t
->buffers
= create_buffers(t
);
9497 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9499 /* xtp meaning is normal by default */
9500 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9502 /* set empty favicon */
9503 xt_icon_from_name(t
, "text-html");
9505 /* and show it all */
9506 gtk_widget_show_all(b
);
9507 gtk_widget_show_all(t
->vbox
);
9509 /* compact tab bar */
9510 t
->tab_elems
.label
= gtk_label_new(title
);
9511 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9512 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9513 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9514 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9516 t
->tab_elems
.eventbox
= gtk_event_box_new();
9517 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9518 t
->tab_elems
.sep
= gtk_vseparator_new();
9520 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9521 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9522 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9523 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9524 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9525 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9527 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9529 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9531 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9534 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9536 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9538 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9541 id
= position
>= 0 ? position
:
9542 gtk_notebook_get_current_page(notebook
) + 1;
9543 if (id
> gtk_notebook_get_n_pages(notebook
))
9546 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9547 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9548 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9549 t
->tab_elems
.eventbox
, id
);
9554 #if GTK_CHECK_VERSION(2, 20, 0)
9555 /* turn spinner off if we are a new tab without uri */
9557 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9558 gtk_widget_hide(t
->spinner
);
9561 /* make notebook tabs reorderable */
9562 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9564 /* compact tabs clickable */
9565 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9566 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9568 g_object_connect(G_OBJECT(t
->cmd
),
9569 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9570 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9571 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9572 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9573 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9576 /* reuse wv_button_cb to hide oops */
9577 g_object_connect(G_OBJECT(t
->oops
),
9578 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9581 g_signal_connect(t
->buffers
,
9582 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9583 g_object_connect(G_OBJECT(t
->buffers
),
9584 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9586 g_object_connect(G_OBJECT(t
->wv
),
9587 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9588 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9589 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9590 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9591 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9592 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9593 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9594 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9595 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9596 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9597 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9598 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9599 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9600 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9601 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9602 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9604 g_signal_connect(t
->wv
,
9605 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9606 g_signal_connect(t
->wv
,
9607 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9609 /* hijack the unused keys as if we were the browser */
9610 g_object_connect(G_OBJECT(t
->toolbar
),
9611 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9614 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9615 G_CALLBACK(tab_close_cb
), t
);
9618 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9619 /* restore the tab's history */
9620 if (u
&& u
->history
) {
9624 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9625 items
= g_list_next(items
);
9628 item
= g_list_nth_data(u
->history
, u
->back
);
9630 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9633 g_list_free(u
->history
);
9635 webkit_web_back_forward_list_clear(t
->bfl
);
9641 url_set_visibility();
9642 statusbar_set_visibility();
9645 set_current_tab(t
->tab_id
);
9646 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9650 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9654 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9661 recolor_compact_tabs();
9662 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9667 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9673 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9675 if (gtk_notebook_get_current_page(notebook
) == -1)
9678 TAILQ_FOREACH(t
, &tabs
, entry
) {
9679 if (t
->tab_id
== pn
) {
9680 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9683 uri
= get_title(t
, TRUE
);
9684 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9690 /* can't use focus_webview here */
9691 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9698 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9701 struct tab
*t
= NULL
, *tt
;
9705 TAILQ_FOREACH(tt
, &tabs
, entry
)
9706 if (tt
->tab_id
== pn
) {
9712 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9714 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9719 menuitem_response(struct tab
*t
)
9721 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9725 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9727 GtkWidget
*menu
, *menu_items
;
9728 GdkEventButton
*bevent
;
9732 if (event
->type
== GDK_BUTTON_PRESS
) {
9733 bevent
= (GdkEventButton
*) event
;
9734 menu
= gtk_menu_new();
9736 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9737 if ((uri
= get_uri(ti
)) == NULL
)
9738 /* XXX make sure there is something to print */
9739 /* XXX add gui pages in here to look purdy */
9741 menu_items
= gtk_menu_item_new_with_label(uri
);
9742 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9743 gtk_widget_show(menu_items
);
9745 g_signal_connect_swapped((menu_items
),
9746 "activate", G_CALLBACK(menuitem_response
),
9750 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9751 bevent
->button
, bevent
->time
);
9753 /* unref object so it'll free itself when popped down */
9754 #if !GTK_CHECK_VERSION(3, 0, 0)
9755 /* XXX does not need unref with gtk+3? */
9756 g_object_ref_sink(menu
);
9757 g_object_unref(menu
);
9760 return (TRUE
/* eat event */);
9763 return (FALSE
/* propagate */);
9767 icon_size_map(int icon_size
)
9769 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9770 icon_size
> GTK_ICON_SIZE_DIALOG
)
9771 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9777 create_button(char *name
, char *stockid
, int size
)
9779 GtkWidget
*button
, *image
;
9783 rcstring
= g_strdup_printf(
9784 "style \"%s-style\"\n"
9786 " GtkWidget::focus-padding = 0\n"
9787 " GtkWidget::focus-line-width = 0\n"
9791 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9792 gtk_rc_parse_string(rcstring
);
9794 button
= gtk_button_new();
9795 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9796 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9798 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9799 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9800 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9801 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9802 gtk_widget_set_name(button
, name
);
9803 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9809 button_set_stockid(GtkWidget
*button
, char *stockid
)
9813 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9814 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9815 gtk_button_set_image(GTK_BUTTON(button
), image
);
9819 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9822 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9825 if (xterm_workaround
== 0)
9829 * xterm doesn't play nice with clipboards because it clears the
9830 * primary when clicked. We rely on primary being set to properly
9831 * handle middle mouse button clicks (paste). So when someone clears
9832 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9833 * other application behavior (as in DON'T clear primary).
9836 p
= gtk_clipboard_wait_for_text(primary
);
9838 if (gdk_property_get(gdk_get_default_root_window(),
9840 gdk_atom_intern("STRING", FALSE
),
9842 1024 * 1024 /* picked out of my butt */,
9848 /* yes sir, we need to NUL the string */
9850 gtk_clipboard_set_text(primary
, p
, -1);
9864 char file
[PATH_MAX
];
9867 vbox
= gtk_vbox_new(FALSE
, 0);
9868 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9869 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9870 #if !GTK_CHECK_VERSION(3, 0, 0)
9871 /* XXX seems to be needed with gtk+2 */
9872 gtk_notebook_set_tab_hborder(notebook
, 0);
9873 gtk_notebook_set_tab_vborder(notebook
, 0);
9875 gtk_notebook_set_scrollable(notebook
, TRUE
);
9876 gtk_notebook_set_show_border(notebook
, FALSE
);
9877 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9879 abtn
= gtk_button_new();
9880 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9881 gtk_widget_set_size_request(arrow
, -1, -1);
9882 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9883 gtk_widget_set_size_request(abtn
, -1, 20);
9885 #if GTK_CHECK_VERSION(2, 20, 0)
9886 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9888 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9890 /* compact tab bar */
9891 tab_bar
= gtk_hbox_new(TRUE
, 0);
9893 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9894 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9895 gtk_widget_set_size_request(vbox
, -1, -1);
9897 g_object_connect(G_OBJECT(notebook
),
9898 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9900 g_object_connect(G_OBJECT(notebook
),
9901 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9902 NULL
, (char *)NULL
);
9903 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9904 G_CALLBACK(arrow_cb
), NULL
);
9906 main_window
= create_window();
9907 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9910 for (i
= 0; i
< LENGTH(icons
); i
++) {
9911 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9912 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9913 l
= g_list_append(l
, pb
);
9915 gtk_window_set_default_icon_list(l
);
9917 /* clipboard work around */
9918 if (xterm_workaround
)
9920 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9921 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9923 gtk_widget_show_all(abtn
);
9924 gtk_widget_show_all(main_window
);
9925 notebook_tab_set_visibility();
9929 set_hook(void **hook
, char *name
)
9932 errx(1, "set_hook");
9934 if (*hook
== NULL
) {
9935 *hook
= dlsym(RTLD_NEXT
, name
);
9937 errx(1, "can't hook %s", name
);
9941 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9943 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9945 g_return_val_if_fail(cookie1
, FALSE
);
9946 g_return_val_if_fail(cookie2
, FALSE
);
9948 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9949 !strcmp (cookie1
->value
, cookie2
->value
) &&
9950 !strcmp (cookie1
->path
, cookie2
->path
) &&
9951 !strcmp (cookie1
->domain
, cookie2
->domain
));
9955 transfer_cookies(void)
9958 SoupCookie
*sc
, *pc
;
9960 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9962 for (;cf
; cf
= cf
->next
) {
9964 sc
= soup_cookie_copy(pc
);
9965 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9968 soup_cookies_free(cf
);
9972 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9977 print_cookie("soup_cookie_jar_delete_cookie", c
);
9979 if (cookies_enabled
== 0)
9982 if (jar
== NULL
|| c
== NULL
)
9985 /* find and remove from persistent jar */
9986 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9988 for (;cf
; cf
= cf
->next
) {
9990 if (soup_cookie_equal(ci
, c
)) {
9991 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9996 soup_cookies_free(cf
);
9998 /* delete from session jar */
9999 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
10003 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
10005 struct domain
*d
= NULL
;
10009 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
10010 jar
, p_cookiejar
, s_cookiejar
);
10012 if (cookies_enabled
== 0)
10015 /* see if we are up and running */
10016 if (p_cookiejar
== NULL
) {
10017 _soup_cookie_jar_add_cookie(jar
, cookie
);
10020 /* disallow p_cookiejar adds, shouldn't happen */
10021 if (jar
== p_cookiejar
)
10025 if (jar
== NULL
|| cookie
== NULL
)
10028 if (enable_cookie_whitelist
&&
10029 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
10031 DNPRINTF(XT_D_COOKIE
,
10032 "soup_cookie_jar_add_cookie: reject %s\n",
10034 if (save_rejected_cookies
) {
10035 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
10036 show_oops(NULL
, "can't open reject cookie file");
10039 fseek(r_cookie_f
, 0, SEEK_END
);
10040 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
10041 cookie
->http_only
? "#HttpOnly_" : "",
10043 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
10045 cookie
->secure
? "TRUE" : "FALSE",
10047 (gulong
)soup_date_to_time_t(cookie
->expires
) :
10051 fflush(r_cookie_f
);
10052 fclose(r_cookie_f
);
10054 if (!allow_volatile_cookies
)
10058 if (cookie
->expires
== NULL
&& session_timeout
) {
10059 soup_cookie_set_expires(cookie
,
10060 soup_date_new_from_now(session_timeout
));
10061 print_cookie("modified add cookie", cookie
);
10064 /* see if we are white listed for persistence */
10065 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
10066 /* add to persistent jar */
10067 c
= soup_cookie_copy(cookie
);
10068 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
10069 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
10072 /* add to session jar */
10073 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
10074 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
10078 setup_cookies(void)
10080 char file
[PATH_MAX
];
10082 set_hook((void *)&_soup_cookie_jar_add_cookie
,
10083 "soup_cookie_jar_add_cookie");
10084 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
10085 "soup_cookie_jar_delete_cookie");
10087 if (cookies_enabled
== 0)
10091 * the following code is intricate due to overriding several libsoup
10093 * do not alter order of these operations.
10096 /* rejected cookies */
10097 if (save_rejected_cookies
)
10098 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
10101 /* persistent cookies */
10102 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
10103 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
10105 /* session cookies */
10106 s_cookiejar
= soup_cookie_jar_new();
10107 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
10108 cookie_policy
, (void *)NULL
);
10109 transfer_cookies();
10111 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
10115 setup_proxy(char *uri
)
10118 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
10119 soup_uri_free(proxy_uri
);
10123 if (http_proxy
!= uri
) {
10124 g_free(http_proxy
);
10130 http_proxy
= g_strdup(uri
);
10131 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
10132 proxy_uri
= soup_uri_new(http_proxy
);
10133 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
10134 g_object_set(session
, "proxy-uri", proxy_uri
,
10140 set_http_proxy(char *proxy
)
10147 /* see if we need to clear it instead */
10148 if (strlen(proxy
) == 0) {
10153 uri
= soup_uri_new(proxy
);
10154 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
10157 setup_proxy(proxy
);
10159 soup_uri_free(uri
);
10165 send_cmd_to_socket(char *cmd
)
10167 int s
, len
, rv
= 1;
10168 struct sockaddr_un sa
;
10170 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10171 warnx("%s: socket", __func__
);
10175 sa
.sun_family
= AF_UNIX
;
10176 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10177 work_dir
, XT_SOCKET_FILE
);
10178 len
= SUN_LEN(&sa
);
10180 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10181 warnx("%s: connect", __func__
);
10185 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
10186 warnx("%s: send", __func__
);
10197 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
10200 char str
[XT_MAX_URL_LENGTH
];
10201 socklen_t t
= sizeof(struct sockaddr_un
);
10202 struct sockaddr_un sa
;
10207 gint fd
= g_io_channel_unix_get_fd(source
);
10209 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
10214 if (getpeereid(s
, &uid
, &gid
) == -1) {
10215 warn("getpeereid");
10218 if (uid
!= getuid() || gid
!= getgid()) {
10219 warnx("unauthorized user");
10225 warnx("not a valid user");
10229 n
= recv(s
, str
, sizeof(str
), 0);
10233 tt
= TAILQ_LAST(&tabs
, tab_list
);
10234 cmd_execute(tt
, str
);
10241 int s
, len
, rv
= 1;
10242 struct sockaddr_un sa
;
10244 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10245 warn("is_running: socket");
10249 sa
.sun_family
= AF_UNIX
;
10250 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10251 work_dir
, XT_SOCKET_FILE
);
10252 len
= SUN_LEN(&sa
);
10254 /* connect to see if there is a listener */
10255 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
10256 rv
= 0; /* not running */
10258 rv
= 1; /* already running */
10269 struct sockaddr_un sa
;
10271 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10272 warn("build_socket: socket");
10276 sa
.sun_family
= AF_UNIX
;
10277 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10278 work_dir
, XT_SOCKET_FILE
);
10279 len
= SUN_LEN(&sa
);
10281 /* connect to see if there is a listener */
10282 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10283 /* no listener so we will */
10284 unlink(sa
.sun_path
);
10286 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10287 warn("build_socket: bind");
10291 if (listen(s
, 1) == -1) {
10292 warn("build_socket: listen");
10305 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10306 GtkTreeIter
*iter
, struct tab
*t
)
10310 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10311 load_uri(t
, value
);
10318 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10319 GtkTreeIter
*iter
, struct tab
*t
)
10323 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10324 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10325 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10332 completion_add_uri(const gchar
*uri
)
10336 /* add uri to list_store */
10337 gtk_list_store_append(completion_model
, &iter
);
10338 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10342 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10343 GtkTreeIter
*iter
, gpointer user_data
)
10346 gboolean match
= FALSE
;
10348 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10354 match
= match_uri(value
, key
);
10361 completion_add(struct tab
*t
)
10363 /* enable completion for tab */
10364 t
->completion
= gtk_entry_completion_new();
10365 gtk_entry_completion_set_text_column(t
->completion
, 0);
10366 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10367 gtk_entry_completion_set_model(t
->completion
,
10368 GTK_TREE_MODEL(completion_model
));
10369 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10371 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10372 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10373 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10374 G_CALLBACK(completion_select_cb
), t
);
10375 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10376 G_CALLBACK(completion_hover_cb
), t
);
10384 if (stat(dir
, &sb
)) {
10385 if (mkdir(dir
, S_IRWXU
) == -1)
10386 err(1, "mkdir %s", dir
);
10387 if (stat(dir
, &sb
))
10388 err(1, "stat %s", dir
);
10390 if (S_ISDIR(sb
.st_mode
) == 0)
10391 errx(1, "%s not a dir", dir
);
10392 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10393 warnx("fixing invalid permissions on %s", dir
);
10394 if (chmod(dir
, S_IRWXU
) == -1)
10395 err(1, "chmod %s", dir
);
10403 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10407 GStaticRecMutex my_gdk_mtx
= G_STATIC_REC_MUTEX_INIT
;
10408 volatile int mtx_depth
;
10412 * The linux flash plugin violates the gdk locking mechanism.
10413 * Work around the issue by using a recursive mutex with some match applied
10414 * to see if we hit a buggy condition.
10416 * The following code is painful so just don't read it. It really doesn't
10417 * make much sense but seems to work.
10422 g_static_rec_mutex_lock(&my_gdk_mtx
);
10425 if (mtx_depth
<= 0) {
10426 /* should not happen */
10427 show_oops(NULL
, "negative mutex locking bug, trying to "
10429 fprintf(stderr
, "negative mutex locking bug, trying to "
10431 g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10432 g_static_rec_mutex_lock(&my_gdk_mtx
);
10437 if (mtx_depth
!= 1) {
10438 /* decrease mutext depth to 1 */
10440 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10442 } while (mtx_depth
> 1);
10451 /* if mutex depth isn't 1 then something went bad */
10452 if (mtx_depth
!= 1) {
10453 x
= g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10455 /* should not happen */
10456 show_oops(NULL
, "mutex unlocking bug, trying to "
10458 fprintf(stderr
, "mutex unlocking bug, trying to "
10462 if (mtx_complain
== 0) {
10463 show_oops(NULL
, "buggy mutex implementation detected, "
10464 "work around implemented");
10465 fprintf(stderr
, "buggy mutex implementation detected, "
10466 "work around implemented");
10473 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10477 main(int argc
, char *argv
[])
10480 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10481 char conf
[PATH_MAX
] = { '\0' };
10482 char file
[PATH_MAX
];
10483 char *env_proxy
= NULL
;
10487 struct sigaction sact
;
10488 GIOChannel
*channel
;
10495 g_thread_init(NULL
);
10496 gdk_threads_set_lock_functions(mtx_lock
, mtx_unlock
);
10497 gdk_threads_init();
10498 gdk_threads_enter();
10500 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pthread
);
10502 gtk_init(&argc
, &argv
);
10504 gnutls_global_init();
10506 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10511 RB_INIT(&downloads
);
10513 TAILQ_INIT(&sessions
);
10516 TAILQ_INIT(&aliases
);
10517 TAILQ_INIT(&undos
);
10523 /* fiddle with ulimits */
10524 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10527 /* just use them all */
10528 rlp
.rlim_cur
= rlp
.rlim_max
;
10529 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10531 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10533 else if (rlp
.rlim_cur
<= 256)
10534 startpage_add("%s requires at least 256 file "
10535 "descriptors, currently it has up to %d available",
10536 __progname
, rlp
.rlim_cur
);
10539 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10548 errx(0 , "Version: %s", version
);
10551 strlcpy(conf
, optarg
, sizeof(conf
));
10554 strlcpy(named_session
, optarg
, sizeof(named_session
));
10573 init_keybindings();
10575 /* generate session keys for xtp pages */
10576 generate_xtp_session_key(&dl_session_key
);
10577 generate_xtp_session_key(&hl_session_key
);
10578 generate_xtp_session_key(&cl_session_key
);
10579 generate_xtp_session_key(&fl_session_key
);
10582 bzero(&sact
, sizeof(sact
));
10583 sigemptyset(&sact
.sa_mask
);
10584 sact
.sa_handler
= sigchild
;
10585 sact
.sa_flags
= SA_NOCLDSTOP
;
10586 sigaction(SIGCHLD
, &sact
, NULL
);
10588 /* set download dir */
10589 pwd
= getpwuid(getuid());
10591 errx(1, "invalid user %d", getuid());
10592 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10594 /* compile buffer command regexes */
10597 /* set default string settings */
10598 home
= g_strdup("https://www.cyphertite.com");
10599 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10600 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10601 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10602 cmd_font_name
= g_strdup("monospace normal 9");
10603 oops_font_name
= g_strdup("monospace normal 9");
10604 statusbar_font_name
= g_strdup("monospace normal 9");
10605 tabbar_font_name
= g_strdup("monospace normal 9");
10606 statusbar_elems
= g_strdup("BP");
10607 encoding
= g_strdup("ISO-8859-1");
10609 /* read config file */
10610 if (strlen(conf
) == 0)
10611 snprintf(conf
, sizeof conf
, "%s/.%s",
10612 pwd
->pw_dir
, XT_CONF_FILE
);
10613 config_parse(conf
, 0);
10616 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10617 oops_font
= pango_font_description_from_string(oops_font_name
);
10618 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10619 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10621 /* working directory */
10622 if (strlen(work_dir
) == 0)
10623 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10624 pwd
->pw_dir
, XT_DIR
);
10627 /* icon cache dir */
10628 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10629 xxx_dir(cache_dir
);
10632 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10633 xxx_dir(certs_dir
);
10636 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10637 work_dir
, XT_SESSIONS_DIR
);
10638 xxx_dir(sessions_dir
);
10640 /* runtime settings that can override config file */
10641 if (runtime_settings
[0] != '\0')
10642 config_parse(runtime_settings
, 1);
10645 if (!strcmp(download_dir
, pwd
->pw_dir
))
10646 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10647 xxx_dir(download_dir
);
10649 /* favorites file */
10650 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10651 if (stat(file
, &sb
)) {
10652 warnx("favorites file doesn't exist, creating it");
10653 if ((f
= fopen(file
, "w")) == NULL
)
10654 err(1, "favorites");
10658 /* quickmarks file */
10659 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10660 if (stat(file
, &sb
)) {
10661 warnx("quickmarks file doesn't exist, creating it");
10662 if ((f
= fopen(file
, "w")) == NULL
)
10663 err(1, "quickmarks");
10667 /* search history */
10668 if (history_autosave
) {
10669 snprintf(search_file
, sizeof search_file
, "%s/%s",
10670 work_dir
, XT_SEARCH_FILE
);
10671 if (stat(search_file
, &sb
)) {
10672 warnx("search history file doesn't exist, creating it");
10673 if ((f
= fopen(search_file
, "w")) == NULL
)
10674 err(1, "search_history");
10677 history_read(&shl
, search_file
, &search_history_count
);
10680 /* command history */
10681 if (history_autosave
) {
10682 snprintf(command_file
, sizeof command_file
, "%s/%s",
10683 work_dir
, XT_COMMAND_FILE
);
10684 if (stat(command_file
, &sb
)) {
10685 warnx("command history file doesn't exist, creating it");
10686 if ((f
= fopen(command_file
, "w")) == NULL
)
10687 err(1, "command_history");
10690 history_read(&chl
, command_file
, &cmd_history_count
);
10694 session
= webkit_get_default_session();
10699 if (stat(ssl_ca_file
, &sb
)) {
10700 warnx("no CA file: %s", ssl_ca_file
);
10701 g_free(ssl_ca_file
);
10702 ssl_ca_file
= NULL
;
10704 g_object_set(session
,
10705 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10706 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10710 /* guess_search regex */
10711 if (url_regex
== NULL
)
10712 url_regex
= g_strdup(XT_URL_REGEX
);
10714 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10715 startpage_add("invalid url regex %s", url_regex
);
10718 env_proxy
= getenv("http_proxy");
10720 setup_proxy(env_proxy
);
10722 setup_proxy(http_proxy
);
10725 send_cmd_to_socket(argv
[0]);
10729 /* set some connection parameters */
10730 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10731 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10734 /* see if there is already an xxxterm running */
10735 if (single_instance
&& is_running()) {
10737 warnx("already running");
10742 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10743 send_cmd_to_socket(cmd
);
10753 /* uri completion */
10754 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10757 buffers_store
= gtk_list_store_new
10758 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10764 notebook_tab_set_visibility();
10766 if (save_global_history
)
10767 restore_global_history();
10769 /* restore session list */
10770 restore_sessions_list();
10772 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10773 restore_saved_tabs();
10775 a
.s
= named_session
;
10776 a
.i
= XT_SES_DONOTHING
;
10777 open_tabs(NULL
, &a
);
10780 /* see if we have an exception */
10781 if (!TAILQ_EMPTY(&spl
)) {
10782 create_new_tab("about:startpage", NULL
, focus
, -1);
10787 create_new_tab(argv
[0], NULL
, focus
, -1);
10794 if (TAILQ_EMPTY(&tabs
))
10795 create_new_tab(home
, NULL
, 1, -1);
10798 if ((s
= build_socket()) != -1) {
10799 channel
= g_io_channel_unix_new(s
);
10800 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10806 gdk_threads_leave();
10807 g_static_rec_mutex_unlock_full(&my_gdk_mtx
); /* just in case */
10810 gnutls_global_deinit();