2 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
4 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
5 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
6 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
7 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * create privacy browsing
25 * - encrypted local data
42 #include <sys/types.h>
44 #if defined(__linux__)
45 #include "linux/util.h"
46 #include "linux/tree.h"
47 #include <bsd/stdlib.h>
48 # if !defined(sane_libbsd_headers)
49 void arc4random_buf(void *, size_t);
51 #elif defined(__FreeBSD__)
53 #include "freebsd/util.h"
59 #include <sys/queue.h>
60 #include <sys/resource.h>
61 #include <sys/socket.h>
67 #include <gdk/gdkkeysyms.h>
69 #if GTK_CHECK_VERSION(3,0,0)
70 /* we still use GDK_* instead of GDK_KEY_* */
71 #include <gdk/gdkkeysyms-compat.h>
74 #include <webkit/webkit.h>
75 #include <libsoup/soup.h>
76 #include <JavaScriptCore/JavaScript.h>
77 #include <gnutls/gnutls.h>
78 #include <gnutls/x509.h>
81 #include "javascript.h"
83 /* comment if you don't want to use threads */
89 GCRY_THREAD_OPTION_PTHREAD_IMPL
;
94 javascript.h borrowed from vimprobable2 under the following license:
96 Copyright (c) 2009 Leon Winter
97 Copyright (c) 2009 Hannes Schueller
98 Copyright (c) 2009 Matto Fransen
100 Permission is hereby granted, free of charge, to any person obtaining a copy
101 of this software and associated documentation files (the "Software"), to deal
102 in the Software without restriction, including without limitation the rights
103 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
104 copies of the Software, and to permit persons to whom the Software is
105 furnished to do so, subject to the following conditions:
107 The above copyright notice and this permission notice shall be included in
108 all copies or substantial portions of the Software.
110 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
111 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
112 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
113 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
114 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
115 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
119 static char *version
= XXXTERM_VERSION
;
121 /* hooked functions */
122 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
123 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
128 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
129 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
130 #define XT_D_MOVE 0x0001
131 #define XT_D_KEY 0x0002
132 #define XT_D_TAB 0x0004
133 #define XT_D_URL 0x0008
134 #define XT_D_CMD 0x0010
135 #define XT_D_NAV 0x0020
136 #define XT_D_DOWNLOAD 0x0040
137 #define XT_D_CONFIG 0x0080
138 #define XT_D_JS 0x0100
139 #define XT_D_FAVORITE 0x0200
140 #define XT_D_PRINTING 0x0400
141 #define XT_D_COOKIE 0x0800
142 #define XT_D_KEYBINDING 0x1000
143 #define XT_D_CLIP 0x2000
144 #define XT_D_BUFFERCMD 0x4000
145 u_int32_t swm_debug
= 0
163 #define DPRINTF(x...)
164 #define DNPRINTF(n,x...)
167 #define LENGTH(x) (sizeof x / sizeof x[0])
168 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
169 ~(GDK_BUTTON1_MASK) & \
170 ~(GDK_BUTTON2_MASK) & \
171 ~(GDK_BUTTON3_MASK) & \
172 ~(GDK_BUTTON4_MASK) & \
175 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
186 TAILQ_ENTRY(tab
) entry
;
188 GtkWidget
*tab_content
;
197 GtkWidget
*uri_entry
;
198 GtkWidget
*search_entry
;
200 GtkWidget
*browser_win
;
201 GtkWidget
*statusbar_box
;
203 GtkWidget
*statusbar
;
204 GtkWidget
*buffercmd
;
215 GtkWidget
*js_toggle
;
216 GtkEntryCompletion
*completion
;
220 WebKitWebHistoryItem
*item
;
221 WebKitWebBackForwardList
*bfl
;
224 WebKitNetworkRequest
*icon_request
;
225 WebKitDownload
*icon_download
;
226 gchar
*icon_dest_uri
;
228 /* adjustments for browser */
231 GtkAdjustment
*adjust_h
;
232 GtkAdjustment
*adjust_v
;
238 int xtp_meaning
; /* identifies dls/favorites */
240 int popup
; /* 1 if cmd_entry has popup visible */
242 /* https thread stuff */
248 #define XT_HINT_NONE (0)
249 #define XT_HINT_NUMERICAL (1)
250 #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_HINT_NEWTAB (1<<0)
539 #define XT_TABS_NORMAL 0
540 #define XT_TABS_COMPACT 1
542 #define XT_BUFCMD_SZ (8)
550 TAILQ_ENTRY(mime_type
) entry
;
552 TAILQ_HEAD(mime_type_list
, mime_type
);
558 TAILQ_ENTRY(alias
) entry
;
560 TAILQ_HEAD(alias_list
, alias
);
562 /* settings that require restart */
563 int tabless
= 0; /* allow only 1 tab */
564 int enable_socket
= 0;
565 int single_instance
= 0; /* only allow one xxxterm to run */
566 int fancy_bar
= 1; /* fancy toolbar */
567 int browser_mode
= XT_BM_NORMAL
;
568 int enable_localstorage
= 1;
569 char *statusbar_elems
= NULL
;
571 /* runtime settings */
572 int show_tabs
= 1; /* show tabs on notebook */
573 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
574 int show_url
= 1; /* show url toolbar on notebook */
575 int show_statusbar
= 0; /* vimperator style status bar */
576 int ctrl_click_focus
= 0; /* ctrl click gets focus */
577 int cookies_enabled
= 1; /* enable cookies */
578 int read_only_cookies
= 0; /* enable to not write cookies */
579 int enable_scripts
= 1;
580 int enable_plugins
= 1;
581 gfloat default_zoom_level
= 1.0;
582 char default_script
[PATH_MAX
];
583 int window_height
= 768;
584 int window_width
= 1024;
585 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
586 int refresh_interval
= 10; /* download refresh interval */
587 int enable_plugin_whitelist
= 0;
588 int enable_cookie_whitelist
= 0;
589 int enable_js_whitelist
= 0;
590 int session_timeout
= 3600; /* cookie session timeout */
591 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
592 char *ssl_ca_file
= NULL
;
593 char *resource_dir
= NULL
;
594 gboolean ssl_strict_certs
= FALSE
;
595 int append_next
= 1; /* append tab after current tab */
597 char *search_string
= NULL
;
598 char *http_proxy
= NULL
;
599 char download_dir
[PATH_MAX
];
600 char runtime_settings
[PATH_MAX
]; /* override of settings */
601 int allow_volatile_cookies
= 0;
602 int save_global_history
= 0; /* save global history to disk */
603 char *user_agent
= NULL
;
604 int save_rejected_cookies
= 0;
605 int session_autosave
= 0;
606 int guess_search
= 0;
607 int dns_prefetch
= FALSE
;
608 gint max_connections
= 25;
609 gint max_host_connections
= 5;
610 gint enable_spell_checking
= 0;
611 char *spell_check_languages
= NULL
;
612 int xterm_workaround
= 0;
613 char *url_regex
= NULL
;
614 int history_autosave
= 0;
615 char search_file
[PATH_MAX
];
616 char command_file
[PATH_MAX
];
617 char *encoding
= NULL
;
619 char *cmd_font_name
= NULL
;
620 char *oops_font_name
= NULL
;
621 char *statusbar_font_name
= NULL
;
622 char *tabbar_font_name
= NULL
;
623 PangoFontDescription
*cmd_font
;
624 PangoFontDescription
*oops_font
;
625 PangoFontDescription
*statusbar_font
;
626 PangoFontDescription
*tabbar_font
;
627 char *qmarks
[XT_NOMARKS
];
629 int btn_down
; /* M1 down in any wv */
630 regex_t url_re
; /* guess_search regex */
634 int set_browser_mode(struct settings
*, char *);
635 int set_cookie_policy(struct settings
*, char *);
636 int set_download_dir(struct settings
*, char *);
637 int set_default_script(struct settings
*, char *);
638 int set_runtime_dir(struct settings
*, char *);
639 int set_tab_style(struct settings
*, char *);
640 int set_work_dir(struct settings
*, char *);
641 int add_alias(struct settings
*, char *);
642 int add_mime_type(struct settings
*, char *);
643 int add_cookie_wl(struct settings
*, char *);
644 int add_js_wl(struct settings
*, char *);
645 int add_pl_wl(struct settings
*, char *);
646 int add_kb(struct settings
*, char *);
647 void button_set_stockid(GtkWidget
*, char *);
648 GtkWidget
* create_button(char *, char *, int);
650 char *get_browser_mode(struct settings
*);
651 char *get_cookie_policy(struct settings
*);
652 char *get_download_dir(struct settings
*);
653 char *get_default_script(struct settings
*);
654 char *get_runtime_dir(struct settings
*);
655 char *get_tab_style(struct settings
*);
656 char *get_work_dir(struct settings
*);
657 void startpage_add(const char *, ...);
659 void walk_alias(struct settings
*, void (*)(struct settings
*,
660 char *, void *), void *);
661 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
662 char *, void *), void *);
663 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
664 char *, void *), void *);
665 void walk_pl_wl(struct settings
*, void (*)(struct settings
*,
666 char *, void *), void *);
667 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
669 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
670 char *, void *), void *);
672 void recalc_tabs(void);
673 void recolor_compact_tabs(void);
674 void set_current_tab(int page_num
);
675 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
676 void marks_clear(struct tab
*t
);
678 int set_http_proxy(char *);
681 int (*set
)(struct settings
*, char *);
682 char *(*get
)(struct settings
*);
683 void (*walk
)(struct settings
*,
684 void (*cb
)(struct settings
*, char *, void *),
688 struct special s_browser_mode
= {
694 struct special s_cookie
= {
700 struct special s_alias
= {
706 struct special s_mime
= {
712 struct special s_js
= {
718 struct special s_pl
= {
724 struct special s_kb
= {
730 struct special s_cookie_wl
= {
736 struct special s_default_script
= {
742 struct special s_download_dir
= {
748 struct special s_work_dir
= {
754 struct special s_tab_style
= {
763 #define XT_S_INVALID (0)
766 #define XT_S_FLOAT (3)
768 #define XT_SF_RESTART (1<<0)
769 #define XT_SF_RUNTIME (1<<1)
774 int (*activate
)(char *);
776 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
777 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
778 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
779 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
780 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
781 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
782 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
783 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
784 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
785 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
786 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
787 { "enable_plugin_whitelist", XT_S_INT
, 0, &enable_plugin_whitelist
, NULL
, NULL
},
788 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
789 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
790 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
791 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
792 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
793 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
794 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
795 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
796 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
797 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
798 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
799 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
800 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
801 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
802 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
803 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
804 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
805 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
806 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
807 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
808 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
809 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
810 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
811 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
812 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
813 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
814 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
815 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
816 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
817 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
818 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
819 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
820 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
821 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
822 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
823 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
824 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
827 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
828 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
829 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
830 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
832 /* runtime settings */
833 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
834 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
835 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
836 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
837 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
838 { "pl_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_pl
},
841 int about(struct tab
*, struct karg
*);
842 int blank(struct tab
*, struct karg
*);
843 int ca_cmd(struct tab
*, struct karg
*);
844 int cookie_show_wl(struct tab
*, struct karg
*);
845 int js_show_wl(struct tab
*, struct karg
*);
846 int pl_show_wl(struct tab
*, struct karg
*);
847 int help(struct tab
*, struct karg
*);
848 int set(struct tab
*, struct karg
*);
849 int stats(struct tab
*, struct karg
*);
850 int marco(struct tab
*, struct karg
*);
851 int startpage(struct tab
*, struct karg
*);
852 const char * marco_message(int *);
853 int xtp_page_cl(struct tab
*, struct karg
*);
854 int xtp_page_dl(struct tab
*, struct karg
*);
855 int xtp_page_fl(struct tab
*, struct karg
*);
856 int xtp_page_hl(struct tab
*, struct karg
*);
857 void xt_icon_from_file(struct tab
*, char *);
858 const gchar
*get_uri(struct tab
*);
859 const gchar
*get_title(struct tab
*, bool);
861 #define XT_URI_ABOUT ("about:")
862 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
863 #define XT_URI_ABOUT_ABOUT ("about")
864 #define XT_URI_ABOUT_BLANK ("blank")
865 #define XT_URI_ABOUT_CERTS ("certs")
866 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
867 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
868 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
869 #define XT_URI_ABOUT_FAVORITES ("favorites")
870 #define XT_URI_ABOUT_HELP ("help")
871 #define XT_URI_ABOUT_HISTORY ("history")
872 #define XT_URI_ABOUT_JSWL ("jswl")
873 #define XT_URI_ABOUT_PLUGINWL ("plwl")
874 #define XT_URI_ABOUT_SET ("set")
875 #define XT_URI_ABOUT_STATS ("stats")
876 #define XT_URI_ABOUT_MARCO ("marco")
877 #define XT_URI_ABOUT_STARTPAGE ("startpage")
881 int (*func
)(struct tab
*, struct karg
*);
883 { XT_URI_ABOUT_ABOUT
, about
},
884 { XT_URI_ABOUT_BLANK
, blank
},
885 { XT_URI_ABOUT_CERTS
, ca_cmd
},
886 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
887 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
888 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
889 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
890 { XT_URI_ABOUT_HELP
, help
},
891 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
892 { XT_URI_ABOUT_JSWL
, js_show_wl
},
893 { XT_URI_ABOUT_SET
, set
},
894 { XT_URI_ABOUT_STATS
, stats
},
895 { XT_URI_ABOUT_MARCO
, marco
},
896 { XT_URI_ABOUT_STARTPAGE
, startpage
},
897 { XT_URI_ABOUT_PLUGINWL
, pl_show_wl
},
900 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
901 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
902 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
903 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
904 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
905 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
906 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
909 extern char *__progname
;
912 GtkWidget
*main_window
;
913 GtkNotebook
*notebook
;
915 GtkWidget
*arrow
, *abtn
;
916 struct tab_list tabs
;
917 struct history_list hl
;
918 struct session_list sessions
;
919 struct download_list downloads
;
920 struct domain_list c_wl
;
921 struct domain_list js_wl
;
922 struct domain_list pl_wl
;
923 struct undo_tailq undos
;
924 struct keybinding_list kbl
;
926 struct command_list chl
;
927 struct command_list shl
;
928 struct command_entry
*history_at
;
929 struct command_entry
*search_at
;
931 int updating_dl_tabs
= 0;
932 int updating_hl_tabs
= 0;
933 int updating_cl_tabs
= 0;
934 int updating_fl_tabs
= 0;
935 int cmd_history_count
= 0;
936 int search_history_count
= 0;
938 long long unsigned int blocked_cookies
= 0;
939 char named_session
[PATH_MAX
];
940 GtkListStore
*completion_model
;
941 GtkListStore
*buffers_store
;
943 void xxx_dir(char *);
944 int icon_size_map(int);
945 void completion_add(struct tab
*);
946 void completion_add_uri(const gchar
*);
947 void show_oops(struct tab
*, const char *, ...);
950 history_delete(struct command_list
*l
, int *counter
)
952 struct command_entry
*c
;
954 if (l
== NULL
|| counter
== NULL
)
957 c
= TAILQ_LAST(l
, command_list
);
961 TAILQ_REMOVE(l
, c
, entry
);
968 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
970 struct command_entry
*c
;
973 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
976 /* don't add the same line */
977 c
= TAILQ_FIRST(list
);
979 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
982 c
= g_malloc0(sizeof *c
);
983 c
->line
= g_strdup_printf(" %s", l
);
986 TAILQ_INSERT_HEAD(list
, c
, entry
);
989 history_delete(list
, counter
);
991 if (history_autosave
&& file
) {
992 f
= fopen(file
, "w");
994 show_oops(NULL
, "couldn't write history %s", file
);
998 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
1000 fprintf(f
, "%s\n", c
->line
);
1008 history_read(struct command_list
*list
, char *file
, int *counter
)
1011 char *s
, line
[65536];
1013 if (list
== NULL
|| file
== NULL
)
1016 f
= fopen(file
, "r");
1018 startpage_add("couldn't open history file %s", file
);
1023 s
= fgets(line
, sizeof line
, f
);
1024 if (s
== NULL
|| feof(f
) || ferror(f
))
1026 if ((s
= strchr(line
, '\n')) == NULL
) {
1027 startpage_add("invalid history file %s", file
);
1033 history_add(list
, NULL
, line
+ 1, counter
);
1041 /* marks and quickmarks array storage.
1042 * first a-z, then A-Z, then 0-9 */
1049 if (i
>= 0 && i
<= 'z' - 'a')
1053 if (i
>= 0 && i
<= 'Z' - 'A')
1068 if (m
>= 'a' && m
<= 'z')
1069 return ret
+ m
- 'a';
1071 ret
+= 'z' - 'a' + 1;
1072 if (m
>= 'A' && m
<= 'Z')
1073 return ret
+ m
- 'A';
1075 ret
+= 'Z' - 'A' + 1;
1076 if (m
>= '0' && m
<= '9')
1077 return ret
+ m
- '0';
1086 int saved_errno
, status
;
1089 saved_errno
= errno
;
1091 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1095 if (errno
!= ECHILD
) {
1097 clog_warn("sigchild: waitpid:");
1103 if (WIFEXITED(status
)) {
1104 if (WEXITSTATUS(status
) != 0) {
1106 clog_warnx("sigchild: child exit status: %d",
1107 WEXITSTATUS(status));
1112 clog_warnx("sigchild: child is terminated abnormally");
1117 errno
= saved_errno
;
1121 is_g_object_setting(GObject
*o
, char *str
)
1123 guint n_props
= 0, i
;
1124 GParamSpec
**proplist
;
1126 if (! G_IS_OBJECT(o
))
1129 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1132 for (i
=0; i
< n_props
; i
++) {
1133 if (! strcmp(proplist
[i
]->name
, str
))
1140 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1144 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1146 "<title>%s</title>\n"
1155 addstyles
? XT_PAGE_STYLE
: "",
1164 * Display a web page from a HTML string in memory, rather than from a URL
1167 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1169 char file
[PATH_MAX
];
1172 /* we set this to indicate we want to manually do navaction */
1174 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1176 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1178 /* set t->xtp_meaning */
1179 for (i
= 0; i
< LENGTH(about_list
); i
++)
1180 if (!strcmp(title
, about_list
[i
].name
)) {
1185 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
1187 #if GTK_CHECK_VERSION(2, 20, 0)
1188 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1189 gtk_widget_hide(t
->spinner
);
1191 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1192 xt_icon_from_file(t
, file
);
1197 get_current_tab(void)
1201 TAILQ_FOREACH(t
, &tabs
, entry
) {
1202 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1206 warnx("%s: no current tab", __func__
);
1212 set_status(struct tab
*t
, gchar
*s
, int status
)
1220 case XT_STATUS_LOADING
:
1221 type
= g_strdup_printf("Loading: %s", s
);
1224 case XT_STATUS_LINK
:
1225 type
= g_strdup_printf("Link: %s", s
);
1227 t
->status
= g_strdup(gtk_entry_get_text(
1228 GTK_ENTRY(t
->sbe
.statusbar
)));
1232 type
= g_strdup_printf("%s", s
);
1234 t
->status
= g_strdup(type
);
1238 t
->status
= g_strdup(s
);
1240 case XT_STATUS_NOTHING
:
1245 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1251 hide_cmd(struct tab
*t
)
1253 history_at
= NULL
; /* just in case */
1254 search_at
= NULL
; /* just in case */
1255 gtk_widget_hide(t
->cmd
);
1259 show_cmd(struct tab
*t
)
1263 gtk_widget_hide(t
->oops
);
1264 gtk_widget_show(t
->cmd
);
1268 hide_buffers(struct tab
*t
)
1270 gtk_widget_hide(t
->buffers
);
1271 gtk_list_store_clear(buffers_store
);
1281 sort_tabs_by_page_num(struct tab
***stabs
)
1286 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1288 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1290 TAILQ_FOREACH(t
, &tabs
, entry
)
1291 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1297 buffers_make_list(void)
1300 const gchar
*title
= NULL
;
1302 struct tab
**stabs
= NULL
;
1304 num_tabs
= sort_tabs_by_page_num(&stabs
);
1306 for (i
= 0; i
< num_tabs
; i
++)
1308 gtk_list_store_append(buffers_store
, &iter
);
1309 title
= get_title(stabs
[i
], FALSE
);
1310 gtk_list_store_set(buffers_store
, &iter
,
1311 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1321 show_buffers(struct tab
*t
)
1323 buffers_make_list();
1324 gtk_widget_show(t
->buffers
);
1325 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1329 toggle_buffers(struct tab
*t
)
1331 if (gtk_widget_get_visible(t
->buffers
))
1338 buffers(struct tab
*t
, struct karg
*args
)
1346 hide_oops(struct tab
*t
)
1348 gtk_widget_hide(t
->oops
);
1352 show_oops(struct tab
*at
, const char *fmt
, ...)
1356 struct tab
*t
= NULL
;
1362 if ((t
= get_current_tab()) == NULL
)
1368 if (vasprintf(&msg
, fmt
, ap
) == -1)
1369 errx(1, "show_oops failed");
1372 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1373 gtk_widget_hide(t
->cmd
);
1374 gtk_widget_show(t
->oops
);
1381 get_as_string(struct settings
*s
)
1392 warnx("get_as_string skip %s\n", s
->name
);
1393 } else if (s
->type
== XT_S_INT
)
1394 r
= g_strdup_printf("%d", *s
->ival
);
1395 else if (s
->type
== XT_S_STR
)
1396 r
= g_strdup(*s
->sval
);
1397 else if (s
->type
== XT_S_FLOAT
)
1398 r
= g_strdup_printf("%f", *s
->fval
);
1400 r
= g_strdup_printf("INVALID TYPE");
1406 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1411 for (i
= 0; i
< LENGTH(rs
); i
++) {
1412 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1413 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1415 s
= get_as_string(&rs
[i
]);
1416 cb(&rs
[i
], s
, cb_args
);
1423 set_browser_mode(struct settings
*s
, char *val
)
1425 if (!strcmp(val
, "whitelist")) {
1426 browser_mode
= XT_BM_WHITELIST
;
1427 allow_volatile_cookies
= 0;
1428 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1429 cookies_enabled
= 1;
1430 enable_cookie_whitelist
= 1;
1431 enable_plugin_whitelist
= 1;
1433 read_only_cookies
= 0;
1434 save_rejected_cookies
= 0;
1435 session_timeout
= 3600;
1437 enable_js_whitelist
= 1;
1438 enable_localstorage
= 0;
1439 } else if (!strcmp(val
, "normal")) {
1440 browser_mode
= XT_BM_NORMAL
;
1441 allow_volatile_cookies
= 0;
1442 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1443 cookies_enabled
= 1;
1444 enable_cookie_whitelist
= 0;
1445 enable_plugin_whitelist
= 0;
1447 read_only_cookies
= 0;
1448 save_rejected_cookies
= 0;
1449 session_timeout
= 3600;
1451 enable_js_whitelist
= 0;
1452 enable_localstorage
= 1;
1453 } else if (!strcmp(val
, "kiosk")) {
1454 browser_mode
= XT_BM_KIOSK
;
1455 allow_volatile_cookies
= 0;
1456 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1457 cookies_enabled
= 1;
1458 enable_cookie_whitelist
= 0;
1459 enable_plugin_whitelist
= 0;
1461 read_only_cookies
= 0;
1462 save_rejected_cookies
= 0;
1463 session_timeout
= 3600;
1465 enable_js_whitelist
= 0;
1466 enable_localstorage
= 1;
1476 get_browser_mode(struct settings
*s
)
1480 if (browser_mode
== XT_BM_WHITELIST
)
1481 r
= g_strdup("whitelist");
1482 else if (browser_mode
== XT_BM_NORMAL
)
1483 r
= g_strdup("normal");
1484 else if (browser_mode
== XT_BM_KIOSK
)
1485 r
= g_strdup("kiosk");
1493 set_cookie_policy(struct settings
*s
, char *val
)
1495 if (!strcmp(val
, "no3rdparty"))
1496 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1497 else if (!strcmp(val
, "accept"))
1498 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1499 else if (!strcmp(val
, "reject"))
1500 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1508 get_cookie_policy(struct settings
*s
)
1512 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1513 r
= g_strdup("no3rdparty");
1514 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1515 r
= g_strdup("accept");
1516 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1517 r
= g_strdup("reject");
1525 get_default_script(struct settings
*s
)
1527 if (default_script
[0] == '\0')
1529 return (g_strdup(default_script
));
1533 set_default_script(struct settings
*s
, char *val
)
1536 snprintf(default_script
, sizeof default_script
, "%s/%s",
1537 pwd
->pw_dir
, &val
[1]);
1539 strlcpy(default_script
, val
, sizeof default_script
);
1545 get_download_dir(struct settings
*s
)
1547 if (download_dir
[0] == '\0')
1549 return (g_strdup(download_dir
));
1553 set_download_dir(struct settings
*s
, char *val
)
1556 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1557 pwd
->pw_dir
, &val
[1]);
1559 strlcpy(download_dir
, val
, sizeof download_dir
);
1566 * We use these to prevent people putting xxxt:// URLs on
1567 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1569 #define XT_XTP_SES_KEY_SZ 8
1570 #define XT_XTP_SES_KEY_HEX_FMT \
1571 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1572 char *dl_session_key
; /* downloads */
1573 char *hl_session_key
; /* history list */
1574 char *cl_session_key
; /* cookie list */
1575 char *fl_session_key
; /* favorites list */
1577 char work_dir
[PATH_MAX
];
1578 char certs_dir
[PATH_MAX
];
1579 char cache_dir
[PATH_MAX
];
1580 char sessions_dir
[PATH_MAX
];
1581 char cookie_file
[PATH_MAX
];
1582 SoupURI
*proxy_uri
= NULL
;
1583 SoupSession
*session
;
1584 SoupCookieJar
*s_cookiejar
;
1585 SoupCookieJar
*p_cookiejar
;
1586 char rc_fname
[PATH_MAX
];
1588 struct mime_type_list mtl
;
1589 struct alias_list aliases
;
1592 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1593 void delete_tab(struct tab
*);
1594 void setzoom_webkit(struct tab
*, int);
1595 int run_script(struct tab
*, char *);
1596 int download_rb_cmp(struct download
*, struct download
*);
1597 gboolean
cmd_execute(struct tab
*t
, char *str
);
1600 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1602 return (strcmp(h1
->uri
, h2
->uri
));
1604 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1607 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1609 return (strcmp(d1
->d
, d2
->d
));
1611 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1614 get_work_dir(struct settings
*s
)
1616 if (work_dir
[0] == '\0')
1618 return (g_strdup(work_dir
));
1622 set_work_dir(struct settings
*s
, char *val
)
1625 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1626 pwd
->pw_dir
, &val
[1]);
1628 strlcpy(work_dir
, val
, sizeof work_dir
);
1634 get_tab_style(struct settings
*s
)
1636 if (tab_style
== XT_TABS_NORMAL
)
1637 return (g_strdup("normal"));
1639 return (g_strdup("compact"));
1643 set_tab_style(struct settings
*s
, char *val
)
1645 if (!strcmp(val
, "normal"))
1646 tab_style
= XT_TABS_NORMAL
;
1647 else if (!strcmp(val
, "compact"))
1648 tab_style
= XT_TABS_COMPACT
;
1656 * generate a session key to secure xtp commands.
1657 * pass in a ptr to the key in question and it will
1658 * be modified in place.
1661 generate_xtp_session_key(char **key
)
1663 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1669 /* make a new one */
1670 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1671 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1672 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1673 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1675 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1679 * validate a xtp session key.
1683 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1685 if (strcmp(trusted
, untrusted
) != 0) {
1686 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1695 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1697 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1699 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1701 struct valid_url_types
{
1712 valid_url_type(char *url
)
1716 for (i
= 0; i
< LENGTH(vut
); i
++)
1717 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1724 print_cookie(char *msg
, SoupCookie
*c
)
1730 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1731 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1732 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1733 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1734 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1735 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1736 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1737 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1738 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1739 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1743 walk_alias(struct settings
*s
,
1744 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1749 if (s
== NULL
|| cb
== NULL
) {
1750 show_oops(NULL
, "walk_alias invalid parameters");
1754 TAILQ_FOREACH(a
, &aliases
, entry
) {
1755 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1756 cb(s
, str
, cb_args
);
1762 match_alias(char *url_in
)
1766 char *url_out
= NULL
, *search
, *enc_arg
;
1768 search
= g_strdup(url_in
);
1770 if (strsep(&arg
, " \t") == NULL
) {
1771 show_oops(NULL
, "match_alias: NULL URL");
1775 TAILQ_FOREACH(a
, &aliases
, entry
) {
1776 if (!strcmp(search
, a
->a_name
))
1781 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1784 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1785 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1788 url_out
= g_strdup_printf(a
->a_uri
, "");
1796 guess_url_type(char *url_in
)
1799 char *url_out
= NULL
, *enc_search
= NULL
;
1802 /* substitute aliases */
1803 url_out
= match_alias(url_in
);
1804 if (url_out
!= NULL
)
1807 /* see if we are an about page */
1808 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1809 for (i
= 0; i
< LENGTH(about_list
); i
++)
1810 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1811 about_list
[i
].name
)) {
1812 url_out
= g_strdup(url_in
);
1816 if (guess_search
&& url_regex
&&
1817 !(g_str_has_prefix(url_in
, "http://") ||
1818 g_str_has_prefix(url_in
, "https://"))) {
1819 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1820 /* invalid URI so search instead */
1821 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1822 url_out
= g_strdup_printf(search_string
, enc_search
);
1828 /* XXX not sure about this heuristic */
1829 if (stat(url_in
, &sb
) == 0)
1830 url_out
= g_strdup_printf("file://%s", url_in
);
1832 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1834 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1840 load_uri(struct tab
*t
, gchar
*uri
)
1843 gchar
*newuri
= NULL
;
1849 /* Strip leading spaces. */
1850 while (*uri
&& isspace(*uri
))
1853 if (strlen(uri
) == 0) {
1858 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1860 if (valid_url_type(uri
)) {
1861 newuri
= guess_url_type(uri
);
1865 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1866 for (i
= 0; i
< LENGTH(about_list
); i
++)
1867 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1868 bzero(&args
, sizeof args
);
1869 about_list
[i
].func(t
, &args
);
1870 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1874 show_oops(t
, "invalid about page");
1878 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1880 webkit_web_view_load_uri(t
->wv
, uri
);
1887 get_uri(struct tab
*t
)
1889 const gchar
*uri
= NULL
;
1891 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1893 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1894 uri
= webkit_web_view_get_uri(t
->wv
);
1896 /* use tmp_uri to make sure it is g_freed */
1899 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1900 about_list
[t
->xtp_meaning
].name
);
1907 get_title(struct tab
*t
, bool window
)
1909 const gchar
*set
= NULL
, *title
= NULL
;
1910 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1912 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1913 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1916 title
= webkit_web_view_get_title(t
->wv
);
1917 if ((set
= title
? title
: get_uri(t
)))
1921 set
= window
? XT_NAME
: "(untitled)";
1927 add_alias(struct settings
*s
, char *line
)
1930 struct alias
*a
= NULL
;
1932 if (s
== NULL
|| line
== NULL
) {
1933 show_oops(NULL
, "add_alias invalid parameters");
1938 a
= g_malloc(sizeof(*a
));
1940 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1941 show_oops(NULL
, "add_alias: incomplete alias definition");
1944 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1945 show_oops(NULL
, "add_alias: invalid alias definition");
1949 a
->a_name
= g_strdup(alias
);
1950 a
->a_uri
= g_strdup(l
);
1952 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1954 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1964 add_mime_type(struct settings
*s
, char *line
)
1968 struct mime_type
*m
= NULL
;
1969 int downloadfirst
= 0;
1971 /* XXX this could be smarter */
1973 if (line
== NULL
|| strlen(line
) == 0) {
1974 show_oops(NULL
, "add_mime_type invalid parameters");
1983 m
= g_malloc(sizeof(*m
));
1985 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1986 show_oops(NULL
, "add_mime_type: invalid mime_type");
1989 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1990 mime_type
[strlen(mime_type
) - 1] = '\0';
1995 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1996 show_oops(NULL
, "add_mime_type: invalid mime_type");
2000 m
->mt_type
= g_strdup(mime_type
);
2001 m
->mt_action
= g_strdup(l
);
2002 m
->mt_download
= downloadfirst
;
2004 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
2005 m
->mt_type
, m
->mt_action
, m
->mt_default
);
2007 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
2017 find_mime_type(char *mime_type
)
2019 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
2021 TAILQ_FOREACH(m
, &mtl
, entry
) {
2022 if (m
->mt_default
&&
2023 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
2026 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
2039 walk_mime_type(struct settings
*s
,
2040 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2042 struct mime_type
*m
;
2045 if (s
== NULL
|| cb
== NULL
) {
2046 show_oops(NULL
, "walk_mime_type invalid parameters");
2050 TAILQ_FOREACH(m
, &mtl
, entry
) {
2051 str
= g_strdup_printf("%s%s --> %s",
2053 m
->mt_default
? "*" : "",
2055 cb(s
, str
, cb_args
);
2061 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2067 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2070 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2072 /* treat *.moo.com the same as .moo.com */
2073 if (str
[0] == '*' && str
[1] == '.')
2075 else if (str
[0] == '.')
2080 /* slice off port number */
2081 p
= g_strrstr(str
, ":");
2085 d
= g_malloc(sizeof *d
);
2087 d
->d
= g_strdup_printf(".%s", str
);
2089 d
->d
= g_strdup(str
);
2092 if (RB_INSERT(domain_list
, wl
, d
))
2095 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2106 add_cookie_wl(struct settings
*s
, char *entry
)
2108 wl_add(entry
, &c_wl
, 1);
2113 walk_cookie_wl(struct settings
*s
,
2114 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2118 if (s
== NULL
|| cb
== NULL
) {
2119 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2123 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2124 cb(s
, d
->d
, cb_args
);
2128 walk_js_wl(struct settings
*s
,
2129 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2133 if (s
== NULL
|| cb
== NULL
) {
2134 show_oops(NULL
, "walk_js_wl invalid parameters");
2138 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2139 cb(s
, d
->d
, cb_args
);
2143 add_js_wl(struct settings
*s
, char *entry
)
2145 wl_add(entry
, &js_wl
, 1 /* persistent */);
2150 walk_pl_wl(struct settings
*s
,
2151 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2155 if (s
== NULL
|| cb
== NULL
) {
2156 show_oops(NULL
, "walk_pl_wl invalid parameters");
2160 RB_FOREACH_REVERSE(d
, domain_list
, &pl_wl
)
2161 cb(s
, d
->d
, cb_args
);
2165 add_pl_wl(struct settings
*s
, char *entry
)
2167 wl_add(entry
, &pl_wl
, 1 /* persistent */);
2172 wl_find(const gchar
*search
, struct domain_list
*wl
)
2175 struct domain
*d
= NULL
, dfind
;
2178 if (search
== NULL
|| wl
== NULL
)
2180 if (strlen(search
) < 2)
2183 if (search
[0] != '.')
2184 s
= g_strdup_printf(".%s", search
);
2186 s
= g_strdup(search
);
2188 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2191 d
= RB_FIND(domain_list
, wl
, &dfind
);
2205 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2211 if (s
== NULL
|| wl
== NULL
)
2214 if (!strncmp(s
, "http://", strlen("http://")))
2215 s
= &s
[strlen("http://")];
2216 else if (!strncmp(s
, "https://", strlen("https://")))
2217 s
= &s
[strlen("https://")];
2222 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2223 /* chop string at first slash */
2224 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2227 r
= wl_find(ss
, wl
);
2236 settings_add(char *var
, char *val
)
2243 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2244 if (strcmp(var
, rs
[i
].name
))
2248 if (rs
[i
].s
->set(&rs
[i
], val
))
2249 errx(1, "invalid value for %s: %s", var
, val
);
2253 switch (rs
[i
].type
) {
2262 errx(1, "invalid sval for %s",
2276 errx(1, "invalid type for %s", var
);
2285 config_parse(char *filename
, int runtime
)
2288 char *line
, *cp
, *var
, *val
;
2289 size_t len
, lineno
= 0;
2291 char file
[PATH_MAX
];
2294 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2296 if (filename
== NULL
)
2299 if (runtime
&& runtime_settings
[0] != '\0') {
2300 snprintf(file
, sizeof file
, "%s/%s",
2301 work_dir
, runtime_settings
);
2302 if (stat(file
, &sb
)) {
2303 warnx("runtime file doesn't exist, creating it");
2304 if ((f
= fopen(file
, "w")) == NULL
)
2306 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2310 strlcpy(file
, filename
, sizeof file
);
2312 if ((config
= fopen(file
, "r")) == NULL
) {
2313 warn("config_parse: cannot open %s", filename
);
2318 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2319 if (feof(config
) || ferror(config
))
2323 cp
+= (long)strspn(cp
, WS
);
2324 if (cp
[0] == '\0') {
2330 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2331 startpage_add("invalid configuration file entry: %s",
2334 cp
+= (long)strspn(cp
, WS
);
2336 if ((val
= strsep(&cp
, "\0")) == NULL
)
2339 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",
2341 handled
= settings_add(var
, val
);
2344 startpage_add("invalid configuration file entry"
2345 ": %s=%s", var
, val
);
2355 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2361 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2365 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2368 JSStringGetUTF8CString(jsref
, s
, l
);
2369 JSStringRelease(jsref
);
2375 disable_hints(struct tab
*t
)
2377 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2378 bzero(t
->hint_num
, sizeof t
->hint_num
);
2379 run_script(t
, "vimprobable_clear()");
2381 t
->hint_mode
= XT_HINT_NONE
;
2386 enable_hints(struct tab
*t
)
2388 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2389 run_script(t
, "vimprobable_show_hints()");
2391 t
->hint_mode
= XT_HINT_NONE
;
2394 #define XT_JS_OPEN ("open;")
2395 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2396 #define XT_JS_FIRE ("fire;")
2397 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2398 #define XT_JS_FOUND ("found;")
2399 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2402 run_script(struct tab
*t
, char *s
)
2404 JSGlobalContextRef ctx
;
2405 WebKitWebFrame
*frame
;
2407 JSValueRef val
, exception
;
2410 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2411 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2413 frame
= webkit_web_view_get_main_frame(t
->wv
);
2414 ctx
= webkit_web_frame_get_global_context(frame
);
2416 str
= JSStringCreateWithUTF8CString(s
);
2417 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2418 NULL
, 0, &exception
);
2419 JSStringRelease(str
);
2421 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2423 es
= js_ref_to_string(ctx
, exception
);
2424 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2428 /* if set open in new tab */
2432 es
= js_ref_to_string(ctx
, val
);
2433 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2435 /* handle return value right here */
2436 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2439 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2442 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2443 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2444 &es
[XT_JS_FIRE_LEN
]);
2449 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2450 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2461 hint(struct tab
*t
, struct karg
*args
)
2464 DNPRINTF(XT_D_JS
, "hint: tab %d args %d\n", t
->tab_id
, args
->i
);
2466 if (args
->i
== XT_HINT_NEWTAB
)
2469 if (t
->hints_on
== 0)
2478 apply_style(struct tab
*t
)
2480 g_object_set(G_OBJECT(t
->settings
),
2481 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2485 userstyle(struct tab
*t
, struct karg
*args
)
2487 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2491 g_object_set(G_OBJECT(t
->settings
),
2492 "user-stylesheet-uri", NULL
, (char *)NULL
);
2501 * Doesn't work fully, due to the following bug:
2502 * https://bugs.webkit.org/show_bug.cgi?id=51747
2505 restore_global_history(void)
2507 char file
[PATH_MAX
];
2512 const char delim
[3] = {'\\', '\\', '\0'};
2514 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2516 if ((f
= fopen(file
, "r")) == NULL
) {
2517 warnx("%s: fopen", __func__
);
2522 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2523 if (feof(f
) || ferror(f
))
2526 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2527 if (feof(f
) || ferror(f
)) {
2529 warnx("%s: broken history file\n", __func__
);
2533 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2534 webkit_web_history_item_new_with_data(uri
, title
);
2535 h
= g_malloc(sizeof(struct history
));
2536 h
->uri
= g_strdup(uri
);
2537 h
->title
= g_strdup(title
);
2538 RB_INSERT(history_list
, &hl
, h
);
2539 completion_add_uri(h
->uri
);
2541 warnx("%s: failed to restore history\n", __func__
);
2557 save_global_history_to_disk(struct tab
*t
)
2559 char file
[PATH_MAX
];
2563 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2565 if ((f
= fopen(file
, "w")) == NULL
) {
2566 show_oops(t
, "%s: global history file: %s",
2567 __func__
, strerror(errno
));
2571 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2572 if (h
->uri
&& h
->title
)
2573 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2582 quit(struct tab
*t
, struct karg
*args
)
2584 if (save_global_history
)
2585 save_global_history_to_disk(t
);
2593 restore_sessions_list(void)
2596 struct dirent
*dp
= NULL
;
2599 sdir
= opendir(sessions_dir
);
2601 while ((dp
= readdir(sdir
)) != NULL
)
2602 if (dp
->d_type
== DT_REG
) {
2603 s
= g_malloc(sizeof(struct session
));
2604 s
->name
= g_strdup(dp
->d_name
);
2605 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2612 open_tabs(struct tab
*t
, struct karg
*a
)
2614 char file
[PATH_MAX
];
2618 struct tab
*ti
, *tt
;
2623 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2624 if ((f
= fopen(file
, "r")) == NULL
)
2627 ti
= TAILQ_LAST(&tabs
, tab_list
);
2630 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2631 if (feof(f
) || ferror(f
))
2634 /* retrieve session name */
2635 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2636 strlcpy(named_session
,
2637 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2638 sizeof named_session
);
2642 if (uri
&& strlen(uri
))
2643 create_new_tab(uri
, NULL
, 1, -1);
2649 /* close open tabs */
2650 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2652 tt
= TAILQ_FIRST(&tabs
);
2672 restore_saved_tabs(void)
2674 char file
[PATH_MAX
];
2675 int unlink_file
= 0;
2680 snprintf(file
, sizeof file
, "%s/%s",
2681 sessions_dir
, XT_RESTART_TABS_FILE
);
2682 if (stat(file
, &sb
) == -1)
2683 a
.s
= XT_SAVED_TABS_FILE
;
2686 a
.s
= XT_RESTART_TABS_FILE
;
2689 a
.i
= XT_SES_DONOTHING
;
2690 rv
= open_tabs(NULL
, &a
);
2699 save_tabs(struct tab
*t
, struct karg
*a
)
2701 char file
[PATH_MAX
];
2703 int num_tabs
= 0, i
;
2704 struct tab
**stabs
= NULL
;
2709 snprintf(file
, sizeof file
, "%s/%s",
2710 sessions_dir
, named_session
);
2712 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2714 if ((f
= fopen(file
, "w")) == NULL
) {
2715 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2719 /* save session name */
2720 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2722 /* Save tabs, in the order they are arranged in the notebook. */
2723 num_tabs
= sort_tabs_by_page_num(&stabs
);
2725 for (i
= 0; i
< num_tabs
; i
++)
2727 if (get_uri(stabs
[i
]) != NULL
)
2728 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2729 else if (gtk_entry_get_text(GTK_ENTRY(
2730 stabs
[i
]->uri_entry
)))
2731 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2732 stabs
[i
]->uri_entry
)));
2737 /* try and make sure this gets to disk NOW. XXX Backup first? */
2738 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2739 show_oops(t
, "May not have managed to save session: %s",
2749 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2761 run_page_script(struct tab
*t
, struct karg
*args
)
2764 char *tmp
, script
[PATH_MAX
];
2766 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2767 if (tmp
[0] == '\0') {
2768 show_oops(t
, "no script specified");
2772 if ((uri
= get_uri(t
)) == NULL
) {
2773 show_oops(t
, "tab is empty, not running script");
2778 snprintf(script
, sizeof script
, "%s/%s",
2779 pwd
->pw_dir
, &tmp
[1]);
2781 strlcpy(script
, tmp
, sizeof script
);
2785 show_oops(t
, "can't fork to run script");
2795 execlp(script
, script
, uri
, (void *)NULL
);
2805 yank_uri(struct tab
*t
, struct karg
*args
)
2808 GtkClipboard
*clipboard
;
2810 if ((uri
= get_uri(t
)) == NULL
)
2813 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2814 gtk_clipboard_set_text(clipboard
, uri
, -1);
2820 paste_uri(struct tab
*t
, struct karg
*args
)
2822 GtkClipboard
*clipboard
;
2823 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2825 gchar
*p
= NULL
, *uri
;
2827 /* try primary clipboard first */
2828 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2829 p
= gtk_clipboard_wait_for_text(clipboard
);
2831 /* if it failed get whatever text is in cut_buffer0 */
2832 if (p
== NULL
&& xterm_workaround
)
2833 if (gdk_property_get(gdk_get_default_root_window(),
2835 gdk_atom_intern("STRING", FALSE
),
2837 1024 * 1024 /* picked out of my butt */,
2843 /* yes sir, we need to NUL the string */
2849 while (*uri
&& isspace(*uri
))
2851 if (strlen(uri
) == 0) {
2852 show_oops(t
, "empty paste buffer");
2855 if (guess_search
== 0 && valid_url_type(uri
)) {
2856 /* we can be clever and paste this in search box */
2857 show_oops(t
, "not a valid URL");
2861 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2863 else if (args
->i
== XT_PASTE_NEW_TAB
)
2864 create_new_tab(uri
, NULL
, 1, -1);
2875 find_domain(const gchar
*s
, int toplevel
)
2883 uri
= soup_uri_new(s
);
2885 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2889 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2890 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2891 while(--p
>= uri
->host
&& *p
!= '.');
2898 ret
= g_strdup_printf(".%s", p
);
2906 toggle_cwl(struct tab
*t
, struct karg
*args
)
2917 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2919 if (uri
== NULL
|| dom
== NULL
||
2920 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2921 show_oops(t
, "Can't toggle domain in cookie white list");
2924 d
= wl_find(dom
, &c_wl
);
2931 if (args
->i
& XT_WL_TOGGLE
)
2933 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2935 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2939 /* enable cookies for domain */
2940 wl_add(dom
, &c_wl
, 0);
2942 /* disable cookies for domain */
2944 RB_REMOVE(domain_list
, &c_wl
, d
);
2947 if (args
->i
& XT_WL_RELOAD
)
2948 webkit_web_view_reload(t
->wv
);
2956 toggle_js(struct tab
*t
, struct karg
*args
)
2966 g_object_get(G_OBJECT(t
->settings
),
2967 "enable-scripts", &es
, (char *)NULL
);
2968 if (args
->i
& XT_WL_TOGGLE
)
2970 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2972 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2978 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2980 if (uri
== NULL
|| dom
== NULL
||
2981 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2982 show_oops(t
, "Can't toggle domain in JavaScript white list");
2987 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2988 wl_add(dom
, &js_wl
, 0 /* session */);
2990 d
= wl_find(dom
, &js_wl
);
2992 RB_REMOVE(domain_list
, &js_wl
, d
);
2993 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2995 g_object_set(G_OBJECT(t
->settings
),
2996 "enable-scripts", es
, (char *)NULL
);
2997 g_object_set(G_OBJECT(t
->settings
),
2998 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2999 webkit_web_view_set_settings(t
->wv
, t
->settings
);
3001 if (args
->i
& XT_WL_RELOAD
)
3002 webkit_web_view_reload(t
->wv
);
3010 toggle_pl(struct tab
*t
, struct karg
*args
)
3020 g_object_get(G_OBJECT(t
->settings
),
3021 "enable-plugins", &es
, (char *)NULL
);
3022 if (args
->i
& XT_WL_TOGGLE
)
3024 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
3026 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
3032 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3034 if (uri
== NULL
|| dom
== NULL
||
3035 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3036 show_oops(t
, "Can't toggle domain in plugins white list");
3041 wl_add(dom
, &pl_wl
, 0 /* session */);
3043 d
= wl_find(dom
, &pl_wl
);
3045 RB_REMOVE(domain_list
, &pl_wl
, d
);
3047 g_object_set(G_OBJECT(t
->settings
),
3048 "enable-plugins", es
, (char *)NULL
);
3049 webkit_web_view_set_settings(t
->wv
, t
->settings
);
3051 if (args
->i
& XT_WL_RELOAD
)
3052 webkit_web_view_reload(t
->wv
);
3060 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
3065 g_object_get(G_OBJECT(t
->settings
),
3066 "enable-scripts", &es
, (char *)NULL
);
3071 set
= XT_WL_DISABLE
;
3073 a
.i
= set
| XT_WL_TOPLEVEL
;
3076 a
.i
= set
| XT_WL_TOPLEVEL
;
3079 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
3084 toggle_src(struct tab
*t
, struct karg
*args
)
3091 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
3092 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
3093 webkit_web_view_reload(t
->wv
);
3099 focus_webview(struct tab
*t
)
3104 /* only grab focus if we are visible */
3105 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
3106 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
3110 focus(struct tab
*t
, struct karg
*args
)
3112 if (t
== NULL
|| args
== NULL
)
3118 if (args
->i
== XT_FOCUS_URI
)
3119 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
3120 else if (args
->i
== XT_FOCUS_SEARCH
)
3121 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
3127 stats(struct tab
*t
, struct karg
*args
)
3129 char *page
, *body
, *s
, line
[64 * 1024];
3130 long long unsigned int line_count
= 0;
3134 show_oops(NULL
, "stats invalid parameters");
3137 if (save_rejected_cookies
) {
3138 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
3140 s
= fgets(line
, sizeof line
, r_cookie_f
);
3141 if (s
== NULL
|| feof(r_cookie_f
) ||
3147 snprintf(line
, sizeof line
,
3148 "<br/>Cookies blocked(*) total: %llu", line_count
);
3150 show_oops(t
, "Can't open blocked cookies file: %s",
3154 body
= g_strdup_printf(
3155 "Cookies blocked(*) this session: %llu"
3157 "<p><small><b>*</b> results vary based on settings</small></p>",
3161 page
= get_html_page("Statistics", body
, "", 0);
3164 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3171 marco(struct tab
*t
, struct karg
*args
)
3173 char *page
, line
[64 * 1024];
3177 show_oops(NULL
, "marco invalid parameters");
3180 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3182 page
= get_html_page("Marco Sez...", line
, "", 0);
3184 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3191 blank(struct tab
*t
, struct karg
*args
)
3194 show_oops(NULL
, "blank invalid parameters");
3196 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3202 about(struct tab
*t
, struct karg
*args
)
3207 show_oops(NULL
, "about invalid parameters");
3209 body
= g_strdup_printf("<b>Version: %s</b>"
3210 #ifdef XXXTERM_BUILDSTR
3211 "<br><b>Build: %s</b>"
3216 "<li>Marco Peereboom <marco@peereboom.us></li>"
3217 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3218 "<li>Edd Barrett <vext01@gmail.com> </li>"
3219 "<li>Todd T. Fries <todd@fries.net> </li>"
3220 "<li>Raphael Graf <r@undefined.ch> </li>"
3222 "Copyrights and licenses can be found on the XXXTerm "
3223 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3225 #ifdef XXXTERM_BUILDSTR
3226 version
, XXXTERM_BUILDSTR
3232 page
= get_html_page("About", body
, "", 0);
3235 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3242 help(struct tab
*t
, struct karg
*args
)
3244 char *page
, *head
, *body
;
3247 show_oops(NULL
, "help invalid parameters");
3249 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3250 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3252 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3253 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3254 "cgi-bin/man-cgi?xxxterm</a>";
3256 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3258 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3265 startpage(struct tab
*t
, struct karg
*args
)
3267 char *page
, *body
, *b
;
3271 show_oops(NULL
, "startpage invalid parameters");
3273 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3275 TAILQ_FOREACH(s
, &spl
, entry
) {
3277 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3281 page
= get_html_page("Startup Exception", body
, "", 0);
3284 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3291 startpage_add(const char *fmt
, ...)
3301 if (vasprintf(&msg
, fmt
, ap
) == -1)
3302 errx(1, "startpage_add failed");
3305 s
= g_malloc0(sizeof *s
);
3308 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3312 * update all favorite tabs apart from one. Pass NULL if
3313 * you want to update all.
3316 update_favorite_tabs(struct tab
*apart_from
)
3319 if (!updating_fl_tabs
) {
3320 updating_fl_tabs
= 1; /* stop infinite recursion */
3321 TAILQ_FOREACH(t
, &tabs
, entry
)
3322 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3323 && (t
!= apart_from
))
3324 xtp_page_fl(t
, NULL
);
3325 updating_fl_tabs
= 0;
3329 /* show a list of favorites (bookmarks) */
3331 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3333 char file
[PATH_MAX
];
3335 char *uri
= NULL
, *title
= NULL
;
3336 size_t len
, lineno
= 0;
3338 char *body
, *tmp
, *page
= NULL
;
3339 const char delim
[3] = {'\\', '\\', '\0'};
3341 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3344 warn("%s: bad param", __func__
);
3346 /* new session key */
3347 if (!updating_fl_tabs
)
3348 generate_xtp_session_key(&fl_session_key
);
3350 /* open favorites */
3351 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3352 if ((f
= fopen(file
, "r")) == NULL
) {
3353 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3358 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3359 "<th style='width: 40px'>#</th><th>Link</th>"
3360 "<th style='width: 40px'>Rm</th></tr>\n");
3363 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3365 if (strlen(title
) == 0 || title
[0] == '#') {
3371 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3372 if (feof(f
) || ferror(f
)) {
3373 show_oops(t
, "favorites file corrupt");
3379 body
= g_strdup_printf("%s<tr>"
3381 "<td><a href='%s'>%s</a></td>"
3382 "<td style='text-align: center'>"
3383 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3385 body
, i
, uri
, title
,
3386 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3398 /* if none, say so */
3401 body
= g_strdup_printf("%s<tr>"
3402 "<td colspan='3' style='text-align: center'>"
3403 "No favorites - To add one use the 'favadd' command."
3404 "</td></tr>", body
);
3409 body
= g_strdup_printf("%s</table>", body
);
3419 page
= get_html_page("Favorites", body
, "", 1);
3420 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3424 update_favorite_tabs(t
);
3433 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3434 size_t cert_count
, char *title
)
3436 gnutls_datum_t cinfo
;
3440 body
= g_strdup("");
3442 for (i
= 0; i
< cert_count
; i
++) {
3443 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3448 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3449 body
, i
, cinfo
.data
);
3450 gnutls_free(cinfo
.data
);
3454 tmp
= get_html_page(title
, body
, "", 0);
3457 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3462 ca_cmd(struct tab
*t
, struct karg
*args
)
3465 int rv
= 1, certs
= 0, certs_read
;
3468 gnutls_x509_crt_t
*c
= NULL
;
3469 char *certs_buf
= NULL
, *s
;
3471 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3472 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3476 if (fstat(fileno(f
), &sb
) == -1) {
3477 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3481 certs_buf
= g_malloc(sb
.st_size
+ 1);
3482 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3483 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3486 certs_buf
[sb
.st_size
] = '\0';
3489 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3491 s
+= strlen("BEGIN CERTIFICATE");
3494 bzero(&dt
, sizeof dt
);
3495 dt
.data
= (unsigned char *)certs_buf
;
3496 dt
.size
= sb
.st_size
;
3497 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3498 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3499 GNUTLS_X509_FMT_PEM
, 0);
3500 if (certs_read
<= 0) {
3501 show_oops(t
, "No cert(s) available");
3504 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3517 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
3521 struct addrinfo hints
, *res
= NULL
, *ai
;
3522 int rv
= -1, s
= -1, on
, error
;
3524 static gchar myerror
[256]; /* this is not thread safe */
3527 *error_str
= myerror
;
3528 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3529 *error_str
= "invalid URI";
3533 su
= soup_uri_new(uri
);
3535 *error_str
= "invalid soup URI";
3538 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3539 *error_str
= "invalid HTTPS URI";
3543 snprintf(port
, sizeof port
, "%d", su
->port
);
3544 bzero(&hints
, sizeof(struct addrinfo
));
3545 hints
.ai_flags
= AI_CANONNAME
;
3546 hints
.ai_family
= AF_UNSPEC
;
3547 hints
.ai_socktype
= SOCK_STREAM
;
3549 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3550 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
3551 gai_strerror(errno
));
3555 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3561 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3563 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3566 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3569 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3573 snprintf(myerror
, sizeof myerror
,
3574 "could not obtain certificates from: %s",
3580 strlcpy(domain
, su
->host
, domain_sz
);
3587 if (rv
== -1 && s
!= -1)
3594 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3597 gnutls_deinit(gsession
);
3599 gnutls_certificate_free_credentials(xcred
);
3605 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
3606 gnutls_certificate_credentials_t
*xc
)
3608 gnutls_certificate_credentials_t xcred
;
3609 gnutls_session_t gsession
;
3611 static gchar myerror
[1024]; /* this is not thread safe */
3613 if (gs
== NULL
|| xc
== NULL
)
3620 gnutls_certificate_allocate_credentials(&xcred
);
3621 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3622 GNUTLS_X509_FMT_PEM
);
3624 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3625 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3626 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3627 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3628 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3629 snprintf(myerror
, sizeof myerror
,
3630 "gnutls_handshake failed %d fatal %d %s",
3632 gnutls_error_is_fatal(rv
),
3633 gnutls_strerror_name(rv
));
3634 stop_tls(gsession
, xcred
);
3638 gnutls_credentials_type_t cred
;
3639 cred
= gnutls_auth_get_type(gsession
);
3640 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3641 snprintf(myerror
, sizeof myerror
,
3642 "gnutls_auth_get_type failed %d",
3644 stop_tls(gsession
, xcred
);
3652 *error_str
= myerror
;
3657 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3661 const gnutls_datum_t
*cl
;
3662 gnutls_x509_crt_t
*all_certs
;
3665 if (certs
== NULL
|| cert_count
== NULL
)
3667 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3669 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3673 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3674 for (i
= 0; i
< len
; i
++) {
3675 gnutls_x509_crt_init(&all_certs
[i
]);
3676 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3677 GNUTLS_X509_FMT_PEM
< 0)) {
3691 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3695 for (i
= 0; i
< cert_count
; i
++)
3696 gnutls_x509_crt_deinit(certs
[i
]);
3701 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3703 GdkColor c_text
, c_base
;
3705 gdk_color_parse(text
, &c_text
);
3706 gdk_color_parse(base
, &c_base
);
3708 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3709 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3710 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3711 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3713 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3714 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3715 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3716 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3720 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3721 size_t cert_count
, char *domain
)
3724 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3729 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3732 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3733 if ((f
= fopen(file
, "w")) == NULL
) {
3734 show_oops(t
, "Can't create cert file %s %s",
3735 file
, strerror(errno
));
3739 for (i
= 0; i
< cert_count
; i
++) {
3740 cert_buf_sz
= sizeof cert_buf
;
3741 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3742 cert_buf
, &cert_buf_sz
)) {
3743 show_oops(t
, "gnutls_x509_crt_export failed");
3746 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3747 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3752 /* not the best spot but oh well */
3753 gdk_color_parse("lightblue", &color
);
3754 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3755 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3768 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
3770 char domain
[8182], file
[PATH_MAX
];
3771 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3773 unsigned int error
= 0;
3775 size_t cert_buf_sz
, cert_count
;
3776 enum cert_trust rv
= CERT_UNTRUSTED
;
3777 static gchar serr
[80]; /* this isn't thread safe */
3778 gnutls_session_t gsession
;
3779 gnutls_x509_crt_t
*certs
;
3780 gnutls_certificate_credentials_t xcred
;
3782 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3786 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
3787 sizeof domain
)) == -1)
3790 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3793 if (start_tls(error_str
, s
, &gsession
, &xcred
))
3795 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3797 /* verify certs in case cert file doesn't exist */
3798 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3800 *error_str
= "Invalid certificates";
3805 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3806 *error_str
= "Can't get connection certificates";
3810 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3811 if ((f
= fopen(file
, "r")) == NULL
) {
3817 for (i
= 0; i
< cert_count
; i
++) {
3818 cert_buf_sz
= sizeof cert_buf
;
3819 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3820 cert_buf
, &cert_buf_sz
)) {
3823 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3824 rv
= CERT_BAD
; /* critical */
3827 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3828 rv
= CERT_BAD
; /* critical */
3837 free_connection_certs(certs
, cert_count
);
3839 /* we close the socket first for speed */
3843 /* only complain if we didn't save it locally */
3844 if (error
&& rv
!= CERT_LOCAL
) {
3845 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3846 if (error
& GNUTLS_CERT_INVALID
)
3847 strlcat(serr
, "invalid, ", sizeof serr
);
3848 if (error
& GNUTLS_CERT_REVOKED
)
3849 strlcat(serr
, "revoked, ", sizeof serr
);
3850 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3851 strlcat(serr
, "signer not found, ", sizeof serr
);
3852 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3853 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3854 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3855 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3856 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3857 strlcat(serr
, "not activated, ", sizeof serr
);
3858 if (error
& GNUTLS_CERT_EXPIRED
)
3859 strlcat(serr
, "expired, ", sizeof serr
);
3860 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3861 if (serr
[i
] == ',') {
3868 stop_tls(gsession
, xcred
);
3874 cert_cmd(struct tab
*t
, struct karg
*args
)
3876 const gchar
*uri
, *error_str
= NULL
;
3880 gnutls_session_t gsession
;
3881 gnutls_x509_crt_t
*certs
;
3882 gnutls_certificate_credentials_t xcred
;
3887 if (ssl_ca_file
== NULL
) {
3888 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3892 if ((uri
= get_uri(t
)) == NULL
) {
3893 show_oops(t
, "Invalid URI");
3897 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
3898 sizeof domain
)) == -1) {
3899 show_oops(t
, "%s", error_str
);
3904 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
3908 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3909 show_oops(t
, "get_connection_certs failed");
3913 if (args
->i
& XT_SHOW
)
3914 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3915 else if (args
->i
& XT_SAVE
)
3916 save_certs(t
, certs
, cert_count
, domain
);
3918 free_connection_certs(certs
, cert_count
);
3920 /* we close the socket first for speed */
3923 stop_tls(gsession
, xcred
);
3924 if (error_str
&& strlen(error_str
))
3925 show_oops(t
, "%s", error_str
);
3930 remove_cookie(int index
)
3936 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3938 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3940 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3944 print_cookie("remove cookie", c
);
3945 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3950 soup_cookies_free(cf
);
3956 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3961 body
= g_strdup("");
3964 if (args
->i
& XT_WL_PERSISTENT
) {
3966 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3968 RB_FOREACH(d
, domain_list
, wl
) {
3972 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3978 if (args
->i
& XT_WL_SESSION
) {
3980 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3982 RB_FOREACH(d
, domain_list
, wl
) {
3986 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3991 tmp
= get_html_page(title
, body
, "", 0);
3994 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3995 else if (wl
== &c_wl
)
3996 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3998 load_webkit_string(t
, tmp
, XT_URI_ABOUT_PLUGINWL
);
4003 #define XT_WL_INVALID (0)
4004 #define XT_WL_JAVASCRIPT (1)
4005 #define XT_WL_COOKIE (2)
4006 #define XT_WL_PLUGIN (3)
4008 wl_save(struct tab
*t
, struct karg
*args
, int list
)
4010 char file
[PATH_MAX
], *lst_str
= NULL
;
4012 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
4020 if (t
== NULL
|| args
== NULL
)
4023 if (runtime_settings
[0] == '\0')
4026 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
4027 if ((f
= fopen(file
, "r+")) == NULL
)
4031 case XT_WL_JAVASCRIPT
:
4032 lst_str
= "JavaScript";
4033 lt
= g_strdup_printf("js_wl=%s", dom
);
4037 lt
= g_strdup_printf("cookie_wl=%s", dom
);
4041 lt
= g_strdup_printf("pl_wl=%s", dom
);
4044 show_oops(t
, "Invalid list id: %d", list
);
4049 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
4050 if (uri
== NULL
|| dom
== NULL
||
4051 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
4052 show_oops(t
, "Can't add domain to %s white list", lst_str
);
4057 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
4060 if (!strcmp(line
, lt
))
4066 fprintf(f
, "%s\n", lt
);
4071 case XT_WL_JAVASCRIPT
:
4072 d
= wl_find(dom
, &js_wl
);
4074 settings_add("js_wl", dom
);
4075 d
= wl_find(dom
, &js_wl
);
4081 d
= wl_find(dom
, &c_wl
);
4083 settings_add("cookie_wl", dom
);
4084 d
= wl_find(dom
, &c_wl
);
4088 /* find and add to persistent jar */
4089 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
4090 for (;cf
; cf
= cf
->next
) {
4092 if (!strcmp(dom
, ci
->domain
) ||
4093 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
4094 c
= soup_cookie_copy(ci
);
4095 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
4098 soup_cookies_free(cf
);
4102 d
= wl_find(dom
, &pl_wl
);
4104 settings_add("pl_wl", dom
);
4105 d
= wl_find(dom
, &pl_wl
);
4110 abort(); /* can't happen */
4128 js_show_wl(struct tab
*t
, struct karg
*args
)
4130 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
4131 wl_show(t
, args
, "JavaScript White List", &js_wl
);
4137 cookie_show_wl(struct tab
*t
, struct karg
*args
)
4139 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
4140 wl_show(t
, args
, "Cookie White List", &c_wl
);
4146 cookie_cmd(struct tab
*t
, struct karg
*args
)
4148 if (args
->i
& XT_SHOW
)
4149 wl_show(t
, args
, "Cookie White List", &c_wl
);
4150 else if (args
->i
& XT_WL_TOGGLE
) {
4151 args
->i
|= XT_WL_RELOAD
;
4152 toggle_cwl(t
, args
);
4153 } else if (args
->i
& XT_SAVE
) {
4154 args
->i
|= XT_WL_RELOAD
;
4155 wl_save(t
, args
, XT_WL_COOKIE
);
4156 } else if (args
->i
& XT_DELETE
)
4157 show_oops(t
, "'cookie delete' currently unimplemented");
4163 js_cmd(struct tab
*t
, struct karg
*args
)
4165 if (args
->i
& XT_SHOW
)
4166 wl_show(t
, args
, "JavaScript White List", &js_wl
);
4167 else if (args
->i
& XT_SAVE
) {
4168 args
->i
|= XT_WL_RELOAD
;
4169 wl_save(t
, args
, XT_WL_JAVASCRIPT
);
4170 } else if (args
->i
& XT_WL_TOGGLE
) {
4171 args
->i
|= XT_WL_RELOAD
;
4173 } else if (args
->i
& XT_DELETE
)
4174 show_oops(t
, "'js delete' currently unimplemented");
4180 pl_show_wl(struct tab
*t
, struct karg
*args
)
4182 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
4183 wl_show(t
, args
, "Plugin White List", &pl_wl
);
4189 pl_cmd(struct tab
*t
, struct karg
*args
)
4191 if (args
->i
& XT_SHOW
)
4192 wl_show(t
, args
, "Plugin White List", &pl_wl
);
4193 else if (args
->i
& XT_SAVE
) {
4194 args
->i
|= XT_WL_RELOAD
;
4195 wl_save(t
, args
, XT_WL_PLUGIN
);
4196 } else if (args
->i
& XT_WL_TOGGLE
) {
4197 args
->i
|= XT_WL_RELOAD
;
4199 } else if (args
->i
& XT_DELETE
)
4200 show_oops(t
, "'plugin delete' currently unimplemented");
4206 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4208 js_toggle_cb(t
->js_toggle
, t
);
4214 add_favorite(struct tab
*t
, struct karg
*args
)
4216 char file
[PATH_MAX
];
4219 size_t urilen
, linelen
;
4220 const gchar
*uri
, *title
;
4225 /* don't allow adding of xtp pages to favorites */
4226 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4227 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4231 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4232 if ((f
= fopen(file
, "r+")) == NULL
) {
4233 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4237 title
= get_title(t
, FALSE
);
4240 if (title
== NULL
|| uri
== NULL
) {
4241 show_oops(t
, "can't add page to favorites");
4245 urilen
= strlen(uri
);
4248 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4249 if (feof(f
) || ferror(f
))
4252 if (linelen
== urilen
&& !strcmp(line
, uri
))
4259 fprintf(f
, "\n%s\n%s", title
, uri
);
4265 update_favorite_tabs(NULL
);
4271 can_go_back_for_real(struct tab
*t
)
4274 WebKitWebHistoryItem
*item
;
4280 /* rely on webkit to make sure we can go backward when on an about page */
4282 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4283 return (webkit_web_view_can_go_back(t
->wv
));
4285 /* the back/forwars list is stupid so help determine if we can go back */
4286 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4288 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4289 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4297 can_go_forward_for_real(struct tab
*t
)
4300 WebKitWebHistoryItem
*item
;
4306 /* rely on webkit to make sure we can go forward when on an about page */
4308 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4309 return (webkit_web_view_can_go_forward(t
->wv
));
4311 /* the back/forwars list is stupid so help selecting a different item */
4312 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4314 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4315 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4323 go_back_for_real(struct tab
*t
)
4326 WebKitWebHistoryItem
*item
;
4334 webkit_web_view_go_back(t
->wv
);
4337 /* the back/forwars list is stupid so help selecting a different item */
4338 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4340 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4341 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4342 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4349 go_forward_for_real(struct tab
*t
)
4352 WebKitWebHistoryItem
*item
;
4360 webkit_web_view_go_forward(t
->wv
);
4363 /* the back/forwars list is stupid so help selecting a different item */
4364 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4366 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4367 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4368 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4375 navaction(struct tab
*t
, struct karg
*args
)
4377 WebKitWebHistoryItem
*item
;
4378 WebKitWebFrame
*frame
;
4380 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4381 t
->tab_id
, args
->i
);
4383 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4385 if (args
->i
== XT_NAV_BACK
)
4386 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4388 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4390 return (XT_CB_PASSTHROUGH
);
4391 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4393 return (XT_CB_PASSTHROUGH
);
4399 go_back_for_real(t
);
4401 case XT_NAV_FORWARD
:
4403 go_forward_for_real(t
);
4406 frame
= webkit_web_view_get_main_frame(t
->wv
);
4407 webkit_web_frame_reload(frame
);
4410 return (XT_CB_PASSTHROUGH
);
4414 move(struct tab
*t
, struct karg
*args
)
4416 GtkAdjustment
*adjust
;
4417 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4423 case XT_MOVE_BOTTOM
:
4425 case XT_MOVE_PAGEDOWN
:
4426 case XT_MOVE_PAGEUP
:
4427 case XT_MOVE_HALFDOWN
:
4428 case XT_MOVE_HALFUP
:
4429 case XT_MOVE_PERCENT
:
4430 adjust
= t
->adjust_v
;
4433 adjust
= t
->adjust_h
;
4437 pos
= gtk_adjustment_get_value(adjust
);
4438 ps
= gtk_adjustment_get_page_size(adjust
);
4439 upper
= gtk_adjustment_get_upper(adjust
);
4440 lower
= gtk_adjustment_get_lower(adjust
);
4441 si
= gtk_adjustment_get_step_increment(adjust
);
4442 pi
= gtk_adjustment_get_page_increment(adjust
);
4445 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4446 "max %f si %f pi %f\n",
4447 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4448 pos
, ps
, upper
, lower
, max
, si
, pi
);
4454 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4459 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4461 case XT_MOVE_BOTTOM
:
4462 case XT_MOVE_FARRIGHT
:
4463 gtk_adjustment_set_value(adjust
, max
);
4466 case XT_MOVE_FARLEFT
:
4467 gtk_adjustment_set_value(adjust
, lower
);
4469 case XT_MOVE_PAGEDOWN
:
4471 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4473 case XT_MOVE_PAGEUP
:
4475 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4477 case XT_MOVE_HALFDOWN
:
4479 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4481 case XT_MOVE_HALFUP
:
4483 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4485 case XT_MOVE_PERCENT
:
4486 percent
= atoi(args
->s
) / 100.0;
4487 pos
= max
* percent
;
4488 if (pos
< 0.0 || pos
> max
)
4490 gtk_adjustment_set_value(adjust
, pos
);
4493 return (XT_CB_PASSTHROUGH
);
4496 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4498 return (XT_CB_HANDLED
);
4502 url_set_visibility(void)
4506 TAILQ_FOREACH(t
, &tabs
, entry
)
4507 if (show_url
== 0) {
4508 gtk_widget_hide(t
->toolbar
);
4511 gtk_widget_show(t
->toolbar
);
4515 notebook_tab_set_visibility(void)
4517 if (show_tabs
== 0) {
4518 gtk_widget_hide(tab_bar
);
4519 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4521 if (tab_style
== XT_TABS_NORMAL
) {
4522 gtk_widget_hide(tab_bar
);
4523 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4524 } else if (tab_style
== XT_TABS_COMPACT
) {
4525 gtk_widget_show(tab_bar
);
4526 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4532 statusbar_set_visibility(void)
4536 TAILQ_FOREACH(t
, &tabs
, entry
)
4537 if (show_statusbar
== 0) {
4538 gtk_widget_hide(t
->statusbar_box
);
4541 gtk_widget_show(t
->statusbar_box
);
4545 url_set(struct tab
*t
, int enable_url_entry
)
4550 show_url
= enable_url_entry
;
4552 if (enable_url_entry
) {
4553 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4554 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4555 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4557 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4558 GTK_ENTRY_ICON_PRIMARY
);
4560 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4561 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4562 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4563 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4569 fullscreen(struct tab
*t
, struct karg
*args
)
4571 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4574 return (XT_CB_PASSTHROUGH
);
4576 if (show_url
== 0) {
4584 url_set_visibility();
4585 notebook_tab_set_visibility();
4587 return (XT_CB_HANDLED
);
4591 statustoggle(struct tab
*t
, struct karg
*args
)
4593 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4595 if (show_statusbar
== 1) {
4597 statusbar_set_visibility();
4598 } else if (show_statusbar
== 0) {
4600 statusbar_set_visibility();
4602 return (XT_CB_HANDLED
);
4606 urlaction(struct tab
*t
, struct karg
*args
)
4608 int rv
= XT_CB_HANDLED
;
4610 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4613 return (XT_CB_PASSTHROUGH
);
4617 if (show_url
== 0) {
4619 url_set_visibility();
4623 if (show_url
== 1) {
4625 url_set_visibility();
4633 tabaction(struct tab
*t
, struct karg
*args
)
4635 int rv
= XT_CB_HANDLED
;
4636 char *url
= args
->s
;
4640 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4643 return (XT_CB_PASSTHROUGH
);
4647 if (strlen(url
) > 0)
4648 create_new_tab(url
, NULL
, 1, args
->precount
);
4650 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4653 if (args
->precount
< 0)
4656 TAILQ_FOREACH(tt
, &tabs
, entry
)
4657 if (tt
->tab_id
== args
->precount
- 1) {
4662 case XT_TAB_DELQUIT
:
4663 if (gtk_notebook_get_n_pages(notebook
) > 1)
4669 if (strlen(url
) > 0)
4672 rv
= XT_CB_PASSTHROUGH
;
4678 if (show_tabs
== 0) {
4680 notebook_tab_set_visibility();
4684 if (show_tabs
== 1) {
4686 notebook_tab_set_visibility();
4689 case XT_TAB_NEXTSTYLE
:
4690 if (tab_style
== XT_TABS_NORMAL
) {
4691 tab_style
= XT_TABS_COMPACT
;
4692 recolor_compact_tabs();
4695 tab_style
= XT_TABS_NORMAL
;
4696 notebook_tab_set_visibility();
4698 case XT_TAB_UNDO_CLOSE
:
4699 if (undo_count
== 0) {
4700 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4705 u
= TAILQ_FIRST(&undos
);
4706 create_new_tab(u
->uri
, u
, 1, -1);
4708 TAILQ_REMOVE(&undos
, u
, entry
);
4710 /* u->history is freed in create_new_tab() */
4715 rv
= XT_CB_PASSTHROUGH
;
4729 resizetab(struct tab
*t
, struct karg
*args
)
4731 if (t
== NULL
|| args
== NULL
) {
4732 show_oops(NULL
, "resizetab invalid parameters");
4733 return (XT_CB_PASSTHROUGH
);
4736 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4737 t
->tab_id
, args
->i
);
4739 setzoom_webkit(t
, args
->i
);
4741 return (XT_CB_HANDLED
);
4745 movetab(struct tab
*t
, struct karg
*args
)
4749 if (t
== NULL
|| args
== NULL
) {
4750 show_oops(NULL
, "movetab invalid parameters");
4751 return (XT_CB_PASSTHROUGH
);
4754 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4755 t
->tab_id
, args
->i
);
4757 if (args
->i
>= XT_TAB_INVALID
)
4758 return (XT_CB_PASSTHROUGH
);
4760 if (TAILQ_EMPTY(&tabs
))
4761 return (XT_CB_PASSTHROUGH
);
4763 n
= gtk_notebook_get_n_pages(notebook
);
4764 dest
= gtk_notebook_get_current_page(notebook
);
4768 if (args
->precount
< 0)
4769 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4771 dest
= args
->precount
- 1;
4775 if (args
->precount
< 0)
4778 dest
-= args
->precount
% n
;
4791 return (XT_CB_PASSTHROUGH
);
4794 if (dest
< 0 || dest
>= n
)
4795 return (XT_CB_PASSTHROUGH
);
4796 if (t
->tab_id
== dest
) {
4797 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4798 return (XT_CB_HANDLED
);
4801 set_current_tab(dest
);
4803 return (XT_CB_HANDLED
);
4809 command(struct tab
*t
, struct karg
*args
)
4811 char *s
= NULL
, *ss
= NULL
;
4815 if (t
== NULL
|| args
== NULL
) {
4816 show_oops(NULL
, "command invalid parameters");
4817 return (XT_CB_PASSTHROUGH
);
4828 if (cmd_prefix
== 0)
4831 ss
= g_strdup_printf(":%d", cmd_prefix
);
4842 case XT_CMD_OPEN_CURRENT
:
4845 case XT_CMD_TABNEW_CURRENT
:
4846 if (!s
) /* FALL THROUGH? */
4848 if ((uri
= get_uri(t
)) != NULL
) {
4849 ss
= g_strdup_printf("%s%s", s
, uri
);
4854 show_oops(t
, "command: invalid opcode %d", args
->i
);
4855 return (XT_CB_PASSTHROUGH
);
4858 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4860 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4861 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4862 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4864 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4865 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4870 return (XT_CB_HANDLED
);
4874 * Return a new string with a download row (in html)
4875 * appended. Old string is freed.
4878 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4881 WebKitDownloadStatus stat
;
4882 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4884 char cur_sz
[FMT_SCALED_STRSIZE
];
4885 char tot_sz
[FMT_SCALED_STRSIZE
];
4888 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4890 /* All actions wil take this form:
4891 * xxxt://class/seskey
4893 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4894 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4896 stat
= webkit_download_get_status(dl
->download
);
4899 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4900 status_html
= g_strdup_printf("Finished");
4901 cmd_html
= g_strdup_printf(
4902 "<a href='%s%d/%d'>Remove</a>",
4903 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4905 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4906 /* gather size info */
4907 progress
= 100 * webkit_download_get_progress(dl
->download
);
4910 webkit_download_get_current_size(dl
->download
), cur_sz
);
4912 webkit_download_get_total_size(dl
->download
), tot_sz
);
4914 status_html
= g_strdup_printf(
4915 "<div style='width: 100%%' align='center'>"
4916 "<div class='progress-outer'>"
4917 "<div class='progress-inner' style='width: %.2f%%'>"
4918 "</div></div></div>"
4919 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4920 progress
, cur_sz
, tot_sz
, progress
);
4922 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4923 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4927 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4928 status_html
= g_strdup_printf("Cancelled");
4929 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4930 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4932 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4933 status_html
= g_strdup_printf("Error!");
4934 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4935 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4937 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4938 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4939 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4940 status_html
= g_strdup_printf("Starting");
4943 show_oops(t
, "%s: unknown download status", __func__
);
4946 new_html
= g_strdup_printf(
4947 "%s\n<tr><td>%s</td><td>%s</td>"
4948 "<td style='text-align:center'>%s</td></tr>\n",
4949 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4950 status_html
, cmd_html
);
4954 g_free(status_html
);
4965 * update all download tabs apart from one. Pass NULL if
4966 * you want to update all.
4969 update_download_tabs(struct tab
*apart_from
)
4972 if (!updating_dl_tabs
) {
4973 updating_dl_tabs
= 1; /* stop infinite recursion */
4974 TAILQ_FOREACH(t
, &tabs
, entry
)
4975 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4976 && (t
!= apart_from
))
4977 xtp_page_dl(t
, NULL
);
4978 updating_dl_tabs
= 0;
4983 * update all cookie tabs apart from one. Pass NULL if
4984 * you want to update all.
4987 update_cookie_tabs(struct tab
*apart_from
)
4990 if (!updating_cl_tabs
) {
4991 updating_cl_tabs
= 1; /* stop infinite recursion */
4992 TAILQ_FOREACH(t
, &tabs
, entry
)
4993 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4994 && (t
!= apart_from
))
4995 xtp_page_cl(t
, NULL
);
4996 updating_cl_tabs
= 0;
5001 * update all history tabs apart from one. Pass NULL if
5002 * you want to update all.
5005 update_history_tabs(struct tab
*apart_from
)
5009 if (!updating_hl_tabs
) {
5010 updating_hl_tabs
= 1; /* stop infinite recursion */
5011 TAILQ_FOREACH(t
, &tabs
, entry
)
5012 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
5013 && (t
!= apart_from
))
5014 xtp_page_hl(t
, NULL
);
5015 updating_hl_tabs
= 0;
5019 /* cookie management XTP page */
5021 xtp_page_cl(struct tab
*t
, struct karg
*args
)
5023 char *body
, *page
, *tmp
;
5024 int i
= 1; /* all ids start 1 */
5025 GSList
*sc
, *pc
, *pc_start
;
5027 char *type
, *table_headers
, *last_domain
;
5029 DNPRINTF(XT_D_CMD
, "%s", __func__
);
5032 show_oops(NULL
, "%s invalid parameters", __func__
);
5036 /* Generate a new session key */
5037 if (!updating_cl_tabs
)
5038 generate_xtp_session_key(&cl_session_key
);
5041 table_headers
= g_strdup_printf("<table><tr>"
5044 "<th style='width:200px'>Value</th>"
5048 "<th>HTTP<br />only</th>"
5049 "<th style='width:40px'>Rm</th></tr>\n");
5051 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
5052 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
5056 last_domain
= strdup("");
5057 for (; sc
; sc
= sc
->next
) {
5060 if (strcmp(last_domain
, c
->domain
) != 0) {
5063 last_domain
= strdup(c
->domain
);
5067 body
= g_strdup_printf("%s</table>"
5069 body
, c
->domain
, table_headers
);
5073 body
= g_strdup_printf("<h2>%s</h2>%s\n",
5074 c
->domain
, table_headers
);
5079 for (pc
= pc_start
; pc
; pc
= pc
->next
)
5080 if (soup_cookie_equal(pc
->data
, c
)) {
5081 type
= "Session + Persistent";
5086 body
= g_strdup_printf(
5089 "<td style='word-wrap:normal'>%s</td>"
5091 " <textarea rows='4'>%s</textarea>"
5097 "<td style='text-align:center'>"
5098 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
5105 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
5120 soup_cookies_free(sc
);
5121 soup_cookies_free(pc
);
5123 /* small message if there are none */
5125 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5126 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
5129 body
= g_strdup_printf("%s</table>", body
);
5132 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
5134 g_free(table_headers
);
5135 g_free(last_domain
);
5137 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
5138 update_cookie_tabs(t
);
5146 xtp_page_hl(struct tab
*t
, struct karg
*args
)
5148 char *body
, *page
, *tmp
;
5150 int i
= 1; /* all ids start 1 */
5152 DNPRINTF(XT_D_CMD
, "%s", __func__
);
5155 show_oops(NULL
, "%s invalid parameters", __func__
);
5159 /* Generate a new session key */
5160 if (!updating_hl_tabs
)
5161 generate_xtp_session_key(&hl_session_key
);
5164 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
5165 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
5167 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
5169 body
= g_strdup_printf(
5171 "<td><a href='%s'>%s</a></td>"
5173 "<td style='text-align: center'>"
5174 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
5175 body
, h
->uri
, h
->uri
, h
->title
,
5176 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
5177 XT_XTP_HL_REMOVE
, i
);
5183 /* small message if there are none */
5186 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5187 "colspan='3'>No History</td></tr>\n", body
);
5192 body
= g_strdup_printf("%s</table>", body
);
5195 page
= get_html_page("History", body
, "", TRUE
);
5199 * update all history manager tabs as the xtp session
5200 * key has now changed. No need to update the current tab.
5201 * Already did that above.
5203 update_history_tabs(t
);
5205 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5212 * Generate a web page detailing the status of any downloads
5215 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5217 struct download
*dl
;
5218 char *body
, *page
, *tmp
;
5222 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5225 show_oops(NULL
, "%s invalid parameters", __func__
);
5230 * Generate a new session key for next page instance.
5231 * This only happens for the top level call to xtp_page_dl()
5232 * in which case updating_dl_tabs is 0.
5234 if (!updating_dl_tabs
)
5235 generate_xtp_session_key(&dl_session_key
);
5237 /* header - with refresh so as to update */
5238 if (refresh_interval
>= 1)
5239 ref
= g_strdup_printf(
5240 "<meta http-equiv='refresh' content='%u"
5241 ";url=%s%d/%s/%d' />\n",
5250 body
= g_strdup_printf("<div align='center'>"
5251 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5252 "</p><table><tr><th style='width: 60%%'>"
5253 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5254 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5256 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5257 body
= xtp_page_dl_row(t
, body
, dl
);
5261 /* message if no downloads in list */
5264 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5265 " style='text-align: center'>"
5266 "No downloads</td></tr>\n", body
);
5271 body
= g_strdup_printf("%s</table></div>", body
);
5274 page
= get_html_page("Downloads", body
, ref
, 1);
5279 * update all download manager tabs as the xtp session
5280 * key has now changed. No need to update the current tab.
5281 * Already did that above.
5283 update_download_tabs(t
);
5285 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5292 search(struct tab
*t
, struct karg
*args
)
5296 if (t
== NULL
|| args
== NULL
) {
5297 show_oops(NULL
, "search invalid parameters");
5302 case XT_SEARCH_NEXT
:
5303 d
= t
->search_forward
;
5305 case XT_SEARCH_PREV
:
5306 d
= !t
->search_forward
;
5309 return (XT_CB_PASSTHROUGH
);
5312 if (t
->search_text
== NULL
) {
5313 if (global_search
== NULL
)
5314 return (XT_CB_PASSTHROUGH
);
5316 d
= t
->search_forward
= TRUE
;
5317 t
->search_text
= g_strdup(global_search
);
5318 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5319 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5323 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5324 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5326 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5328 return (XT_CB_HANDLED
);
5331 struct settings_args
{
5337 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5340 struct settings_args
*sa
= cb_args
;
5345 if (s
->flags
& XT_SF_RUNTIME
)
5351 *sa
->body
= g_strdup_printf(
5353 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5354 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5366 set_show(struct tab
*t
, struct karg
*args
)
5368 char *body
, *page
, *tmp
;
5370 struct settings_args sa
;
5372 bzero(&sa
, sizeof sa
);
5376 body
= g_strdup_printf("<div align='center'><table><tr>"
5377 "<th align='left'>Setting</th>"
5378 "<th align='left'>Value</th></tr>\n");
5380 settings_walk(print_setting
, &sa
);
5383 /* small message if there are none */
5386 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5387 "colspan='2'>No settings</td></tr>\n", body
);
5392 body
= g_strdup_printf("%s</table></div>", body
);
5395 page
= get_html_page("Settings", body
, "", 0);
5399 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5403 return (XT_CB_PASSTHROUGH
);
5407 set(struct tab
*t
, struct karg
*args
)
5412 if (args
== NULL
|| args
->s
== NULL
)
5413 return (set_show(t
, args
));
5416 p
= g_strstrip(args
->s
);
5419 return (set_show(t
, args
));
5421 /* we got some sort of string */
5422 val
= g_strrstr(p
, "=");
5425 val
= g_strchomp(val
);
5428 for (i
= 0; i
< LENGTH(rs
); i
++) {
5429 if (strcmp(rs
[i
].name
, p
))
5432 if (rs
[i
].activate
) {
5433 if (rs
[i
].activate(val
))
5434 show_oops(t
, "%s invalid value %s",
5437 show_oops(t
, ":set %s = %s", p
, val
);
5440 show_oops(t
, "not a runtime option: %s", p
);
5444 show_oops(t
, "unknown option: %s", p
);
5448 for (i
= 0; i
< LENGTH(rs
); i
++) {
5449 if (strcmp(rs
[i
].name
, p
))
5452 /* XXX this could use some cleanup */
5453 switch (rs
[i
].type
) {
5456 show_oops(t
, "%s = %d",
5457 rs
[i
].name
, *rs
[i
].ival
);
5458 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5459 show_oops(t
, "%s = %s",
5461 rs
[i
].s
->get(&rs
[i
]));
5462 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5463 show_oops(t
, "%s = ...", rs
[i
].name
);
5465 show_oops(t
, "%s = ", rs
[i
].name
);
5469 show_oops(t
, "%s = %f",
5470 rs
[i
].name
, *rs
[i
].fval
);
5471 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5472 show_oops(t
, "%s = %s",
5474 rs
[i
].s
->get(&rs
[i
]));
5475 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5476 show_oops(t
, "%s = ...", rs
[i
].name
);
5478 show_oops(t
, "%s = ", rs
[i
].name
);
5481 if (rs
[i
].sval
&& *rs
[i
].sval
)
5482 show_oops(t
, "%s = %s",
5483 rs
[i
].name
, *rs
[i
].sval
);
5484 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5485 show_oops(t
, "%s = %s",
5487 rs
[i
].s
->get(&rs
[i
]));
5488 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5489 show_oops(t
, "%s = ...", rs
[i
].name
);
5491 show_oops(t
, "%s = ", rs
[i
].name
);
5494 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5500 show_oops(t
, "unknown option: %s", p
);
5503 return (XT_CB_PASSTHROUGH
);
5507 session_save(struct tab
*t
, char *filename
)
5513 if (strlen(filename
) == 0)
5516 if (filename
[0] == '.' || filename
[0] == '/')
5520 if (save_tabs(t
, &a
))
5522 strlcpy(named_session
, filename
, sizeof named_session
);
5524 /* add the new session to the list of sessions */
5525 s
= g_malloc(sizeof(struct session
));
5526 s
->name
= g_strdup(filename
);
5527 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5535 session_open(struct tab
*t
, char *filename
)
5540 if (strlen(filename
) == 0)
5543 if (filename
[0] == '.' || filename
[0] == '/')
5547 a
.i
= XT_SES_CLOSETABS
;
5548 if (open_tabs(t
, &a
))
5551 strlcpy(named_session
, filename
, sizeof named_session
);
5559 session_delete(struct tab
*t
, char *filename
)
5561 char file
[PATH_MAX
];
5565 if (strlen(filename
) == 0)
5568 if (filename
[0] == '.' || filename
[0] == '/')
5571 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5575 if (!strcmp(filename
, named_session
))
5576 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5577 sizeof named_session
);
5579 /* remove session from sessions list */
5580 TAILQ_FOREACH(s
, &sessions
, entry
) {
5581 if (!strcmp(s
->name
, filename
))
5586 TAILQ_REMOVE(&sessions
, s
, entry
);
5587 g_free((gpointer
) s
->name
);
5596 session_cmd(struct tab
*t
, struct karg
*args
)
5598 char *filename
= args
->s
;
5603 if (args
->i
& XT_SHOW
)
5604 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5605 XT_SAVED_TABS_FILE
: named_session
);
5606 else if (args
->i
& XT_SAVE
) {
5607 if (session_save(t
, filename
)) {
5608 show_oops(t
, "Can't save session: %s",
5609 filename
? filename
: "INVALID");
5612 } else if (args
->i
& XT_OPEN
) {
5613 if (session_open(t
, filename
)) {
5614 show_oops(t
, "Can't open session: %s",
5615 filename
? filename
: "INVALID");
5618 } else if (args
->i
& XT_DELETE
) {
5619 if (session_delete(t
, filename
)) {
5620 show_oops(t
, "Can't delete session: %s",
5621 filename
? filename
: "INVALID");
5626 return (XT_CB_PASSTHROUGH
);
5630 * Make a hardcopy of the page
5633 print_page(struct tab
*t
, struct karg
*args
)
5635 WebKitWebFrame
*frame
;
5637 GtkPrintOperation
*op
;
5638 GtkPrintOperationAction action
;
5639 GtkPrintOperationResult print_res
;
5640 GError
*g_err
= NULL
;
5641 int marg_l
, marg_r
, marg_t
, marg_b
;
5643 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5645 ps
= gtk_page_setup_new();
5646 op
= gtk_print_operation_new();
5647 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5648 frame
= webkit_web_view_get_main_frame(t
->wv
);
5650 /* the default margins are too small, so we will bump them */
5651 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5652 XT_PRINT_EXTRA_MARGIN
;
5653 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5654 XT_PRINT_EXTRA_MARGIN
;
5655 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5656 XT_PRINT_EXTRA_MARGIN
;
5657 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5658 XT_PRINT_EXTRA_MARGIN
;
5661 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5662 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5663 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5664 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5666 gtk_print_operation_set_default_page_setup(op
, ps
);
5668 /* this appears to free 'op' and 'ps' */
5669 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5671 /* check it worked */
5672 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5673 show_oops(NULL
, "can't print: %s", g_err
->message
);
5674 g_error_free (g_err
);
5682 go_home(struct tab
*t
, struct karg
*args
)
5689 set_encoding(struct tab
*t
, struct karg
*args
)
5693 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5694 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5696 e
= webkit_web_view_get_encoding(t
->wv
);
5697 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5699 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5705 restart(struct tab
*t
, struct karg
*args
)
5709 a
.s
= XT_RESTART_TABS_FILE
;
5711 execvp(start_argv
[0], start_argv
);
5717 #define CTRL GDK_CONTROL_MASK
5718 #define MOD1 GDK_MOD1_MASK
5719 #define SHFT GDK_SHIFT_MASK
5721 /* inherent to GTK not all keys will be caught at all times */
5722 /* XXX sort key bindings */
5723 struct key_binding
{
5728 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5730 { "cookiejar", MOD1
, 0, GDK_j
},
5731 { "downloadmgr", MOD1
, 0, GDK_d
},
5732 { "history", MOD1
, 0, GDK_h
},
5733 { "print", CTRL
, 0, GDK_p
},
5734 { "search", 0, 0, GDK_slash
},
5735 { "searchb", 0, 0, GDK_question
},
5736 { "statustoggle", CTRL
, 0, GDK_n
},
5737 { "command", 0, 0, GDK_colon
},
5738 { "qa", CTRL
, 0, GDK_q
},
5739 { "restart", MOD1
, 0, GDK_q
},
5740 { "js toggle", CTRL
, 0, GDK_j
},
5741 { "cookie toggle", MOD1
, 0, GDK_c
},
5742 { "togglesrc", CTRL
, 0, GDK_s
},
5743 { "yankuri", 0, 0, GDK_y
},
5744 { "pasteuricur", 0, 0, GDK_p
},
5745 { "pasteurinew", 0, 0, GDK_P
},
5746 { "toplevel toggle", 0, 0, GDK_F4
},
5747 { "help", 0, 0, GDK_F1
},
5748 { "run_script", MOD1
, 0, GDK_r
},
5751 { "searchnext", 0, 0, GDK_n
},
5752 { "searchprevious", 0, 0, GDK_N
},
5755 { "focusaddress", 0, 0, GDK_F6
},
5756 { "focussearch", 0, 0, GDK_F7
},
5759 { "hinting", 0, 0, GDK_f
},
5760 { "hinting_newtab", SHFT
, 0, GDK_F
},
5762 /* custom stylesheet */
5763 { "userstyle", 0, 0, GDK_i
},
5766 { "goback", 0, 0, GDK_BackSpace
},
5767 { "goback", MOD1
, 0, GDK_Left
},
5768 { "goforward", SHFT
, 0, GDK_BackSpace
},
5769 { "goforward", MOD1
, 0, GDK_Right
},
5770 { "reload", 0, 0, GDK_F5
},
5771 { "reload", CTRL
, 0, GDK_r
},
5772 { "reload", CTRL
, 0, GDK_l
},
5773 { "favorites", MOD1
, 1, GDK_f
},
5775 /* vertical movement */
5776 { "scrolldown", 0, 0, GDK_j
},
5777 { "scrolldown", 0, 0, GDK_Down
},
5778 { "scrollup", 0, 0, GDK_Up
},
5779 { "scrollup", 0, 0, GDK_k
},
5780 { "scrollbottom", 0, 0, GDK_G
},
5781 { "scrollbottom", 0, 0, GDK_End
},
5782 { "scrolltop", 0, 0, GDK_Home
},
5783 { "scrollpagedown", 0, 0, GDK_space
},
5784 { "scrollpagedown", CTRL
, 0, GDK_f
},
5785 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5786 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5787 { "scrollpageup", 0, 0, GDK_Page_Up
},
5788 { "scrollpageup", CTRL
, 0, GDK_b
},
5789 { "scrollhalfup", CTRL
, 0, GDK_u
},
5790 /* horizontal movement */
5791 { "scrollright", 0, 0, GDK_l
},
5792 { "scrollright", 0, 0, GDK_Right
},
5793 { "scrollleft", 0, 0, GDK_Left
},
5794 { "scrollleft", 0, 0, GDK_h
},
5795 { "scrollfarright", 0, 0, GDK_dollar
},
5796 { "scrollfarleft", 0, 0, GDK_0
},
5799 { "tabnew", CTRL
, 0, GDK_t
},
5800 { "999tabnew", CTRL
, 0, GDK_T
},
5801 { "tabclose", CTRL
, 1, GDK_w
},
5802 { "tabundoclose", 0, 0, GDK_U
},
5803 { "tabnext 1", CTRL
, 0, GDK_1
},
5804 { "tabnext 2", CTRL
, 0, GDK_2
},
5805 { "tabnext 3", CTRL
, 0, GDK_3
},
5806 { "tabnext 4", CTRL
, 0, GDK_4
},
5807 { "tabnext 5", CTRL
, 0, GDK_5
},
5808 { "tabnext 6", CTRL
, 0, GDK_6
},
5809 { "tabnext 7", CTRL
, 0, GDK_7
},
5810 { "tabnext 8", CTRL
, 0, GDK_8
},
5811 { "tabnext 9", CTRL
, 0, GDK_9
},
5812 { "tabfirst", CTRL
, 0, GDK_less
},
5813 { "tablast", CTRL
, 0, GDK_greater
},
5814 { "tabprevious", CTRL
, 0, GDK_Left
},
5815 { "tabnext", CTRL
, 0, GDK_Right
},
5816 { "focusout", CTRL
, 0, GDK_minus
},
5817 { "focusin", CTRL
, 0, GDK_plus
},
5818 { "focusin", CTRL
, 0, GDK_equal
},
5819 { "focusreset", CTRL
, 0, GDK_0
},
5821 /* command aliases (handy when -S flag is used) */
5822 { "promptopen", 0, 0, GDK_F9
},
5823 { "promptopencurrent", 0, 0, GDK_F10
},
5824 { "prompttabnew", 0, 0, GDK_F11
},
5825 { "prompttabnewcurrent",0, 0, GDK_F12
},
5827 TAILQ_HEAD(keybinding_list
, key_binding
);
5830 walk_kb(struct settings
*s
,
5831 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5833 struct key_binding
*k
;
5836 if (s
== NULL
|| cb
== NULL
) {
5837 show_oops(NULL
, "walk_kb invalid parameters");
5841 TAILQ_FOREACH(k
, &kbl
, entry
) {
5847 if (gdk_keyval_name(k
->key
) == NULL
)
5850 strlcat(str
, k
->cmd
, sizeof str
);
5851 strlcat(str
, ",", sizeof str
);
5853 if (k
->mask
& GDK_SHIFT_MASK
)
5854 strlcat(str
, "S-", sizeof str
);
5855 if (k
->mask
& GDK_CONTROL_MASK
)
5856 strlcat(str
, "C-", sizeof str
);
5857 if (k
->mask
& GDK_MOD1_MASK
)
5858 strlcat(str
, "M1-", sizeof str
);
5859 if (k
->mask
& GDK_MOD2_MASK
)
5860 strlcat(str
, "M2-", sizeof str
);
5861 if (k
->mask
& GDK_MOD3_MASK
)
5862 strlcat(str
, "M3-", sizeof str
);
5863 if (k
->mask
& GDK_MOD4_MASK
)
5864 strlcat(str
, "M4-", sizeof str
);
5865 if (k
->mask
& GDK_MOD5_MASK
)
5866 strlcat(str
, "M5-", sizeof str
);
5868 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5869 cb(s
, str
, cb_args
);
5874 init_keybindings(void)
5877 struct key_binding
*k
;
5879 for (i
= 0; i
< LENGTH(keys
); i
++) {
5880 k
= g_malloc0(sizeof *k
);
5881 k
->cmd
= keys
[i
].cmd
;
5882 k
->mask
= keys
[i
].mask
;
5883 k
->use_in_entry
= keys
[i
].use_in_entry
;
5884 k
->key
= keys
[i
].key
;
5885 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5887 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5888 k
->cmd
? k
->cmd
: "unnamed key");
5893 keybinding_clearall(void)
5895 struct key_binding
*k
, *next
;
5897 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5898 next
= TAILQ_NEXT(k
, entry
);
5902 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5903 k
->cmd
? k
->cmd
: "unnamed key");
5904 TAILQ_REMOVE(&kbl
, k
, entry
);
5910 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5912 struct key_binding
*k
;
5913 guint keyval
, mask
= 0;
5916 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5918 /* Keys which are to be used in entry have been prefixed with an
5919 * exclamation mark. */
5923 /* find modifier keys */
5924 if (strstr(key
, "S-"))
5925 mask
|= GDK_SHIFT_MASK
;
5926 if (strstr(key
, "C-"))
5927 mask
|= GDK_CONTROL_MASK
;
5928 if (strstr(key
, "M1-"))
5929 mask
|= GDK_MOD1_MASK
;
5930 if (strstr(key
, "M2-"))
5931 mask
|= GDK_MOD2_MASK
;
5932 if (strstr(key
, "M3-"))
5933 mask
|= GDK_MOD3_MASK
;
5934 if (strstr(key
, "M4-"))
5935 mask
|= GDK_MOD4_MASK
;
5936 if (strstr(key
, "M5-"))
5937 mask
|= GDK_MOD5_MASK
;
5940 for (i
= strlen(key
) - 1; i
> 0; i
--)
5944 /* validate keyname */
5945 keyval
= gdk_keyval_from_name(key
);
5946 if (keyval
== GDK_VoidSymbol
) {
5947 warnx("invalid keybinding name %s", key
);
5950 /* must run this test too, gtk+ doesn't handle 10 for example */
5951 if (gdk_keyval_name(keyval
) == NULL
) {
5952 warnx("invalid keybinding name %s", key
);
5956 /* Remove eventual dupes. */
5957 TAILQ_FOREACH(k
, &kbl
, entry
)
5958 if (k
->key
== keyval
&& k
->mask
== mask
) {
5959 TAILQ_REMOVE(&kbl
, k
, entry
);
5965 k
= g_malloc0(sizeof *k
);
5966 k
->cmd
= g_strdup(cmd
);
5968 k
->use_in_entry
= use_in_entry
;
5971 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5976 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5977 k
->cmd
, gdk_keyval_name(keyval
));
5979 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5985 add_kb(struct settings
*s
, char *entry
)
5989 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5991 /* clearall is special */
5992 if (!strcmp(entry
, "clearall")) {
5993 keybinding_clearall();
5997 kb
= strstr(entry
, ",");
6003 return (keybinding_add(entry
, key
, key
[0] == '!'));
6009 int (*func
)(struct tab
*, struct karg
*);
6013 { "command", 0, command
, ':', 0 },
6014 { "search", 0, command
, '/', 0 },
6015 { "searchb", 0, command
, '?', 0 },
6016 { "togglesrc", 0, toggle_src
, 0, 0 },
6018 /* yanking and pasting */
6019 { "yankuri", 0, yank_uri
, 0, 0 },
6020 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
6021 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
6022 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
6025 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
6026 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
6029 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
6030 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
6033 { "hinting", 0, hint
, 0, 0 },
6034 { "hinting_newtab", 0, hint
, XT_HINT_NEWTAB
, 0 },
6036 /* custom stylesheet */
6037 { "userstyle", 0, userstyle
, 0, 0 },
6040 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
6041 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
6042 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
6044 /* vertical movement */
6045 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
6046 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
6047 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
6048 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
6049 { "1", 0, move
, XT_MOVE_TOP
, 0 },
6050 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
6051 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
6052 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
6053 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
6054 /* horizontal movement */
6055 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
6056 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
6057 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
6058 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
6060 { "favorites", 0, xtp_page_fl
, 0, 0 },
6061 { "fav", 0, xtp_page_fl
, 0, 0 },
6062 { "favadd", 0, add_favorite
, 0, 0 },
6064 { "qall", 0, quit
, 0, 0 },
6065 { "quitall", 0, quit
, 0, 0 },
6066 { "w", 0, save_tabs
, 0, 0 },
6067 { "wq", 0, save_tabs_and_quit
, 0, 0 },
6068 { "help", 0, help
, 0, 0 },
6069 { "about", 0, about
, 0, 0 },
6070 { "stats", 0, stats
, 0, 0 },
6071 { "version", 0, about
, 0, 0 },
6074 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6075 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6076 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
6077 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6078 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6079 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6080 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
6081 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6082 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6083 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6084 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6086 /* cookie command */
6087 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6088 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6089 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
6090 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6091 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6092 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6093 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
6094 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6095 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6096 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6097 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6099 /* plugin command */
6100 { "plugin", 0, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6101 { "save", 1, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6102 { "domain", 2, pl_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
6103 { "fqdn", 2, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
6104 { "show", 1, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6105 { "all", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
6106 { "persistent", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
6107 { "session", 2, pl_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
6108 { "toggle", 1, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6109 { "domain", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
6110 { "fqdn", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
6112 /* toplevel (domain) command */
6113 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6114 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
6117 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
6120 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
6121 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
6122 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
6124 { "ca", 0, ca_cmd
, 0, 0 },
6125 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
6126 { "dl", 0, xtp_page_dl
, 0, 0 },
6127 { "h", 0, xtp_page_hl
, 0, 0 },
6128 { "history", 0, xtp_page_hl
, 0, 0 },
6129 { "home", 0, go_home
, 0, 0 },
6130 { "restart", 0, restart
, 0, 0 },
6131 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
6132 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
6133 { "statustoggle", 0, statustoggle
, 0, 0 },
6134 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
6136 { "print", 0, print_page
, 0, 0 },
6139 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
6140 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
6141 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
6142 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6143 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
6144 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
6145 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
6146 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6147 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
6148 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
6149 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
6150 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
6151 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
6152 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
6153 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
6154 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
6155 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
6156 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
6157 { "buffers", 0, buffers
, 0, 0 },
6158 { "ls", 0, buffers
, 0, 0 },
6159 { "tabs", 0, buffers
, 0, 0 },
6160 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
6162 /* command aliases (handy when -S flag is used) */
6163 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
6164 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
6165 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
6166 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
6169 { "set", 0, set
, 0, XT_SETARG
},
6171 { "fullscreen", 0, fullscreen
, 0, 0 },
6172 { "f", 0, fullscreen
, 0, 0 },
6175 { "session", 0, session_cmd
, XT_SHOW
, 0 },
6176 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
6177 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
6178 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
6179 { "show", 1, session_cmd
, XT_SHOW
, 0 },
6186 } cmd_status
= {-1, 0};
6189 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6192 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
6199 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6206 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6208 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
6214 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
6216 a
.i
= XT_NAV_FORWARD
;
6226 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6228 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6230 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6237 * cancel, remove, etc. downloads
6240 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6242 struct download find
, *d
= NULL
;
6244 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6246 /* some commands require a valid download id */
6247 if (cmd
!= XT_XTP_DL_LIST
) {
6248 /* lookup download in question */
6250 d
= RB_FIND(download_list
, &downloads
, &find
);
6253 show_oops(t
, "%s: no such download", __func__
);
6258 /* decide what to do */
6260 case XT_XTP_DL_CANCEL
:
6261 webkit_download_cancel(d
->download
);
6263 case XT_XTP_DL_REMOVE
:
6264 webkit_download_cancel(d
->download
); /* just incase */
6265 g_object_unref(d
->download
);
6266 RB_REMOVE(download_list
, &downloads
, d
);
6268 case XT_XTP_DL_LIST
:
6272 show_oops(t
, "%s: unknown command", __func__
);
6275 xtp_page_dl(t
, NULL
);
6279 * Actions on history, only does one thing for now, but
6280 * we provide the function for future actions
6283 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6285 struct history
*h
, *next
;
6289 case XT_XTP_HL_REMOVE
:
6290 /* walk backwards, as listed in reverse */
6291 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6292 next
= RB_PREV(history_list
, &hl
, h
);
6294 RB_REMOVE(history_list
, &hl
, h
);
6295 g_free((gpointer
) h
->title
);
6296 g_free((gpointer
) h
->uri
);
6303 case XT_XTP_HL_LIST
:
6304 /* Nothing - just xtp_page_hl() below */
6307 show_oops(t
, "%s: unknown command", __func__
);
6311 xtp_page_hl(t
, NULL
);
6314 /* remove a favorite */
6316 remove_favorite(struct tab
*t
, int index
)
6318 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6319 char *new_favs
, *tmp
;
6324 /* open favorites */
6325 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6327 if ((f
= fopen(file
, "r")) == NULL
) {
6328 show_oops(t
, "%s: can't open favorites: %s",
6329 __func__
, strerror(errno
));
6333 /* build a string which will become the new favroites file */
6334 new_favs
= g_strdup("");
6337 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6338 if (feof(f
) || ferror(f
))
6340 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6347 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6348 if (feof(f
) || ferror(f
)) {
6349 show_oops(t
, "%s: can't parse favorites %s",
6350 __func__
, strerror(errno
));
6355 /* as long as this isn't the one we are deleting add to file */
6358 new_favs
= g_strdup_printf("%s%s\n%s\n",
6359 new_favs
, title
, uri
);
6371 /* write back new favorites file */
6372 if ((f
= fopen(file
, "w")) == NULL
) {
6373 show_oops(t
, "%s: can't open favorites: %s",
6374 __func__
, strerror(errno
));
6378 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
6379 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
6392 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6395 case XT_XTP_FL_LIST
:
6396 /* nothing, just the below call to xtp_page_fl() */
6398 case XT_XTP_FL_REMOVE
:
6399 remove_favorite(t
, arg
);
6402 show_oops(t
, "%s: invalid favorites command", __func__
);
6406 xtp_page_fl(t
, NULL
);
6410 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6413 case XT_XTP_CL_LIST
:
6414 /* nothing, just xtp_page_cl() */
6416 case XT_XTP_CL_REMOVE
:
6420 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6424 xtp_page_cl(t
, NULL
);
6427 /* link an XTP class to it's session key and handler function */
6428 struct xtp_despatch
{
6431 void (*handle_func
)(struct tab
*, uint8_t, int);
6434 struct xtp_despatch xtp_despatches
[] = {
6435 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6436 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6437 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6438 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6439 { XT_XTP_INVALID
, NULL
, NULL
}
6443 * is the url xtp protocol? (xxxt://)
6444 * if so, parse and despatch correct bahvior
6447 parse_xtp_url(struct tab
*t
, const char *url
)
6449 char *dup
= NULL
, *p
, *last
= NULL
;
6450 uint8_t n_tokens
= 0;
6451 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6452 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6457 * tokens array meaning:
6459 * tokens[1] = session key
6460 * tokens[2] = action
6461 * tokens[3] = optional argument
6464 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6466 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6469 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6471 /* split out the url */
6472 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6473 (p
= strtok_r(NULL
, "/", &last
))) {
6475 tokens
[n_tokens
++] = p
;
6478 /* should be atleast three fields 'class/seskey/command/arg' */
6482 dsp
= xtp_despatches
;
6483 req_class
= atoi(tokens
[0]);
6484 while (dsp
->xtp_class
) {
6485 if (dsp
->xtp_class
== req_class
) {
6492 /* did we find one atall? */
6493 if (dsp_match
== NULL
) {
6494 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6498 /* check session key and call despatch function */
6499 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6500 ret
= TRUE
; /* all is well, this was a valid xtp request */
6501 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6514 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6516 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6518 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6521 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6526 show_oops(t
, "activate_uri_entry_cb no uri");
6530 uri
+= strspn(uri
, "\t ");
6532 /* if xxxt:// treat specially */
6533 if (parse_xtp_url(t
, uri
))
6536 /* otherwise continue to load page normally */
6537 load_uri(t
, (gchar
*)uri
);
6542 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6544 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6545 char *newuri
= NULL
;
6548 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6551 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6555 if (search_string
== NULL
) {
6556 show_oops(t
, "no search_string");
6560 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6562 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6563 newuri
= g_strdup_printf(search_string
, enc_search
);
6567 webkit_web_view_load_uri(t
->wv
, newuri
);
6575 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6577 struct domain
*d
= NULL
;
6580 if (uri
== NULL
|| t
== NULL
)
6583 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6588 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6589 es
? "enable" : "disable", uri
);
6591 g_object_set(G_OBJECT(t
->settings
),
6592 "enable-html5-local-storage", es
, (char *)NULL
);
6593 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6597 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6599 struct domain
*d
= NULL
;
6602 if (uri
== NULL
|| t
== NULL
)
6605 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6610 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6611 es
? "enable" : "disable", uri
);
6613 g_object_set(G_OBJECT(t
->settings
),
6614 "enable-scripts", es
, (char *)NULL
);
6615 g_object_set(G_OBJECT(t
->settings
),
6616 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6617 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6619 button_set_stockid(t
->js_toggle
,
6620 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6624 check_and_set_pl(const gchar
*uri
, struct tab
*t
)
6626 struct domain
*d
= NULL
;
6629 if (uri
== NULL
|| t
== NULL
)
6632 if ((d
= wl_find_uri(uri
, &pl_wl
)) == NULL
)
6637 DNPRINTF(XT_D_JS
, "check_and_set_pl: %s %s\n",
6638 es
? "enable" : "disable", uri
);
6640 g_object_set(G_OBJECT(t
->settings
),
6641 "enable-plugins", es
, (char *)NULL
);
6642 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6646 color_address_bar(gpointer p
)
6649 struct tab
*tt
, *t
= p
;
6650 gchar
*col_str
= XT_COLOR_WHITE
;
6651 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
6654 gdk_threads_enter();
6656 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6658 /* make sure t still exists */
6661 TAILQ_FOREACH(tt
, &tabs
, entry
)
6667 if ((uri
= get_uri(t
)) == NULL
)
6672 gdk_threads_leave();
6675 col_str
= XT_COLOR_YELLOW
;
6676 switch (load_compare_cert(u
, &error_str
)) {
6678 col_str
= XT_COLOR_BLUE
;
6681 col_str
= XT_COLOR_GREEN
;
6683 case CERT_UNTRUSTED
:
6684 col_str
= XT_COLOR_YELLOW
;
6687 col_str
= XT_COLOR_RED
;
6692 gdk_threads_enter();
6694 /* make sure t isn't deleted */
6695 TAILQ_FOREACH(tt
, &tabs
, entry
)
6702 /* test to see if the user navigated away and canceled the thread */
6703 if (t
->thread
!= g_thread_self())
6705 if ((uri
= get_uri(t
)) == NULL
) {
6709 if (strcmp(uri
, u
)) {
6710 /* make sure we are still the same url */
6716 gdk_color_parse(col_str
, &color
);
6717 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6719 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6720 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6722 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6724 if (error_str
&& error_str
[0] != '\0')
6725 show_oops(t
, "%s", error_str
);
6730 /* t is invalid at this point */
6732 g_free((gpointer
)u
);
6734 gdk_threads_leave();
6739 show_ca_status(struct tab
*t
, const char *uri
)
6742 gchar
*col_str
= XT_COLOR_WHITE
;
6744 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6745 ssl_strict_certs
, ssl_ca_file
, uri
);
6752 if (ssl_ca_file
== NULL
) {
6753 if (g_str_has_prefix(uri
, "http://"))
6755 if (g_str_has_prefix(uri
, "https://")) {
6756 col_str
= XT_COLOR_RED
;
6761 if (g_str_has_prefix(uri
, "http://") ||
6762 !g_str_has_prefix(uri
, "https://"))
6766 * It is not necessary to see if the thread is already running.
6767 * If the thread is in progress setting it to something else aborts it
6771 /* thread the coloring of the address bar */
6772 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
6774 color_address_bar(t
);
6780 gdk_color_parse(col_str
, &color
);
6781 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6783 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6784 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6786 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6791 free_favicon(struct tab
*t
)
6793 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6794 __func__
, t
->icon_download
, t
->icon_request
);
6796 if (t
->icon_request
)
6797 g_object_unref(t
->icon_request
);
6798 if (t
->icon_dest_uri
)
6799 g_free(t
->icon_dest_uri
);
6801 t
->icon_request
= NULL
;
6802 t
->icon_dest_uri
= NULL
;
6806 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6808 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6809 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6811 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6812 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6814 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6815 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6819 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6821 GdkPixbuf
*pb_scaled
;
6823 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6824 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6825 GDK_INTERP_BILINEAR
);
6829 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6830 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6832 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6833 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6835 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6836 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6838 if (pb_scaled
!= pb
)
6839 g_object_unref(pb_scaled
);
6843 xt_icon_from_file(struct tab
*t
, char *file
)
6847 if (g_str_has_prefix(file
, "file://"))
6848 file
+= strlen("file://");
6850 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6852 xt_icon_from_pixbuf(t
, pb
);
6855 xt_icon_from_name(t
, "text-html");
6859 is_valid_icon(char *file
)
6862 const char *mime_type
;
6866 gf
= g_file_new_for_path(file
);
6867 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6869 mime_type
= g_file_info_get_content_type(fi
);
6870 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6871 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6872 g_strcmp0(mime_type
, "image/png") == 0 ||
6873 g_strcmp0(mime_type
, "image/gif") == 0 ||
6874 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6882 set_favicon_from_file(struct tab
*t
, char *file
)
6886 if (t
== NULL
|| file
== NULL
)
6889 if (g_str_has_prefix(file
, "file://"))
6890 file
+= strlen("file://");
6891 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6893 if (!stat(file
, &sb
)) {
6894 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6895 /* corrupt icon so trash it */
6896 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6899 /* no need to set icon to default here */
6903 xt_icon_from_file(t
, file
);
6907 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6910 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6911 struct tab
*tt
= NULL
, *t
= NULL
;
6914 * find the webview instead of passing in the tab as it could have been
6915 * deleted from underneath us.
6917 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6926 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6927 __func__
, t
->tab_id
, status
);
6930 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6932 t
->icon_download
= NULL
;
6935 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6938 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6941 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6943 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6944 __func__
, t
->tab_id
);
6945 t
->icon_download
= NULL
;
6948 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6951 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6952 __func__
, t
->icon_dest_uri
);
6953 set_favicon_from_file(t
, t
->icon_dest_uri
);
6954 /* these will be freed post callback */
6955 t
->icon_request
= NULL
;
6956 t
->icon_download
= NULL
;
6964 abort_favicon_download(struct tab
*t
)
6966 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6968 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6969 if (t
->icon_download
) {
6970 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6971 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6972 webkit_download_cancel(t
->icon_download
);
6973 t
->icon_download
= NULL
;
6978 xt_icon_from_name(t
, "text-html");
6982 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6984 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6986 if (uri
== NULL
|| t
== NULL
)
6989 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6990 /* take icon from WebKitIconDatabase */
6993 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6995 xt_icon_from_pixbuf(t
, pb
);
6998 xt_icon_from_name(t
, "text-html");
6999 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
7000 /* download icon to cache dir */
7001 gchar
*name_hash
, file
[PATH_MAX
];
7004 if (t
->icon_request
) {
7005 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
7009 /* check to see if we got the icon in cache */
7010 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
7011 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
7014 if (!stat(file
, &sb
)) {
7015 if (sb
.st_size
> 0) {
7016 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
7018 set_favicon_from_file(t
, file
);
7022 /* corrupt icon so trash it */
7023 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
7028 /* create download for icon */
7029 t
->icon_request
= webkit_network_request_new(uri
);
7030 if (t
->icon_request
== NULL
) {
7031 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
7036 t
->icon_download
= webkit_download_new(t
->icon_request
);
7037 if (t
->icon_download
== NULL
)
7040 /* we have to free icon_dest_uri later */
7041 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
7042 webkit_download_set_destination_uri(t
->icon_download
,
7045 if (webkit_download_get_status(t
->icon_download
) ==
7046 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7047 g_object_unref(t
->icon_request
);
7048 g_free(t
->icon_dest_uri
);
7049 t
->icon_request
= NULL
;
7050 t
->icon_dest_uri
= NULL
;
7054 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
7055 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
7057 webkit_download_start(t
->icon_download
);
7062 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
7064 const gchar
*uri
= NULL
, *title
= NULL
;
7065 struct history
*h
, find
;
7069 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
7070 webkit_web_view_get_load_status(wview
),
7071 get_uri(t
) ? get_uri(t
) : "NOTHING");
7074 show_oops(NULL
, "notify_load_status_cb invalid parameters");
7078 switch (webkit_web_view_get_load_status(wview
)) {
7079 case WEBKIT_LOAD_PROVISIONAL
:
7081 abort_favicon_download(t
);
7082 #if GTK_CHECK_VERSION(2, 20, 0)
7083 gtk_widget_show(t
->spinner
);
7084 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
7086 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
7088 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
7090 /* assume we are a new address */
7091 gdk_color_parse("white", &color
);
7092 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
7093 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
7095 /* take focus if we are visible */
7099 /* kill color thread */
7104 case WEBKIT_LOAD_COMMITTED
:
7109 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
7115 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
7117 /* check if js white listing is enabled */
7118 if (enable_plugin_whitelist
)
7119 check_and_set_pl(uri
, t
);
7120 if (enable_cookie_whitelist
)
7121 check_and_set_cookie(uri
, t
);
7122 if (enable_js_whitelist
)
7123 check_and_set_js(uri
, t
);
7129 /* we know enough to autosave the session */
7130 if (session_autosave
) {
7135 show_ca_status(t
, uri
);
7138 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
7142 case WEBKIT_LOAD_FINISHED
:
7148 if (!strncmp(uri
, "http://", strlen("http://")) ||
7149 !strncmp(uri
, "https://", strlen("https://")) ||
7150 !strncmp(uri
, "file://", strlen("file://"))) {
7152 h
= RB_FIND(history_list
, &hl
, &find
);
7154 title
= get_title(t
, FALSE
);
7155 h
= g_malloc(sizeof *h
);
7156 h
->uri
= g_strdup(uri
);
7157 h
->title
= g_strdup(title
);
7158 RB_INSERT(history_list
, &hl
, h
);
7159 completion_add_uri(h
->uri
);
7160 update_history_tabs(NULL
);
7164 set_status(t
, (char *)uri
, XT_STATUS_URI
);
7165 #if WEBKIT_CHECK_VERSION(1, 1, 18)
7166 case WEBKIT_LOAD_FAILED
:
7169 #if GTK_CHECK_VERSION(2, 20, 0)
7170 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7171 gtk_widget_hide(t
->spinner
);
7174 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
7179 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
7181 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
7182 can_go_back_for_real(t
));
7184 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
7185 can_go_forward_for_real(t
));
7189 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
7191 const gchar
*title
= NULL
, *win_title
= NULL
;
7193 title
= get_title(t
, FALSE
);
7194 win_title
= get_title(t
, TRUE
);
7195 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
7196 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
7197 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
7198 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
7202 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7204 run_script(t
, JS_HINTING
);
7208 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
7210 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
7211 progress
== 100 ? 0 : (double)progress
/ 100);
7212 if (show_url
== 0) {
7213 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
7214 progress
== 100 ? 0 : (double)progress
/ 100);
7217 update_statusbar_position(NULL
, NULL
);
7221 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
7222 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
7223 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
7226 WebKitWebNavigationReason reason
;
7227 struct domain
*d
= NULL
;
7230 show_oops(NULL
, "webview_npd_cb invalid parameters");
7234 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
7236 webkit_network_request_get_uri(request
));
7238 uri
= (char *)webkit_network_request_get_uri(request
);
7240 /* if this is an xtp url, we don't load anything else */
7241 if (parse_xtp_url(t
, uri
))
7244 if (t
->ctrl_click
) {
7246 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
7247 webkit_web_policy_decision_ignore(pd
);
7248 return (TRUE
); /* we made the decission */
7252 * This is a little hairy but it comes down to this:
7253 * when we run in whitelist mode we have to assist the browser in
7254 * opening the URL that it would have opened in a new tab.
7256 reason
= webkit_web_navigation_action_get_reason(na
);
7257 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
7258 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7259 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
7260 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7262 webkit_web_policy_decision_use(pd
);
7263 return (TRUE
); /* we made the decision */
7270 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7273 struct domain
*d
= NULL
;
7275 WebKitWebView
*webview
= NULL
;
7277 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
7278 webkit_web_view_get_uri(wv
));
7281 /* open in current tab */
7283 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7284 uri
= webkit_web_view_get_uri(wv
);
7285 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7288 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7290 } else if (enable_scripts
== 1) {
7291 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7299 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
7302 struct domain
*d
= NULL
;
7304 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7306 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7307 uri
= webkit_web_view_get_uri(wv
);
7308 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7312 } else if (enable_scripts
== 1)
7319 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7321 /* we can not eat the event without throwing gtk off so defer it */
7323 /* catch middle click */
7324 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7329 /* catch ctrl click */
7330 if (e
->type
== GDK_BUTTON_RELEASE
&&
7331 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7336 return (XT_CB_PASSTHROUGH
);
7340 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7342 struct mime_type
*m
;
7344 m
= find_mime_type(mime_type
);
7352 show_oops(t
, "can't fork mime handler");
7362 execlp(m
->mt_action
, m
->mt_action
,
7363 webkit_network_request_get_uri(request
), (void *)NULL
);
7372 get_mime_type(const char *file
)
7375 char *mime_type
= NULL
;
7379 if (g_str_has_prefix(file
, "file://"))
7380 file
+= strlen("file://");
7382 gf
= g_file_new_for_path(file
);
7383 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7385 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
7386 mime_type
= g_strdup(m
);
7394 run_download_mimehandler(char *mime_type
, char *file
)
7396 struct mime_type
*m
;
7398 m
= find_mime_type(mime_type
);
7404 show_oops(NULL
, "can't fork download mime handler");
7414 if (g_str_has_prefix(file
, "file://"))
7415 file
+= strlen("file://");
7416 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7425 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7428 WebKitDownloadStatus status
;
7429 const char *file
= NULL
;
7432 if (download
== NULL
)
7434 status
= webkit_download_get_status(download
);
7435 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7438 file
= webkit_download_get_destination_uri(download
);
7441 mime
= get_mime_type(file
);
7445 run_download_mimehandler((char *)mime
, (char *)file
);
7450 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7451 WebKitNetworkRequest
*request
, char *mime_type
,
7452 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7455 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7459 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7460 t
->tab_id
, mime_type
);
7462 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7463 webkit_web_policy_decision_ignore(decision
);
7468 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7469 webkit_web_policy_decision_download(decision
);
7477 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7481 const gchar
*suggested_name
;
7482 gchar
*filename
= NULL
;
7484 struct download
*download_entry
;
7487 if (wk_download
== NULL
|| t
== NULL
) {
7488 show_oops(NULL
, "%s invalid parameters", __func__
);
7492 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7493 if (suggested_name
== NULL
)
7494 return (FALSE
); /* abort download */
7505 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7507 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7508 filename
: suggested_name
);
7510 } while (!stat(uri
+ strlen("file://"), &sb
));
7512 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7513 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7515 webkit_download_set_destination_uri(wk_download
, uri
);
7517 if (webkit_download_get_status(wk_download
) ==
7518 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7519 show_oops(t
, "%s: download failed to start", __func__
);
7521 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7523 /* connect "download first" mime handler */
7524 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7525 G_CALLBACK(download_status_changed_cb
), NULL
);
7527 download_entry
= g_malloc(sizeof(struct download
));
7528 download_entry
->download
= wk_download
;
7529 download_entry
->tab
= t
;
7530 download_entry
->id
= next_download_id
++;
7531 RB_INSERT(download_list
, &downloads
, download_entry
);
7532 /* get from history */
7533 g_object_ref(wk_download
);
7534 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7535 show_oops(t
, "Download of '%s' started...",
7536 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7545 /* sync other download manager tabs */
7546 update_download_tabs(NULL
);
7549 * NOTE: never redirect/render the current tab before this
7550 * function returns. This will cause the download to never start.
7552 return (ret
); /* start download */
7556 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7558 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7561 show_oops(NULL
, "webview_hover_cb");
7566 set_status(t
, uri
, XT_STATUS_LINK
);
7569 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7574 mark(struct tab
*t
, struct karg
*arg
)
7580 if ((index
= marktoindex(mark
)) == -1)
7583 if (arg
->i
== XT_MARK_SET
)
7584 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7585 else if (arg
->i
== XT_MARK_GOTO
) {
7586 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7587 show_oops(t
, "mark '%c' does not exist", mark
);
7590 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7591 something changes the document size */
7592 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7599 marks_clear(struct tab
*t
)
7603 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7604 t
->mark
[i
] = XT_INVALID_MARK
;
7610 char file
[PATH_MAX
];
7611 char *line
= NULL
, *p
;
7616 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7617 if ((f
= fopen(file
, "r+")) == NULL
) {
7618 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7622 for (i
= 1; ; i
++) {
7623 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7625 if (strlen(line
) == 0 || line
[0] == '#') {
7631 p
= strtok(line
, " \t");
7633 if (p
== NULL
|| strlen(p
) != 1 ||
7634 (index
= marktoindex(*p
)) == -1) {
7635 warnx("corrupt quickmarks file, line %d", i
);
7639 p
= strtok(NULL
, " \t");
7640 if (qmarks
[index
] != NULL
)
7641 g_free(qmarks
[index
]);
7642 qmarks
[index
] = g_strdup(p
);
7653 char file
[PATH_MAX
];
7657 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7658 if ((f
= fopen(file
, "r+")) == NULL
) {
7659 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7663 for (i
= 0; i
< XT_NOMARKS
; i
++)
7664 if (qmarks
[i
] != NULL
)
7665 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7673 qmark(struct tab
*t
, struct karg
*arg
)
7678 mark
= arg
->s
[strlen(arg
->s
)-1];
7679 index
= marktoindex(mark
);
7685 if (qmarks
[index
] != NULL
)
7686 g_free(qmarks
[index
]);
7688 qmarks_load(); /* sync if multiple instances */
7689 qmarks
[index
] = g_strdup(get_uri(t
));
7693 if (qmarks
[index
] != NULL
)
7694 load_uri(t
, qmarks
[index
]);
7696 show_oops(t
, "quickmark \"%c\" does not exist",
7702 if (qmarks
[index
] != NULL
)
7703 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7705 show_oops(t
, "quickmark \"%c\" does not exist",
7716 go_up(struct tab
*t
, struct karg
*args
)
7722 levels
= atoi(args
->s
);
7726 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7727 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7729 tmp
+= strlen(XT_PROTO_DELIM
);
7731 /* if an uri starts with a slash, leave it alone (for file:///) */
7738 p
= strrchr(tmp
, '/');
7752 gototab(struct tab
*t
, struct karg
*args
)
7755 struct karg arg
= {0, NULL
, -1};
7757 tab
= atoi(args
->s
);
7759 arg
.i
= XT_TAB_NEXT
;
7768 zoom_amount(struct tab
*t
, struct karg
*arg
)
7770 struct karg narg
= {0, NULL
, -1};
7772 narg
.i
= atoi(arg
->s
);
7773 resizetab(t
, &narg
);
7779 flip_colon(struct tab
*t
, struct karg
*arg
)
7781 struct karg narg
= {0, NULL
, -1};
7784 if (t
== NULL
|| arg
== NULL
)
7787 p
= strstr(arg
->s
, ":");
7799 /* buffer commands receive the regex that triggered them in arg.s */
7800 char bcmd
[XT_BUFCMD_SZ
];
7804 #define XT_PRE_NO (0)
7805 #define XT_PRE_YES (1)
7806 #define XT_PRE_MAYBE (2)
7808 int (*func
)(struct tab
*, struct karg
*);
7812 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7813 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7814 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7815 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7816 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7817 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7818 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7819 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7820 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7821 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7822 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7823 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7824 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7825 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7826 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7827 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7828 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7829 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7833 buffercmd_init(void)
7837 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7838 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7839 REG_EXTENDED
| REG_NOSUB
))
7840 startpage_add("invalid buffercmd regex %s",
7841 buffercmds
[i
].regex
);
7845 buffercmd_abort(struct tab
*t
)
7849 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7850 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7853 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7854 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7858 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7860 struct karg arg
= {0, NULL
, -1};
7863 arg
.s
= g_strdup(bcmd
);
7865 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7866 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7876 buffercmd_addkey(struct tab
*t
, guint keyval
)
7879 char s
[XT_BUFCMD_SZ
];
7881 if (keyval
== GDK_Escape
) {
7883 return (XT_CB_HANDLED
);
7886 /* key with modifier or non-ascii character */
7887 if (!isascii(keyval
))
7888 return (XT_CB_PASSTHROUGH
);
7890 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7891 "to buffer \"%s\"\n", keyval
, bcmd
);
7893 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7894 if (bcmd
[i
] == '\0') {
7899 /* buffer full, ignore input */
7900 if (i
>= LENGTH(bcmd
) -1) {
7901 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7903 return (XT_CB_HANDLED
);
7906 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7908 /* find exact match */
7909 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7910 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7911 (size_t) 0, NULL
, 0) == 0) {
7912 buffercmd_execute(t
, &buffercmds
[i
]);
7916 /* find non exact matches to see if we need to abort ot not */
7917 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7918 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7921 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7922 if (isdigit(bcmd
[0])) {
7923 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7927 if (sscanf(bcmd
, "%s", s
) == 0)
7930 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7931 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7934 if (sscanf(bcmd
, "%s", s
) == 0)
7937 if (c
== -1 && buffercmds
[i
].precount
)
7939 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7942 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7943 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7946 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7951 return (XT_CB_HANDLED
);
7955 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7957 struct key_binding
*k
;
7959 /* handle keybindings if buffercmd is empty.
7960 if not empty, allow commands like C-n */
7961 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7962 TAILQ_FOREACH(k
, &kbl
, entry
)
7963 if (e
->keyval
== k
->key
7964 && (entry
? k
->use_in_entry
: 1)) {
7966 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7967 return (cmd_execute(t
, k
->cmd
));
7968 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7969 return (cmd_execute(t
, k
->cmd
));
7973 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7974 return buffercmd_addkey(t
, e
->keyval
);
7976 return (XT_CB_PASSTHROUGH
);
7980 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7982 char s
[2], buf
[128];
7983 const char *errstr
= NULL
;
7985 /* don't use w directly; use t->whatever instead */
7988 show_oops(NULL
, "wv_keypress_after_cb");
7989 return (XT_CB_PASSTHROUGH
);
7992 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7993 e
->keyval
, e
->state
, t
);
7997 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7999 return (XT_CB_HANDLED
);
8003 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
8005 /* we have a string */
8007 /* we have a number */
8008 snprintf(buf
, sizeof buf
,
8009 "vimprobable_fire(%s)", t
->hint_num
);
8016 /* XXX unfuck this */
8017 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
8018 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
8019 /* last input was numerical */
8021 l
= strlen(t
->hint_num
);
8028 t
->hint_num
[l
] = '\0';
8032 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
8033 /* last input was alphanumerical */
8035 l
= strlen(t
->hint_buf
);
8042 t
->hint_buf
[l
] = '\0';
8052 /* numerical input */
8053 if (CLEAN(e
->state
) == 0 &&
8054 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
8055 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
8056 snprintf(s
, sizeof s
, "%c", e
->keyval
);
8057 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
8058 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
8062 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
8063 "invalid link number\n");
8066 snprintf(buf
, sizeof buf
,
8067 "vimprobable_update_hints(%s)",
8069 t
->hint_mode
= XT_HINT_NUMERICAL
;
8073 /* empty the counter buffer */
8074 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
8075 return (XT_CB_HANDLED
);
8078 /* alphanumerical input */
8079 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
8080 e
->keyval
<= GDK_z
) ||
8081 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
8082 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
8083 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
8084 e
->keyval
<= GDK_9
) ||
8085 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
8086 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
8087 snprintf(s
, sizeof s
, "%c", e
->keyval
);
8088 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
8089 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
8090 " %s\n", t
->hint_buf
);
8092 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
8095 snprintf(buf
, sizeof buf
,
8096 "vimprobable_show_hints('%s')", t
->hint_buf
);
8097 t
->hint_mode
= XT_HINT_ALPHANUM
;
8100 /* empty the counter buffer */
8101 bzero(t
->hint_num
, sizeof t
->hint_num
);
8102 return (XT_CB_HANDLED
);
8105 return (XT_CB_HANDLED
);
8108 snprintf(s
, sizeof s
, "%c", e
->keyval
);
8109 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
8110 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
8113 return (handle_keypress(t
, e
, 0));
8117 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8121 /* Hide buffers, if they are visible, with escape. */
8122 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
8123 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
8124 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8126 return (XT_CB_HANDLED
);
8129 return (XT_CB_PASSTHROUGH
);
8133 search_continue(struct tab
*t
)
8135 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
8136 gboolean rv
= FALSE
;
8140 if (strlen(c
) == 1) {
8141 webkit_web_view_unmark_text_matches(t
->wv
);
8146 t
->search_forward
= TRUE
;
8147 else if (c
[0] == '?')
8148 t
->search_forward
= FALSE
;
8158 search_cb(struct tab
*t
)
8160 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
8163 if (search_continue(t
) == FALSE
)
8167 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
8169 /* not found, mark red */
8170 gdk_color_parse(XT_COLOR_RED
, &color
);
8171 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8172 /* unmark and remove selection */
8173 webkit_web_view_unmark_text_matches(t
->wv
);
8174 /* my kingdom for a way to unselect text in webview */
8176 /* found, highlight all */
8177 webkit_web_view_unmark_text_matches(t
->wv
);
8178 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
8179 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
8180 gdk_color_parse(XT_COLOR_WHITE
, &color
);
8181 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
8189 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8191 const gchar
*c
= gtk_entry_get_text(w
);
8194 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
8195 return (XT_CB_PASSTHROUGH
);
8198 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
8199 e
->keyval
, e
->state
, t
);
8201 if (search_continue(t
) == FALSE
)
8204 /* if search length is > 4 then no longer play timeout games */
8205 if (strlen(c
) > 4) {
8207 g_source_remove(t
->search_id
);
8214 /* reestablish a new timer if the user types fast */
8216 g_source_remove(t
->search_id
);
8217 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
8220 return (XT_CB_PASSTHROUGH
);
8224 match_uri(const gchar
*uri
, const gchar
*key
) {
8227 gboolean match
= FALSE
;
8231 if (!strncmp(key
, uri
, len
))
8234 voffset
= strstr(uri
, "/") + 2;
8235 if (!strncmp(key
, voffset
, len
))
8237 else if (g_str_has_prefix(voffset
, "www.")) {
8238 voffset
= voffset
+ strlen("www.");
8239 if (!strncmp(key
, voffset
, len
))
8248 match_session(const gchar
*name
, const gchar
*key
) {
8251 sub
= strcasestr(name
, key
);
8257 cmd_getlist(int id
, char *key
)
8264 if (cmds
[id
].type
& XT_URLARG
) {
8265 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
8266 if (match_uri(h
->uri
, key
)) {
8267 cmd_status
.list
[c
] = (char *)h
->uri
;
8273 } else if (cmds
[id
].type
& XT_SESSARG
) {
8274 TAILQ_FOREACH(s
, &sessions
, entry
)
8275 if (match_session(s
->name
, key
)) {
8276 cmd_status
.list
[c
] = (char *)s
->name
;
8282 } else if (cmds
[id
].type
& XT_SETARG
) {
8283 for (i
= 0; i
< LENGTH(rs
); i
++)
8284 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
8285 cmd_status
.list
[c
++] = rs
[i
].name
;
8291 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
8293 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
8294 if (cmds
[i
].level
< dep
)
8296 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
8297 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
8298 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
8306 cmd_getnext(int dir
)
8308 cmd_status
.index
+= dir
;
8310 if (cmd_status
.index
< 0)
8311 cmd_status
.index
= cmd_status
.len
- 1;
8312 else if (cmd_status
.index
>= cmd_status
.len
)
8313 cmd_status
.index
= 0;
8315 return cmd_status
.list
[cmd_status
.index
];
8319 cmd_tokenize(char *s
, char *tokens
[])
8322 char *tok
, *last
= NULL
;
8323 size_t len
= strlen(s
);
8326 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8327 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8328 tok
= strtok_r(NULL
, " ", &last
), i
++)
8338 cmd_complete(struct tab
*t
, char *str
, int dir
)
8340 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8341 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8343 char *tok
, *match
, *s
= g_strdup(str
);
8345 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8348 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8351 for (i
= 0; isdigit(s
[i
]); i
++)
8354 for (; isspace(s
[i
]); i
++)
8359 levels
= cmd_tokenize(s
, tokens
);
8361 for (i
= 0; i
< levels
- 1; i
++) {
8364 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8365 if (cmds
[j
].level
< dep
)
8367 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8371 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8378 if (matchcount
== 1) {
8379 strlcat(res
, tok
, sizeof res
);
8380 strlcat(res
, " ", sizeof res
);
8390 if (cmd_status
.index
== -1)
8391 cmd_getlist(parent
, tokens
[i
]);
8393 if (cmd_status
.len
> 0) {
8394 match
= cmd_getnext(dir
);
8395 strlcat(res
, match
, sizeof res
);
8396 gtk_entry_set_text(w
, res
);
8397 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8404 cmd_execute(struct tab
*t
, char *str
)
8406 struct cmd
*cmd
= NULL
;
8407 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
8409 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8410 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8411 struct karg arg
= {0, NULL
, -1};
8416 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8422 while (isspace(s
[0]))
8425 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8426 prefix
= atoi(prefixstr
);
8430 for (tok
= strtok_r(s
, " ", &last
); tok
;
8431 tok
= strtok_r(NULL
, " ", &last
)) {
8433 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8434 if (cmds
[j
].level
< dep
)
8436 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8438 if (cmds
[j
].level
== dep
&&
8439 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8443 if (len
== strlen(cmds
[j
].cmd
)) {
8449 if (matchcount
== 1) {
8454 show_oops(t
, "Invalid command: %s", str
);
8460 show_oops(t
, "Empty command");
8466 arg
.precount
= prefix
;
8467 else if (cmd_prefix
> 0)
8468 arg
.precount
= cmd_prefix
;
8470 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8471 show_oops(t
, "No prefix allowed: %s", str
);
8475 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8476 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8477 if (arg
.s
== NULL
) {
8478 show_oops(t
, "Invalid command");
8481 arg
.precount
= atoi(arg
.s
);
8482 if (arg
.precount
<= 0) {
8483 if (arg
.s
[0] == '0')
8484 show_oops(t
, "Zero count");
8486 show_oops(t
, "Trailing characters");
8491 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8492 __func__
, arg
.precount
, arg
.s
);
8508 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8511 show_oops(NULL
, "entry_key_cb invalid parameters");
8512 return (XT_CB_PASSTHROUGH
);
8515 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8516 e
->keyval
, e
->state
, t
);
8520 if (e
->keyval
== GDK_Escape
) {
8521 /* don't use focus_webview(t) because we want to type :cmds */
8522 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8525 return (handle_keypress(t
, e
, 1));
8528 struct command_entry
*
8529 history_prev(struct command_list
*l
, struct command_entry
*at
)
8532 at
= TAILQ_LAST(l
, command_list
);
8534 at
= TAILQ_PREV(at
, command_list
, entry
);
8536 at
= TAILQ_LAST(l
, command_list
);
8542 struct command_entry
*
8543 history_next(struct command_list
*l
, struct command_entry
*at
)
8546 at
= TAILQ_FIRST(l
);
8548 at
= TAILQ_NEXT(at
, entry
);
8550 at
= TAILQ_FIRST(l
);
8557 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8559 int rv
= XT_CB_HANDLED
;
8560 const gchar
*c
= gtk_entry_get_text(w
);
8563 show_oops(NULL
, "cmd_keypress_cb parameters");
8564 return (XT_CB_PASSTHROUGH
);
8567 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8568 e
->keyval
, e
->state
, t
);
8572 e
->keyval
= GDK_Escape
;
8573 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8574 e
->keyval
= GDK_Escape
;
8576 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8577 e
->keyval
!= GDK_ISO_Left_Tab
)
8578 cmd_status
.index
= -1;
8580 switch (e
->keyval
) {
8583 cmd_complete(t
, (char *)&c
[1], 1);
8585 case GDK_ISO_Left_Tab
:
8587 cmd_complete(t
, (char *)&c
[1], -1);
8592 if ((search_at
= history_next(&shl
, search_at
))) {
8593 search_at
->line
[0] = c
[0];
8594 gtk_entry_set_text(w
, search_at
->line
);
8595 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8598 if ((history_at
= history_prev(&chl
, history_at
))) {
8599 history_at
->line
[0] = c
[0];
8600 gtk_entry_set_text(w
, history_at
->line
);
8601 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8608 if ((search_at
= history_next(&shl
, search_at
))) {
8609 search_at
->line
[0] = c
[0];
8610 gtk_entry_set_text(w
, search_at
->line
);
8611 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8614 if ((history_at
= history_next(&chl
, history_at
))) {
8615 history_at
->line
[0] = c
[0];
8616 gtk_entry_set_text(w
, history_at
->line
);
8617 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8623 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8631 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8632 webkit_web_view_unmark_text_matches(t
->wv
);
8636 rv
= XT_CB_PASSTHROUGH
;
8642 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8644 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8648 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8650 /* popup menu enabled */
8655 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8658 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8659 return (XT_CB_PASSTHROUGH
);
8662 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8663 t
->tab_id
, t
->popup
);
8665 /* if popup is enabled don't lose focus */
8668 return (XT_CB_PASSTHROUGH
);
8674 if (show_url
== 0 || t
->focus_wv
)
8677 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8679 return (XT_CB_PASSTHROUGH
);
8683 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8686 const gchar
*c
= gtk_entry_get_text(entry
);
8689 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8693 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8700 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8706 if (c
[0] == '/' || c
[0] == '?') {
8707 /* see if there is a timer pending */
8709 g_source_remove(t
->search_id
);
8714 if (t
->search_text
) {
8715 g_free(t
->search_text
);
8716 t
->search_text
= NULL
;
8719 t
->search_text
= g_strdup(s
);
8721 g_free(global_search
);
8722 global_search
= g_strdup(s
);
8723 t
->search_forward
= c
[0] == '/';
8725 history_add(&shl
, search_file
, s
, &search_history_count
);
8729 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8736 backward_cb(GtkWidget
*w
, struct tab
*t
)
8741 show_oops(NULL
, "backward_cb invalid parameters");
8745 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8752 forward_cb(GtkWidget
*w
, struct tab
*t
)
8757 show_oops(NULL
, "forward_cb invalid parameters");
8761 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8763 a
.i
= XT_NAV_FORWARD
;
8768 home_cb(GtkWidget
*w
, struct tab
*t
)
8771 show_oops(NULL
, "home_cb invalid parameters");
8775 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8781 stop_cb(GtkWidget
*w
, struct tab
*t
)
8783 WebKitWebFrame
*frame
;
8786 show_oops(NULL
, "stop_cb invalid parameters");
8790 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8792 frame
= webkit_web_view_get_main_frame(t
->wv
);
8793 if (frame
== NULL
) {
8794 show_oops(t
, "stop_cb: no frame");
8798 webkit_web_frame_stop_loading(frame
);
8799 abort_favicon_download(t
);
8803 setup_webkit(struct tab
*t
)
8805 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8806 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8807 FALSE
, (char *)NULL
);
8809 warnx("webkit does not have \"enable-dns-prefetching\" property");
8810 g_object_set(G_OBJECT(t
->settings
),
8811 "user-agent", t
->user_agent
, (char *)NULL
);
8812 g_object_set(G_OBJECT(t
->settings
),
8813 "enable-scripts", enable_scripts
, (char *)NULL
);
8814 g_object_set(G_OBJECT(t
->settings
),
8815 "enable-plugins", enable_plugins
, (char *)NULL
);
8816 g_object_set(G_OBJECT(t
->settings
),
8817 "javascript-can-open-windows-automatically", enable_scripts
,
8819 g_object_set(G_OBJECT(t
->settings
),
8820 "enable-html5-database", FALSE
, (char *)NULL
);
8821 g_object_set(G_OBJECT(t
->settings
),
8822 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8823 g_object_set(G_OBJECT(t
->settings
),
8824 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8825 g_object_set(G_OBJECT(t
->settings
),
8826 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8827 g_object_set(G_OBJECT(t
->wv
),
8828 "full-content-zoom", TRUE
, (char *)NULL
);
8830 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8834 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8836 struct tab
*ti
, *t
= NULL
;
8837 gdouble view_size
, value
, max
;
8840 TAILQ_FOREACH(ti
, &tabs
, entry
)
8841 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8849 if (adjustment
== NULL
)
8850 adjustment
= gtk_scrolled_window_get_vadjustment(
8851 GTK_SCROLLED_WINDOW(t
->browser_win
));
8853 view_size
= gtk_adjustment_get_page_size(adjustment
);
8854 value
= gtk_adjustment_get_value(adjustment
);
8855 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8858 position
= g_strdup("All");
8859 else if (value
== max
)
8860 position
= g_strdup("Bot");
8861 else if (value
== 0)
8862 position
= g_strdup("Top");
8864 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8866 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8873 create_browser(struct tab
*t
)
8877 GtkAdjustment
*adjustment
;
8880 show_oops(NULL
, "create_browser invalid parameters");
8884 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8885 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8886 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8887 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8889 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8890 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8891 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8893 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8894 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8897 t
->settings
= webkit_web_settings_new();
8899 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
8901 if (user_agent
== NULL
) {
8902 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8904 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8907 t
->user_agent
= g_strdup(user_agent
);
8909 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8912 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8913 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8914 G_CALLBACK(update_statusbar_position
), NULL
);
8926 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8927 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8928 gtk_widget_set_name(w
, "xxxterm");
8929 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8930 g_signal_connect(G_OBJECT(w
), "delete_event",
8931 G_CALLBACK (gtk_main_quit
), NULL
);
8937 create_kiosk_toolbar(struct tab
*t
)
8939 GtkWidget
*toolbar
= NULL
, *b
;
8941 b
= gtk_hbox_new(FALSE
, 0);
8943 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8945 /* backward button */
8946 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8947 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8948 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8949 G_CALLBACK(backward_cb
), t
);
8950 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8952 /* forward button */
8953 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8954 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8955 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8956 G_CALLBACK(forward_cb
), t
);
8957 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8960 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8961 gtk_widget_set_sensitive(t
->gohome
, true);
8962 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8963 G_CALLBACK(home_cb
), t
);
8964 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8966 /* create widgets but don't use them */
8967 t
->uri_entry
= gtk_entry_new();
8968 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8969 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8970 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8976 create_toolbar(struct tab
*t
)
8978 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8980 b
= gtk_hbox_new(FALSE
, 0);
8982 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8984 /* backward button */
8985 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8986 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8987 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8988 G_CALLBACK(backward_cb
), t
);
8989 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8991 /* forward button */
8992 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8993 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8994 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8995 G_CALLBACK(forward_cb
), t
);
8996 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
9000 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
9001 gtk_widget_set_sensitive(t
->stop
, FALSE
);
9002 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
9003 G_CALLBACK(stop_cb
), t
);
9004 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
9008 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
9009 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
9010 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
9011 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
9012 G_CALLBACK(js_toggle_cb
), t
);
9013 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
9015 t
->uri_entry
= gtk_entry_new();
9016 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
9017 G_CALLBACK(activate_uri_entry_cb
), t
);
9018 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
9019 G_CALLBACK(entry_key_cb
), t
);
9021 eb1
= gtk_hbox_new(FALSE
, 0);
9022 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
9023 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
9024 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
9027 if (search_string
) {
9029 t
->search_entry
= gtk_entry_new();
9030 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
9031 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
9032 G_CALLBACK(activate_search_entry_cb
), t
);
9033 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
9034 G_CALLBACK(entry_key_cb
), t
);
9035 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
9036 eb2
= gtk_hbox_new(FALSE
, 0);
9037 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
9038 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
9040 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
9047 create_buffers(struct tab
*t
)
9049 GtkCellRenderer
*renderer
;
9052 view
= gtk_tree_view_new();
9054 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
9056 renderer
= gtk_cell_renderer_text_new();
9057 gtk_tree_view_insert_column_with_attributes
9058 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
9060 renderer
= gtk_cell_renderer_text_new();
9061 gtk_tree_view_insert_column_with_attributes
9062 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
9065 gtk_tree_view_set_model
9066 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
9072 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
9073 GtkTreeViewColumn
*col
, struct tab
*t
)
9078 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9080 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
9083 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
9084 set_current_tab(id
- 1);
9090 /* after tab reordering/creation/removal */
9097 TAILQ_FOREACH(t
, &tabs
, entry
) {
9098 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
9099 if (t
->tab_id
> maxid
)
9102 gtk_widget_show(t
->tab_elems
.sep
);
9105 TAILQ_FOREACH(t
, &tabs
, entry
) {
9106 if (t
->tab_id
== maxid
) {
9107 gtk_widget_hide(t
->tab_elems
.sep
);
9113 /* after active tab change */
9115 recolor_compact_tabs(void)
9121 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9122 TAILQ_FOREACH(t
, &tabs
, entry
)
9123 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
9126 curid
= gtk_notebook_get_current_page(notebook
);
9127 TAILQ_FOREACH(t
, &tabs
, entry
)
9128 if (t
->tab_id
== curid
) {
9129 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
9130 gtk_widget_modify_fg(t
->tab_elems
.label
,
9131 GTK_STATE_NORMAL
, &color
);
9137 set_current_tab(int page_num
)
9139 buffercmd_abort(get_current_tab());
9140 gtk_notebook_set_current_page(notebook
, page_num
);
9141 recolor_compact_tabs();
9145 undo_close_tab_save(struct tab
*t
)
9149 struct undo
*u1
, *u2
;
9151 WebKitWebHistoryItem
*item
;
9153 if ((uri
= get_uri(t
)) == NULL
)
9156 u1
= g_malloc0(sizeof(struct undo
));
9157 u1
->uri
= g_strdup(uri
);
9159 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9161 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
9162 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
9165 /* forward history */
9166 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
9170 u1
->history
= g_list_prepend(u1
->history
,
9171 webkit_web_history_item_copy(item
));
9172 items
= g_list_next(items
);
9177 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
9178 u1
->history
= g_list_prepend(u1
->history
,
9179 webkit_web_history_item_copy(item
));
9183 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
9187 u1
->history
= g_list_prepend(u1
->history
,
9188 webkit_web_history_item_copy(item
));
9189 items
= g_list_next(items
);
9192 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
9194 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
9195 u2
= TAILQ_LAST(&undos
, undo_tailq
);
9196 TAILQ_REMOVE(&undos
, u2
, entry
);
9198 g_list_free(u2
->history
);
9207 delete_tab(struct tab
*t
)
9211 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
9217 * no need to join thread here because it won't access t on completion
9221 TAILQ_REMOVE(&tabs
, t
, entry
);
9223 /* Halt all webkit activity. */
9224 abort_favicon_download(t
);
9225 webkit_web_view_stop_loading(t
->wv
);
9227 /* Save the tab, so we can undo the close. */
9228 undo_close_tab_save(t
);
9230 if (browser_mode
== XT_BM_KIOSK
) {
9231 gtk_widget_destroy(t
->uri_entry
);
9232 gtk_widget_destroy(t
->stop
);
9233 gtk_widget_destroy(t
->js_toggle
);
9236 gtk_widget_destroy(t
->tab_elems
.eventbox
);
9237 gtk_widget_destroy(t
->vbox
);
9241 g_source_remove(t
->search_id
);
9243 g_free(t
->user_agent
);
9244 g_free(t
->stylesheet
);
9248 if (TAILQ_EMPTY(&tabs
)) {
9249 if (browser_mode
== XT_BM_KIOSK
)
9250 create_new_tab(home
, NULL
, 1, -1);
9252 create_new_tab(NULL
, NULL
, 1, -1);
9255 /* recreate session */
9256 if (session_autosave
) {
9262 recolor_compact_tabs();
9266 update_statusbar_zoom(struct tab
*t
)
9269 char s
[16] = { '\0' };
9271 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9272 if ((zoom
<= 0.99 || zoom
>= 1.01))
9273 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
9274 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
9278 setzoom_webkit(struct tab
*t
, int adjust
)
9280 #define XT_ZOOMPERCENT 0.04
9285 show_oops(NULL
, "setzoom_webkit invalid parameters");
9289 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9290 if (adjust
== XT_ZOOM_IN
)
9291 zoom
+= XT_ZOOMPERCENT
;
9292 else if (adjust
== XT_ZOOM_OUT
)
9293 zoom
-= XT_ZOOMPERCENT
;
9294 else if (adjust
> 0)
9295 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
9297 show_oops(t
, "setzoom_webkit invalid zoom value");
9301 if (zoom
< XT_ZOOMPERCENT
)
9302 zoom
= XT_ZOOMPERCENT
;
9303 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
9304 update_statusbar_zoom(t
);
9308 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
9310 struct tab
*t
= (struct tab
*) data
;
9312 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
9314 switch (event
->button
) {
9316 set_current_tab(t
->tab_id
);
9327 append_tab(struct tab
*t
)
9332 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9333 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9337 create_sbe(int width
)
9341 sbe
= gtk_entry_new();
9342 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9343 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9344 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9345 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9346 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9347 gtk_widget_set_size_request(sbe
, width
, -1);
9353 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9358 WebKitWebHistoryItem
*item
;
9362 int sbe_p
= 0, sbe_b
= 0,
9365 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9367 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9368 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9372 t
= g_malloc0(sizeof *t
);
9374 if (title
== NULL
) {
9375 title
= "(untitled)";
9379 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9381 /* label + button for tab */
9382 b
= gtk_hbox_new(FALSE
, 0);
9385 #if GTK_CHECK_VERSION(2, 20, 0)
9386 t
->spinner
= gtk_spinner_new();
9388 t
->label
= gtk_label_new(title
);
9389 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9390 gtk_widget_set_size_request(t
->label
, 100, 0);
9391 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9392 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9393 gtk_widget_set_size_request(b
, 130, 0);
9395 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9396 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9397 #if GTK_CHECK_VERSION(2, 20, 0)
9398 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9402 if (browser_mode
== XT_BM_KIOSK
) {
9403 t
->toolbar
= create_kiosk_toolbar(t
);
9404 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9407 t
->toolbar
= create_toolbar(t
);
9409 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9417 t
->browser_win
= create_browser(t
);
9418 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9420 /* oops message for user feedback */
9421 t
->oops
= gtk_entry_new();
9422 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9423 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9424 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9425 gdk_color_parse(XT_COLOR_RED
, &color
);
9426 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9427 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9428 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9431 t
->cmd
= gtk_entry_new();
9432 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9433 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9434 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9435 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9438 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9440 t
->sbe
.statusbar
= gtk_entry_new();
9441 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9442 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9443 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9444 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9446 /* create these widgets only if specified in statusbar_elems */
9448 t
->sbe
.position
= create_sbe(40);
9449 t
->sbe
.zoom
= create_sbe(40);
9450 t
->sbe
.buffercmd
= create_sbe(60);
9452 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9454 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9457 /* gtk widgets cannot be added to a box twice. sbe_* variables
9458 make sure of this */
9459 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9463 GtkWidget
*sep
= gtk_vseparator_new();
9465 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9466 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9467 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9468 FALSE
, FALSE
, FALSE
);
9473 warnx("flag \"%c\" specified more than "
9474 "once in statusbar_elems\n", *p
);
9478 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9479 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9483 warnx("flag \"%c\" specified more than "
9484 "once in statusbar_elems\n", *p
);
9488 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9489 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9493 warnx("flag \"%c\" specified more than "
9494 "once in statusbar_elems\n", *p
);
9498 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9499 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9502 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9507 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9510 t
->buffers
= create_buffers(t
);
9511 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9513 /* xtp meaning is normal by default */
9514 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9516 /* set empty favicon */
9517 xt_icon_from_name(t
, "text-html");
9519 /* and show it all */
9520 gtk_widget_show_all(b
);
9521 gtk_widget_show_all(t
->vbox
);
9523 /* compact tab bar */
9524 t
->tab_elems
.label
= gtk_label_new(title
);
9525 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9526 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9527 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9528 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9530 t
->tab_elems
.eventbox
= gtk_event_box_new();
9531 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9532 t
->tab_elems
.sep
= gtk_vseparator_new();
9534 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9535 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9536 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9537 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9538 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9539 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9541 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9543 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9545 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9548 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9550 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9552 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9555 id
= position
>= 0 ? position
:
9556 gtk_notebook_get_current_page(notebook
) + 1;
9557 if (id
> gtk_notebook_get_n_pages(notebook
))
9560 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9561 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9562 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9563 t
->tab_elems
.eventbox
, id
);
9568 #if GTK_CHECK_VERSION(2, 20, 0)
9569 /* turn spinner off if we are a new tab without uri */
9571 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9572 gtk_widget_hide(t
->spinner
);
9575 /* make notebook tabs reorderable */
9576 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9578 /* compact tabs clickable */
9579 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9580 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9582 g_object_connect(G_OBJECT(t
->cmd
),
9583 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9584 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9585 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9586 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9587 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9590 /* reuse wv_button_cb to hide oops */
9591 g_object_connect(G_OBJECT(t
->oops
),
9592 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9595 g_signal_connect(t
->buffers
,
9596 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9597 g_object_connect(G_OBJECT(t
->buffers
),
9598 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9600 g_object_connect(G_OBJECT(t
->wv
),
9601 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9602 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9603 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9604 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9605 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9606 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9607 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9608 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9609 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9610 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9611 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9612 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9613 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9614 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9615 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9616 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9618 g_signal_connect(t
->wv
,
9619 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9620 g_signal_connect(t
->wv
,
9621 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9623 /* hijack the unused keys as if we were the browser */
9624 g_object_connect(G_OBJECT(t
->toolbar
),
9625 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9628 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9629 G_CALLBACK(tab_close_cb
), t
);
9632 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9633 /* restore the tab's history */
9634 if (u
&& u
->history
) {
9638 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9639 items
= g_list_next(items
);
9642 item
= g_list_nth_data(u
->history
, u
->back
);
9644 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9647 g_list_free(u
->history
);
9649 webkit_web_back_forward_list_clear(t
->bfl
);
9655 url_set_visibility();
9656 statusbar_set_visibility();
9659 set_current_tab(t
->tab_id
);
9660 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9664 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9668 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9675 recolor_compact_tabs();
9676 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9681 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9687 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9689 if (gtk_notebook_get_current_page(notebook
) == -1)
9692 TAILQ_FOREACH(t
, &tabs
, entry
) {
9693 if (t
->tab_id
== pn
) {
9694 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9697 uri
= get_title(t
, TRUE
);
9698 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9704 /* can't use focus_webview here */
9705 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9712 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9715 struct tab
*t
= NULL
, *tt
;
9719 TAILQ_FOREACH(tt
, &tabs
, entry
)
9720 if (tt
->tab_id
== pn
) {
9726 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9728 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9733 menuitem_response(struct tab
*t
)
9735 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9739 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9741 GtkWidget
*menu
, *menu_items
;
9742 GdkEventButton
*bevent
;
9746 if (event
->type
== GDK_BUTTON_PRESS
) {
9747 bevent
= (GdkEventButton
*) event
;
9748 menu
= gtk_menu_new();
9750 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9751 if ((uri
= get_uri(ti
)) == NULL
)
9752 /* XXX make sure there is something to print */
9753 /* XXX add gui pages in here to look purdy */
9755 menu_items
= gtk_menu_item_new_with_label(uri
);
9756 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9757 gtk_widget_show(menu_items
);
9759 g_signal_connect_swapped((menu_items
),
9760 "activate", G_CALLBACK(menuitem_response
),
9764 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9765 bevent
->button
, bevent
->time
);
9767 /* unref object so it'll free itself when popped down */
9768 #if !GTK_CHECK_VERSION(3, 0, 0)
9769 /* XXX does not need unref with gtk+3? */
9770 g_object_ref_sink(menu
);
9771 g_object_unref(menu
);
9774 return (TRUE
/* eat event */);
9777 return (FALSE
/* propagate */);
9781 icon_size_map(int icon_size
)
9783 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9784 icon_size
> GTK_ICON_SIZE_DIALOG
)
9785 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9791 create_button(char *name
, char *stockid
, int size
)
9793 GtkWidget
*button
, *image
;
9797 rcstring
= g_strdup_printf(
9798 "style \"%s-style\"\n"
9800 " GtkWidget::focus-padding = 0\n"
9801 " GtkWidget::focus-line-width = 0\n"
9805 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9806 gtk_rc_parse_string(rcstring
);
9808 button
= gtk_button_new();
9809 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9810 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9812 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9813 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9814 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9815 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9816 gtk_widget_set_name(button
, name
);
9817 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9823 button_set_stockid(GtkWidget
*button
, char *stockid
)
9827 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9828 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9829 gtk_button_set_image(GTK_BUTTON(button
), image
);
9833 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9836 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9839 if (xterm_workaround
== 0)
9843 * xterm doesn't play nice with clipboards because it clears the
9844 * primary when clicked. We rely on primary being set to properly
9845 * handle middle mouse button clicks (paste). So when someone clears
9846 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9847 * other application behavior (as in DON'T clear primary).
9850 p
= gtk_clipboard_wait_for_text(primary
);
9852 if (gdk_property_get(gdk_get_default_root_window(),
9854 gdk_atom_intern("STRING", FALSE
),
9856 1024 * 1024 /* picked out of my butt */,
9862 /* yes sir, we need to NUL the string */
9864 gtk_clipboard_set_text(primary
, p
, -1);
9878 char file
[PATH_MAX
];
9881 vbox
= gtk_vbox_new(FALSE
, 0);
9882 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9883 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9884 #if !GTK_CHECK_VERSION(3, 0, 0)
9885 /* XXX seems to be needed with gtk+2 */
9886 gtk_notebook_set_tab_hborder(notebook
, 0);
9887 gtk_notebook_set_tab_vborder(notebook
, 0);
9889 gtk_notebook_set_scrollable(notebook
, TRUE
);
9890 gtk_notebook_set_show_border(notebook
, FALSE
);
9891 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9893 abtn
= gtk_button_new();
9894 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9895 gtk_widget_set_size_request(arrow
, -1, -1);
9896 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9897 gtk_widget_set_size_request(abtn
, -1, 20);
9899 #if GTK_CHECK_VERSION(2, 20, 0)
9900 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9902 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9904 /* compact tab bar */
9905 tab_bar
= gtk_hbox_new(TRUE
, 0);
9907 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9908 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9909 gtk_widget_set_size_request(vbox
, -1, -1);
9911 g_object_connect(G_OBJECT(notebook
),
9912 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9914 g_object_connect(G_OBJECT(notebook
),
9915 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9916 NULL
, (char *)NULL
);
9917 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9918 G_CALLBACK(arrow_cb
), NULL
);
9920 main_window
= create_window();
9921 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9924 for (i
= 0; i
< LENGTH(icons
); i
++) {
9925 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9926 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9927 l
= g_list_append(l
, pb
);
9929 gtk_window_set_default_icon_list(l
);
9931 /* clipboard work around */
9932 if (xterm_workaround
)
9934 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9935 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9937 gtk_widget_show_all(abtn
);
9938 gtk_widget_show_all(main_window
);
9939 notebook_tab_set_visibility();
9943 set_hook(void **hook
, char *name
)
9946 errx(1, "set_hook");
9948 if (*hook
== NULL
) {
9949 *hook
= dlsym(RTLD_NEXT
, name
);
9951 errx(1, "can't hook %s", name
);
9955 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9957 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9959 g_return_val_if_fail(cookie1
, FALSE
);
9960 g_return_val_if_fail(cookie2
, FALSE
);
9962 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9963 !strcmp (cookie1
->value
, cookie2
->value
) &&
9964 !strcmp (cookie1
->path
, cookie2
->path
) &&
9965 !strcmp (cookie1
->domain
, cookie2
->domain
));
9969 transfer_cookies(void)
9972 SoupCookie
*sc
, *pc
;
9974 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9976 for (;cf
; cf
= cf
->next
) {
9978 sc
= soup_cookie_copy(pc
);
9979 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9982 soup_cookies_free(cf
);
9986 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9991 print_cookie("soup_cookie_jar_delete_cookie", c
);
9993 if (cookies_enabled
== 0)
9996 if (jar
== NULL
|| c
== NULL
)
9999 /* find and remove from persistent jar */
10000 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
10002 for (;cf
; cf
= cf
->next
) {
10004 if (soup_cookie_equal(ci
, c
)) {
10005 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
10010 soup_cookies_free(cf
);
10012 /* delete from session jar */
10013 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
10017 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
10019 struct domain
*d
= NULL
;
10023 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
10024 jar
, p_cookiejar
, s_cookiejar
);
10026 if (cookies_enabled
== 0)
10029 /* see if we are up and running */
10030 if (p_cookiejar
== NULL
) {
10031 _soup_cookie_jar_add_cookie(jar
, cookie
);
10034 /* disallow p_cookiejar adds, shouldn't happen */
10035 if (jar
== p_cookiejar
)
10039 if (jar
== NULL
|| cookie
== NULL
)
10042 if (enable_cookie_whitelist
&&
10043 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
10045 DNPRINTF(XT_D_COOKIE
,
10046 "soup_cookie_jar_add_cookie: reject %s\n",
10048 if (save_rejected_cookies
) {
10049 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
10050 show_oops(NULL
, "can't open reject cookie file");
10053 fseek(r_cookie_f
, 0, SEEK_END
);
10054 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
10055 cookie
->http_only
? "#HttpOnly_" : "",
10057 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
10059 cookie
->secure
? "TRUE" : "FALSE",
10061 (gulong
)soup_date_to_time_t(cookie
->expires
) :
10065 fflush(r_cookie_f
);
10066 fclose(r_cookie_f
);
10068 if (!allow_volatile_cookies
)
10072 if (cookie
->expires
== NULL
&& session_timeout
) {
10073 soup_cookie_set_expires(cookie
,
10074 soup_date_new_from_now(session_timeout
));
10075 print_cookie("modified add cookie", cookie
);
10078 /* see if we are white listed for persistence */
10079 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
10080 /* add to persistent jar */
10081 c
= soup_cookie_copy(cookie
);
10082 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
10083 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
10086 /* add to session jar */
10087 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
10088 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
10092 setup_cookies(void)
10094 char file
[PATH_MAX
];
10096 set_hook((void *)&_soup_cookie_jar_add_cookie
,
10097 "soup_cookie_jar_add_cookie");
10098 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
10099 "soup_cookie_jar_delete_cookie");
10101 if (cookies_enabled
== 0)
10105 * the following code is intricate due to overriding several libsoup
10107 * do not alter order of these operations.
10110 /* rejected cookies */
10111 if (save_rejected_cookies
)
10112 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
10115 /* persistent cookies */
10116 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
10117 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
10119 /* session cookies */
10120 s_cookiejar
= soup_cookie_jar_new();
10121 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
10122 cookie_policy
, (void *)NULL
);
10123 transfer_cookies();
10125 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
10129 setup_proxy(char *uri
)
10132 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
10133 soup_uri_free(proxy_uri
);
10137 if (http_proxy
!= uri
) {
10138 g_free(http_proxy
);
10144 http_proxy
= g_strdup(uri
);
10145 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
10146 proxy_uri
= soup_uri_new(http_proxy
);
10147 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
10148 g_object_set(session
, "proxy-uri", proxy_uri
,
10154 set_http_proxy(char *proxy
)
10161 /* see if we need to clear it instead */
10162 if (strlen(proxy
) == 0) {
10167 uri
= soup_uri_new(proxy
);
10168 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
10171 setup_proxy(proxy
);
10173 soup_uri_free(uri
);
10179 send_cmd_to_socket(char *cmd
)
10181 int s
, len
, rv
= 1;
10182 struct sockaddr_un sa
;
10184 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10185 warnx("%s: socket", __func__
);
10189 sa
.sun_family
= AF_UNIX
;
10190 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10191 work_dir
, XT_SOCKET_FILE
);
10192 len
= SUN_LEN(&sa
);
10194 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10195 warnx("%s: connect", __func__
);
10199 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
10200 warnx("%s: send", __func__
);
10211 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
10214 char str
[XT_MAX_URL_LENGTH
];
10215 socklen_t t
= sizeof(struct sockaddr_un
);
10216 struct sockaddr_un sa
;
10221 gint fd
= g_io_channel_unix_get_fd(source
);
10223 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
10228 if (getpeereid(s
, &uid
, &gid
) == -1) {
10229 warn("getpeereid");
10232 if (uid
!= getuid() || gid
!= getgid()) {
10233 warnx("unauthorized user");
10239 warnx("not a valid user");
10243 n
= recv(s
, str
, sizeof(str
), 0);
10247 tt
= TAILQ_LAST(&tabs
, tab_list
);
10248 cmd_execute(tt
, str
);
10255 int s
, len
, rv
= 1;
10256 struct sockaddr_un sa
;
10258 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10259 warn("is_running: socket");
10263 sa
.sun_family
= AF_UNIX
;
10264 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10265 work_dir
, XT_SOCKET_FILE
);
10266 len
= SUN_LEN(&sa
);
10268 /* connect to see if there is a listener */
10269 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
10270 rv
= 0; /* not running */
10272 rv
= 1; /* already running */
10283 struct sockaddr_un sa
;
10285 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10286 warn("build_socket: socket");
10290 sa
.sun_family
= AF_UNIX
;
10291 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10292 work_dir
, XT_SOCKET_FILE
);
10293 len
= SUN_LEN(&sa
);
10295 /* connect to see if there is a listener */
10296 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10297 /* no listener so we will */
10298 unlink(sa
.sun_path
);
10300 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10301 warn("build_socket: bind");
10305 if (listen(s
, 1) == -1) {
10306 warn("build_socket: listen");
10319 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10320 GtkTreeIter
*iter
, struct tab
*t
)
10324 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10325 load_uri(t
, value
);
10332 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10333 GtkTreeIter
*iter
, struct tab
*t
)
10337 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10338 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10339 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10346 completion_add_uri(const gchar
*uri
)
10350 /* add uri to list_store */
10351 gtk_list_store_append(completion_model
, &iter
);
10352 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10356 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10357 GtkTreeIter
*iter
, gpointer user_data
)
10360 gboolean match
= FALSE
;
10362 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10368 match
= match_uri(value
, key
);
10375 completion_add(struct tab
*t
)
10377 /* enable completion for tab */
10378 t
->completion
= gtk_entry_completion_new();
10379 gtk_entry_completion_set_text_column(t
->completion
, 0);
10380 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10381 gtk_entry_completion_set_model(t
->completion
,
10382 GTK_TREE_MODEL(completion_model
));
10383 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10385 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10386 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10387 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10388 G_CALLBACK(completion_select_cb
), t
);
10389 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10390 G_CALLBACK(completion_hover_cb
), t
);
10398 if (stat(dir
, &sb
)) {
10399 if (mkdir(dir
, S_IRWXU
) == -1)
10400 err(1, "mkdir %s", dir
);
10401 if (stat(dir
, &sb
))
10402 err(1, "stat %s", dir
);
10404 if (S_ISDIR(sb
.st_mode
) == 0)
10405 errx(1, "%s not a dir", dir
);
10406 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10407 warnx("fixing invalid permissions on %s", dir
);
10408 if (chmod(dir
, S_IRWXU
) == -1)
10409 err(1, "chmod %s", dir
);
10417 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10421 GStaticRecMutex my_gdk_mtx
= G_STATIC_REC_MUTEX_INIT
;
10422 volatile int mtx_depth
;
10426 * The linux flash plugin violates the gdk locking mechanism.
10427 * Work around the issue by using a recursive mutex with some match applied
10428 * to see if we hit a buggy condition.
10430 * The following code is painful so just don't read it. It really doesn't
10431 * make much sense but seems to work.
10436 g_static_rec_mutex_lock(&my_gdk_mtx
);
10439 if (mtx_depth
<= 0) {
10440 /* should not happen */
10441 show_oops(NULL
, "negative mutex locking bug, trying to "
10443 fprintf(stderr
, "negative mutex locking bug, trying to "
10445 g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10446 g_static_rec_mutex_lock(&my_gdk_mtx
);
10451 if (mtx_depth
!= 1) {
10452 /* decrease mutext depth to 1 */
10454 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10456 } while (mtx_depth
> 1);
10465 /* if mutex depth isn't 1 then something went bad */
10466 if (mtx_depth
!= 1) {
10467 x
= g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10469 /* should not happen */
10470 show_oops(NULL
, "mutex unlocking bug, trying to "
10472 fprintf(stderr
, "mutex unlocking bug, trying to "
10476 if (mtx_complain
== 0) {
10477 show_oops(NULL
, "buggy mutex implementation detected, "
10478 "work around implemented");
10479 fprintf(stderr
, "buggy mutex implementation detected, "
10480 "work around implemented");
10487 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10491 main(int argc
, char *argv
[])
10494 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10495 char conf
[PATH_MAX
] = { '\0' };
10496 char file
[PATH_MAX
];
10497 char *env_proxy
= NULL
;
10501 struct sigaction sact
;
10502 GIOChannel
*channel
;
10509 g_thread_init(NULL
);
10510 gdk_threads_set_lock_functions(mtx_lock
, mtx_unlock
);
10511 gdk_threads_init();
10512 gdk_threads_enter();
10514 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pthread
);
10516 gtk_init(&argc
, &argv
);
10518 gnutls_global_init();
10520 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10525 RB_INIT(&downloads
);
10527 TAILQ_INIT(&sessions
);
10530 TAILQ_INIT(&aliases
);
10531 TAILQ_INIT(&undos
);
10537 /* fiddle with ulimits */
10538 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10541 /* just use them all */
10542 rlp
.rlim_cur
= rlp
.rlim_max
;
10543 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10545 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10547 else if (rlp
.rlim_cur
<= 256)
10548 startpage_add("%s requires at least 256 file "
10549 "descriptors, currently it has up to %d available",
10550 __progname
, rlp
.rlim_cur
);
10553 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10562 errx(0 , "Version: %s", version
);
10565 strlcpy(conf
, optarg
, sizeof(conf
));
10568 strlcpy(named_session
, optarg
, sizeof(named_session
));
10587 init_keybindings();
10589 /* generate session keys for xtp pages */
10590 generate_xtp_session_key(&dl_session_key
);
10591 generate_xtp_session_key(&hl_session_key
);
10592 generate_xtp_session_key(&cl_session_key
);
10593 generate_xtp_session_key(&fl_session_key
);
10596 bzero(&sact
, sizeof(sact
));
10597 sigemptyset(&sact
.sa_mask
);
10598 sact
.sa_handler
= sigchild
;
10599 sact
.sa_flags
= SA_NOCLDSTOP
;
10600 sigaction(SIGCHLD
, &sact
, NULL
);
10602 /* set download dir */
10603 pwd
= getpwuid(getuid());
10605 errx(1, "invalid user %d", getuid());
10606 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10608 /* compile buffer command regexes */
10611 /* set default string settings */
10612 home
= g_strdup("https://www.cyphertite.com");
10613 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10614 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10615 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10616 cmd_font_name
= g_strdup("monospace normal 9");
10617 oops_font_name
= g_strdup("monospace normal 9");
10618 statusbar_font_name
= g_strdup("monospace normal 9");
10619 tabbar_font_name
= g_strdup("monospace normal 9");
10620 statusbar_elems
= g_strdup("BP");
10621 encoding
= g_strdup("ISO-8859-1");
10623 /* read config file */
10624 if (strlen(conf
) == 0)
10625 snprintf(conf
, sizeof conf
, "%s/.%s",
10626 pwd
->pw_dir
, XT_CONF_FILE
);
10627 config_parse(conf
, 0);
10630 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10631 oops_font
= pango_font_description_from_string(oops_font_name
);
10632 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10633 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10635 /* working directory */
10636 if (strlen(work_dir
) == 0)
10637 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10638 pwd
->pw_dir
, XT_DIR
);
10641 /* icon cache dir */
10642 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10643 xxx_dir(cache_dir
);
10646 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10647 xxx_dir(certs_dir
);
10650 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10651 work_dir
, XT_SESSIONS_DIR
);
10652 xxx_dir(sessions_dir
);
10654 /* runtime settings that can override config file */
10655 if (runtime_settings
[0] != '\0')
10656 config_parse(runtime_settings
, 1);
10659 if (!strcmp(download_dir
, pwd
->pw_dir
))
10660 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10661 xxx_dir(download_dir
);
10663 /* favorites file */
10664 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10665 if (stat(file
, &sb
)) {
10666 warnx("favorites file doesn't exist, creating it");
10667 if ((f
= fopen(file
, "w")) == NULL
)
10668 err(1, "favorites");
10672 /* quickmarks file */
10673 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10674 if (stat(file
, &sb
)) {
10675 warnx("quickmarks file doesn't exist, creating it");
10676 if ((f
= fopen(file
, "w")) == NULL
)
10677 err(1, "quickmarks");
10681 /* search history */
10682 if (history_autosave
) {
10683 snprintf(search_file
, sizeof search_file
, "%s/%s",
10684 work_dir
, XT_SEARCH_FILE
);
10685 if (stat(search_file
, &sb
)) {
10686 warnx("search history file doesn't exist, creating it");
10687 if ((f
= fopen(search_file
, "w")) == NULL
)
10688 err(1, "search_history");
10691 history_read(&shl
, search_file
, &search_history_count
);
10694 /* command history */
10695 if (history_autosave
) {
10696 snprintf(command_file
, sizeof command_file
, "%s/%s",
10697 work_dir
, XT_COMMAND_FILE
);
10698 if (stat(command_file
, &sb
)) {
10699 warnx("command history file doesn't exist, creating it");
10700 if ((f
= fopen(command_file
, "w")) == NULL
)
10701 err(1, "command_history");
10704 history_read(&chl
, command_file
, &cmd_history_count
);
10708 session
= webkit_get_default_session();
10713 if (stat(ssl_ca_file
, &sb
)) {
10714 warnx("no CA file: %s", ssl_ca_file
);
10715 g_free(ssl_ca_file
);
10716 ssl_ca_file
= NULL
;
10718 g_object_set(session
,
10719 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10720 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10724 /* guess_search regex */
10725 if (url_regex
== NULL
)
10726 url_regex
= g_strdup(XT_URL_REGEX
);
10728 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10729 startpage_add("invalid url regex %s", url_regex
);
10732 env_proxy
= getenv("http_proxy");
10734 setup_proxy(env_proxy
);
10736 setup_proxy(http_proxy
);
10739 send_cmd_to_socket(argv
[0]);
10743 /* set some connection parameters */
10744 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10745 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10748 /* see if there is already an xxxterm running */
10749 if (single_instance
&& is_running()) {
10751 warnx("already running");
10756 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10757 send_cmd_to_socket(cmd
);
10767 /* uri completion */
10768 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10771 buffers_store
= gtk_list_store_new
10772 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10778 notebook_tab_set_visibility();
10780 if (save_global_history
)
10781 restore_global_history();
10783 /* restore session list */
10784 restore_sessions_list();
10786 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10787 restore_saved_tabs();
10789 a
.s
= named_session
;
10790 a
.i
= XT_SES_DONOTHING
;
10791 open_tabs(NULL
, &a
);
10794 /* see if we have an exception */
10795 if (!TAILQ_EMPTY(&spl
)) {
10796 create_new_tab("about:startpage", NULL
, focus
, -1);
10801 create_new_tab(argv
[0], NULL
, focus
, -1);
10808 if (TAILQ_EMPTY(&tabs
))
10809 create_new_tab(home
, NULL
, 1, -1);
10812 if ((s
= build_socket()) != -1) {
10813 channel
= g_io_channel_unix_new(s
);
10814 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10820 gdk_threads_leave();
10821 g_static_rec_mutex_unlock_full(&my_gdk_mtx
); /* just in case */
10824 gnutls_global_deinit();