3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
8 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * create privacy browsing
26 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #include <bsd/stdlib.h>
49 # if !defined(sane_libbsd_headers)
50 void arc4random_buf(void *, size_t);
52 #elif defined(__FreeBSD__)
54 #include "freebsd/util.h"
60 #include <sys/queue.h>
61 #include <sys/resource.h>
62 #include <sys/socket.h>
68 #include <gdk/gdkkeysyms.h>
70 #if GTK_CHECK_VERSION(3,0,0)
71 /* we still use GDK_* instead of GDK_KEY_* */
72 #include <gdk/gdkkeysyms-compat.h>
75 #include <webkit/webkit.h>
76 #include <libsoup/soup.h>
77 #include <JavaScriptCore/JavaScript.h>
78 #include <gnutls/gnutls.h>
79 #include <gnutls/x509.h>
82 #include "javascript.h"
84 /* comment if you don't want to use threads */
90 GCRY_THREAD_OPTION_PTHREAD_IMPL
;
95 javascript.h borrowed from vimprobable2 under the following license:
97 Copyright (c) 2009 Leon Winter
98 Copyright (c) 2009 Hannes Schueller
99 Copyright (c) 2009 Matto Fransen
101 Permission is hereby granted, free of charge, to any person obtaining a copy
102 of this software and associated documentation files (the "Software"), to deal
103 in the Software without restriction, including without limitation the rights
104 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
105 copies of the Software, and to permit persons to whom the Software is
106 furnished to do so, subject to the following conditions:
108 The above copyright notice and this permission notice shall be included in
109 all copies or substantial portions of the Software.
111 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
112 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
113 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
114 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
115 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
116 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
120 static char *version
= XXXTERM_VERSION
;
122 /* hooked functions */
123 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
124 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
129 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
130 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
131 #define XT_D_MOVE 0x0001
132 #define XT_D_KEY 0x0002
133 #define XT_D_TAB 0x0004
134 #define XT_D_URL 0x0008
135 #define XT_D_CMD 0x0010
136 #define XT_D_NAV 0x0020
137 #define XT_D_DOWNLOAD 0x0040
138 #define XT_D_CONFIG 0x0080
139 #define XT_D_JS 0x0100
140 #define XT_D_FAVORITE 0x0200
141 #define XT_D_PRINTING 0x0400
142 #define XT_D_COOKIE 0x0800
143 #define XT_D_KEYBINDING 0x1000
144 #define XT_D_CLIP 0x2000
145 #define XT_D_BUFFERCMD 0x4000
146 u_int32_t swm_debug
= 0
164 #define DPRINTF(x...)
165 #define DNPRINTF(n,x...)
168 #define LENGTH(x) (sizeof x / sizeof x[0])
169 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
170 ~(GDK_BUTTON1_MASK) & \
171 ~(GDK_BUTTON2_MASK) & \
172 ~(GDK_BUTTON3_MASK) & \
173 ~(GDK_BUTTON4_MASK) & \
176 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
187 TAILQ_ENTRY(tab
) entry
;
189 GtkWidget
*tab_content
;
198 GtkWidget
*uri_entry
;
199 GtkWidget
*search_entry
;
201 GtkWidget
*browser_win
;
202 GtkWidget
*statusbar_box
;
204 GtkWidget
*statusbar
;
205 GtkWidget
*buffercmd
;
216 GtkWidget
*js_toggle
;
217 GtkEntryCompletion
*completion
;
221 WebKitWebHistoryItem
*item
;
222 WebKitWebBackForwardList
*bfl
;
225 WebKitNetworkRequest
*icon_request
;
226 WebKitDownload
*icon_download
;
227 gchar
*icon_dest_uri
;
229 /* adjustments for browser */
232 GtkAdjustment
*adjust_h
;
233 GtkAdjustment
*adjust_v
;
239 int xtp_meaning
; /* identifies dls/favorites */
241 int popup
; /* 1 if cmd_entry has popup visible */
243 /* https thread stuff */
249 #define XT_HINT_NONE (0)
250 #define XT_HINT_NUMERICAL (1)
251 #define XT_HINT_ALPHANUM (2)
255 /* custom stylesheet */
265 WebKitWebSettings
*settings
;
269 double mark
[XT_NOMARKS
];
271 TAILQ_HEAD(tab_list
, tab
);
274 RB_ENTRY(history
) entry
;
278 RB_HEAD(history_list
, history
);
281 TAILQ_ENTRY(session
) entry
;
284 TAILQ_HEAD(session_list
, session
);
287 RB_ENTRY(download
) entry
;
289 WebKitDownload
*download
;
292 RB_HEAD(download_list
, download
);
295 RB_ENTRY(domain
) entry
;
297 int handy
; /* app use */
299 RB_HEAD(domain_list
, domain
);
302 TAILQ_ENTRY(undo
) entry
;
305 int back
; /* Keeps track of how many back
306 * history items there are. */
308 TAILQ_HEAD(undo_tailq
, undo
);
312 TAILQ_ENTRY(sp
) entry
;
314 TAILQ_HEAD(sp_list
, sp
);
316 struct command_entry
{
318 TAILQ_ENTRY(command_entry
) entry
;
320 TAILQ_HEAD(command_list
, command_entry
);
322 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
323 int next_download_id
= 1;
332 #define XT_NAME ("XXXTerm")
333 #define XT_DIR (".xxxterm")
334 #define XT_CACHE_DIR ("cache")
335 #define XT_CERT_DIR ("certs/")
336 #define XT_SESSIONS_DIR ("sessions/")
337 #define XT_CONF_FILE ("xxxterm.conf")
338 #define XT_FAVS_FILE ("favorites")
339 #define XT_QMARKS_FILE ("quickmarks")
340 #define XT_SAVED_TABS_FILE ("main_session")
341 #define XT_RESTART_TABS_FILE ("restart_tabs")
342 #define XT_SOCKET_FILE ("socket")
343 #define XT_HISTORY_FILE ("history")
344 #define XT_REJECT_FILE ("rejected.txt")
345 #define XT_COOKIE_FILE ("cookies.txt")
346 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
347 #define XT_SEARCH_FILE ("search_history")
348 #define XT_COMMAND_FILE ("command_history")
349 #define XT_CB_HANDLED (TRUE)
350 #define XT_CB_PASSTHROUGH (FALSE)
351 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
352 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
353 #define XT_DLMAN_REFRESH "10"
354 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
355 "td{overflow: hidden;" \
356 " padding: 2px 2px 2px 2px;" \
357 " border: 1px solid black;" \
358 " vertical-align:top;" \
359 " word-wrap: break-word}\n" \
360 "tr:hover{background: #ffff99}\n" \
361 "th{background-color: #cccccc;" \
362 " border: 1px solid black}\n" \
363 "table{width: 100%%;" \
364 " border: 1px black solid;" \
365 " border-collapse:collapse}\n" \
367 "border: 1px solid black;" \
370 ".progress-inner{float: left;" \
372 " background: green}\n" \
373 ".dlstatus{font-size: small;" \
374 " text-align: center}\n" \
376 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
377 #define XT_MAX_UNDO_CLOSE_TAB (32)
378 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
379 #define XT_PRINT_EXTRA_MARGIN 10
380 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
381 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
384 #define XT_COLOR_RED "#cc0000"
385 #define XT_COLOR_YELLOW "#ffff66"
386 #define XT_COLOR_BLUE "lightblue"
387 #define XT_COLOR_GREEN "#99ff66"
388 #define XT_COLOR_WHITE "white"
389 #define XT_COLOR_BLACK "black"
391 #define XT_COLOR_CT_BACKGROUND "#000000"
392 #define XT_COLOR_CT_INACTIVE "#dddddd"
393 #define XT_COLOR_CT_ACTIVE "#bbbb00"
394 #define XT_COLOR_CT_SEPARATOR "#555555"
396 #define XT_COLOR_SB_SEPARATOR "#555555"
398 #define XT_PROTO_DELIM "://"
401 * xxxterm "protocol" (xtp)
402 * We use this for managing stuff like downloads and favorites. They
403 * make magical HTML pages in memory which have xxxt:// links in order
404 * to communicate with xxxterm's internals. These links take the format:
405 * xxxt://class/session_key/action/arg
407 * Don't begin xtp class/actions as 0. atoi returns that on error.
409 * Typically we have not put addition of items in this framework, as
410 * adding items is either done via an ex-command or via a keybinding instead.
413 #define XT_XTP_STR "xxxt://"
415 /* XTP classes (xxxt://<class>) */
416 #define XT_XTP_INVALID 0 /* invalid */
417 #define XT_XTP_DL 1 /* downloads */
418 #define XT_XTP_HL 2 /* history */
419 #define XT_XTP_CL 3 /* cookies */
420 #define XT_XTP_FL 4 /* favorites */
422 /* XTP download actions */
423 #define XT_XTP_DL_LIST 1
424 #define XT_XTP_DL_CANCEL 2
425 #define XT_XTP_DL_REMOVE 3
427 /* XTP history actions */
428 #define XT_XTP_HL_LIST 1
429 #define XT_XTP_HL_REMOVE 2
431 /* XTP cookie actions */
432 #define XT_XTP_CL_LIST 1
433 #define XT_XTP_CL_REMOVE 2
435 /* XTP cookie actions */
436 #define XT_XTP_FL_LIST 1
437 #define XT_XTP_FL_REMOVE 2
440 #define XT_MOVE_INVALID (0)
441 #define XT_MOVE_DOWN (1)
442 #define XT_MOVE_UP (2)
443 #define XT_MOVE_BOTTOM (3)
444 #define XT_MOVE_TOP (4)
445 #define XT_MOVE_PAGEDOWN (5)
446 #define XT_MOVE_PAGEUP (6)
447 #define XT_MOVE_HALFDOWN (7)
448 #define XT_MOVE_HALFUP (8)
449 #define XT_MOVE_LEFT (9)
450 #define XT_MOVE_FARLEFT (10)
451 #define XT_MOVE_RIGHT (11)
452 #define XT_MOVE_FARRIGHT (12)
453 #define XT_MOVE_PERCENT (13)
455 #define XT_QMARK_SET (0)
456 #define XT_QMARK_OPEN (1)
457 #define XT_QMARK_TAB (2)
459 #define XT_MARK_SET (0)
460 #define XT_MARK_GOTO (1)
462 #define XT_TAB_LAST (-4)
463 #define XT_TAB_FIRST (-3)
464 #define XT_TAB_PREV (-2)
465 #define XT_TAB_NEXT (-1)
466 #define XT_TAB_INVALID (0)
467 #define XT_TAB_NEW (1)
468 #define XT_TAB_DELETE (2)
469 #define XT_TAB_DELQUIT (3)
470 #define XT_TAB_OPEN (4)
471 #define XT_TAB_UNDO_CLOSE (5)
472 #define XT_TAB_SHOW (6)
473 #define XT_TAB_HIDE (7)
474 #define XT_TAB_NEXTSTYLE (8)
476 #define XT_NAV_INVALID (0)
477 #define XT_NAV_BACK (1)
478 #define XT_NAV_FORWARD (2)
479 #define XT_NAV_RELOAD (3)
481 #define XT_FOCUS_INVALID (0)
482 #define XT_FOCUS_URI (1)
483 #define XT_FOCUS_SEARCH (2)
485 #define XT_SEARCH_INVALID (0)
486 #define XT_SEARCH_NEXT (1)
487 #define XT_SEARCH_PREV (2)
489 #define XT_PASTE_CURRENT_TAB (0)
490 #define XT_PASTE_NEW_TAB (1)
492 #define XT_ZOOM_IN (-1)
493 #define XT_ZOOM_OUT (-2)
494 #define XT_ZOOM_NORMAL (100)
496 #define XT_URL_SHOW (1)
497 #define XT_URL_HIDE (2)
499 #define XT_WL_TOGGLE (1<<0)
500 #define XT_WL_ENABLE (1<<1)
501 #define XT_WL_DISABLE (1<<2)
502 #define XT_WL_FQDN (1<<3) /* default */
503 #define XT_WL_TOPLEVEL (1<<4)
504 #define XT_WL_PERSISTENT (1<<5)
505 #define XT_WL_SESSION (1<<6)
506 #define XT_WL_RELOAD (1<<7)
508 #define XT_SHOW (1<<7)
509 #define XT_DELETE (1<<8)
510 #define XT_SAVE (1<<9)
511 #define XT_OPEN (1<<10)
513 #define XT_CMD_OPEN (0)
514 #define XT_CMD_OPEN_CURRENT (1)
515 #define XT_CMD_TABNEW (2)
516 #define XT_CMD_TABNEW_CURRENT (3)
518 #define XT_STATUS_NOTHING (0)
519 #define XT_STATUS_LINK (1)
520 #define XT_STATUS_URI (2)
521 #define XT_STATUS_LOADING (3)
523 #define XT_SES_DONOTHING (0)
524 #define XT_SES_CLOSETABS (1)
526 #define XT_BM_NORMAL (0)
527 #define XT_BM_WHITELIST (1)
528 #define XT_BM_KIOSK (2)
530 #define XT_PREFIX (1<<0)
531 #define XT_USERARG (1<<1)
532 #define XT_URLARG (1<<2)
533 #define XT_INTARG (1<<3)
534 #define XT_SESSARG (1<<4)
535 #define XT_SETARG (1<<5)
537 #define XT_TABS_NORMAL 0
538 #define XT_TABS_COMPACT 1
540 #define XT_BUFCMD_SZ (8)
548 TAILQ_ENTRY(mime_type
) entry
;
550 TAILQ_HEAD(mime_type_list
, mime_type
);
556 TAILQ_ENTRY(alias
) entry
;
558 TAILQ_HEAD(alias_list
, alias
);
560 /* settings that require restart */
561 int tabless
= 0; /* allow only 1 tab */
562 int enable_socket
= 0;
563 int single_instance
= 0; /* only allow one xxxterm to run */
564 int fancy_bar
= 1; /* fancy toolbar */
565 int browser_mode
= XT_BM_NORMAL
;
566 int enable_localstorage
= 1;
567 char *statusbar_elems
= NULL
;
569 /* runtime settings */
570 int show_tabs
= 1; /* show tabs on notebook */
571 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
572 int show_url
= 1; /* show url toolbar on notebook */
573 int show_statusbar
= 0; /* vimperator style status bar */
574 int ctrl_click_focus
= 0; /* ctrl click gets focus */
575 int cookies_enabled
= 1; /* enable cookies */
576 int read_only_cookies
= 0; /* enable to not write cookies */
577 int enable_scripts
= 1;
578 int enable_plugins
= 0;
579 gfloat default_zoom_level
= 1.0;
580 char default_script
[PATH_MAX
];
581 int window_height
= 768;
582 int window_width
= 1024;
583 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
584 int refresh_interval
= 10; /* download refresh interval */
585 int enable_cookie_whitelist
= 0;
586 int enable_js_whitelist
= 0;
587 int session_timeout
= 3600; /* cookie session timeout */
588 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
589 char *ssl_ca_file
= NULL
;
590 char *resource_dir
= NULL
;
591 gboolean ssl_strict_certs
= FALSE
;
592 int append_next
= 1; /* append tab after current tab */
594 char *search_string
= NULL
;
595 char *http_proxy
= NULL
;
596 char download_dir
[PATH_MAX
];
597 char runtime_settings
[PATH_MAX
]; /* override of settings */
598 int allow_volatile_cookies
= 0;
599 int save_global_history
= 0; /* save global history to disk */
600 char *user_agent
= NULL
;
601 int save_rejected_cookies
= 0;
602 int session_autosave
= 0;
603 int guess_search
= 0;
604 int dns_prefetch
= FALSE
;
605 gint max_connections
= 25;
606 gint max_host_connections
= 5;
607 gint enable_spell_checking
= 0;
608 char *spell_check_languages
= NULL
;
609 int xterm_workaround
= 0;
610 char *url_regex
= NULL
;
611 int history_autosave
= 0;
612 char search_file
[PATH_MAX
];
613 char command_file
[PATH_MAX
];
614 char *encoding
= NULL
;
616 char *cmd_font_name
= NULL
;
617 char *oops_font_name
= NULL
;
618 char *statusbar_font_name
= NULL
;
619 char *tabbar_font_name
= NULL
;
620 PangoFontDescription
*cmd_font
;
621 PangoFontDescription
*oops_font
;
622 PangoFontDescription
*statusbar_font
;
623 PangoFontDescription
*tabbar_font
;
624 char *qmarks
[XT_NOMARKS
];
626 int btn_down
; /* M1 down in any wv */
627 regex_t url_re
; /* guess_search regex */
631 int set_browser_mode(struct settings
*, char *);
632 int set_cookie_policy(struct settings
*, char *);
633 int set_download_dir(struct settings
*, char *);
634 int set_default_script(struct settings
*, char *);
635 int set_runtime_dir(struct settings
*, char *);
636 int set_tab_style(struct settings
*, char *);
637 int set_work_dir(struct settings
*, char *);
638 int add_alias(struct settings
*, char *);
639 int add_mime_type(struct settings
*, char *);
640 int add_cookie_wl(struct settings
*, char *);
641 int add_js_wl(struct settings
*, char *);
642 int add_kb(struct settings
*, char *);
643 void button_set_stockid(GtkWidget
*, char *);
644 GtkWidget
* create_button(char *, char *, int);
646 char *get_browser_mode(struct settings
*);
647 char *get_cookie_policy(struct settings
*);
648 char *get_download_dir(struct settings
*);
649 char *get_default_script(struct settings
*);
650 char *get_runtime_dir(struct settings
*);
651 char *get_tab_style(struct settings
*);
652 char *get_work_dir(struct settings
*);
653 void startpage_add(const char *, ...);
655 void walk_alias(struct settings
*, void (*)(struct settings
*,
656 char *, void *), void *);
657 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
658 char *, void *), void *);
659 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
660 char *, void *), void *);
661 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
663 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
664 char *, void *), void *);
666 void recalc_tabs(void);
667 void recolor_compact_tabs(void);
668 void set_current_tab(int page_num
);
669 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
670 void marks_clear(struct tab
*t
);
672 int set_http_proxy(char *);
675 int (*set
)(struct settings
*, char *);
676 char *(*get
)(struct settings
*);
677 void (*walk
)(struct settings
*,
678 void (*cb
)(struct settings
*, char *, void *),
682 struct special s_browser_mode
= {
688 struct special s_cookie
= {
694 struct special s_alias
= {
700 struct special s_mime
= {
706 struct special s_js
= {
712 struct special s_kb
= {
718 struct special s_cookie_wl
= {
724 struct special s_default_script
= {
730 struct special s_download_dir
= {
736 struct special s_work_dir
= {
742 struct special s_tab_style
= {
751 #define XT_S_INVALID (0)
754 #define XT_S_FLOAT (3)
756 #define XT_SF_RESTART (1<<0)
757 #define XT_SF_RUNTIME (1<<1)
762 int (*activate
)(char *);
764 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
765 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
766 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
767 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
768 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
769 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
770 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
771 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
772 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
773 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
774 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
775 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
776 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
777 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
778 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
779 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
780 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
781 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
782 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
783 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
784 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
785 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
786 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
787 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
788 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
789 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
790 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
791 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
792 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
793 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
794 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
795 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
796 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
797 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
798 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
799 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
800 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
801 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
802 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
803 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
804 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
805 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
806 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
807 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
808 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
809 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
810 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
811 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
814 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
815 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
816 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
817 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
819 /* runtime settings */
820 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
821 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
822 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
823 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
824 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
827 int about(struct tab
*, struct karg
*);
828 int blank(struct tab
*, struct karg
*);
829 int ca_cmd(struct tab
*, struct karg
*);
830 int cookie_show_wl(struct tab
*, struct karg
*);
831 int js_show_wl(struct tab
*, struct karg
*);
832 int help(struct tab
*, struct karg
*);
833 int set(struct tab
*, struct karg
*);
834 int stats(struct tab
*, struct karg
*);
835 int marco(struct tab
*, struct karg
*);
836 int startpage(struct tab
*, struct karg
*);
837 const char * marco_message(int *);
838 int xtp_page_cl(struct tab
*, struct karg
*);
839 int xtp_page_dl(struct tab
*, struct karg
*);
840 int xtp_page_fl(struct tab
*, struct karg
*);
841 int xtp_page_hl(struct tab
*, struct karg
*);
842 void xt_icon_from_file(struct tab
*, char *);
843 const gchar
*get_uri(struct tab
*);
844 const gchar
*get_title(struct tab
*, bool);
846 #define XT_URI_ABOUT ("about:")
847 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
848 #define XT_URI_ABOUT_ABOUT ("about")
849 #define XT_URI_ABOUT_BLANK ("blank")
850 #define XT_URI_ABOUT_CERTS ("certs")
851 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
852 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
853 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
854 #define XT_URI_ABOUT_FAVORITES ("favorites")
855 #define XT_URI_ABOUT_HELP ("help")
856 #define XT_URI_ABOUT_HISTORY ("history")
857 #define XT_URI_ABOUT_JSWL ("jswl")
858 #define XT_URI_ABOUT_SET ("set")
859 #define XT_URI_ABOUT_STATS ("stats")
860 #define XT_URI_ABOUT_MARCO ("marco")
861 #define XT_URI_ABOUT_STARTPAGE ("startpage")
865 int (*func
)(struct tab
*, struct karg
*);
867 { XT_URI_ABOUT_ABOUT
, about
},
868 { XT_URI_ABOUT_BLANK
, blank
},
869 { XT_URI_ABOUT_CERTS
, ca_cmd
},
870 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
871 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
872 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
873 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
874 { XT_URI_ABOUT_HELP
, help
},
875 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
876 { XT_URI_ABOUT_JSWL
, js_show_wl
},
877 { XT_URI_ABOUT_SET
, set
},
878 { XT_URI_ABOUT_STATS
, stats
},
879 { XT_URI_ABOUT_MARCO
, marco
},
880 { XT_URI_ABOUT_STARTPAGE
, startpage
},
883 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
884 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
885 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
886 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
887 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
888 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
889 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
892 extern char *__progname
;
895 GtkWidget
*main_window
;
896 GtkNotebook
*notebook
;
898 GtkWidget
*arrow
, *abtn
;
899 struct tab_list tabs
;
900 struct history_list hl
;
901 struct session_list sessions
;
902 struct download_list downloads
;
903 struct domain_list c_wl
;
904 struct domain_list js_wl
;
905 struct undo_tailq undos
;
906 struct keybinding_list kbl
;
908 struct command_list chl
;
909 struct command_list shl
;
910 struct command_entry
*history_at
;
911 struct command_entry
*search_at
;
913 int updating_dl_tabs
= 0;
914 int updating_hl_tabs
= 0;
915 int updating_cl_tabs
= 0;
916 int updating_fl_tabs
= 0;
917 int cmd_history_count
= 0;
918 int search_history_count
= 0;
920 long long unsigned int blocked_cookies
= 0;
921 char named_session
[PATH_MAX
];
922 GtkListStore
*completion_model
;
923 GtkListStore
*buffers_store
;
925 void xxx_dir(char *);
926 int icon_size_map(int);
927 void completion_add(struct tab
*);
928 void completion_add_uri(const gchar
*);
929 void show_oops(struct tab
*, const char *, ...);
932 history_delete(struct command_list
*l
, int *counter
)
934 struct command_entry
*c
;
936 if (l
== NULL
|| counter
== NULL
)
939 c
= TAILQ_LAST(l
, command_list
);
943 TAILQ_REMOVE(l
, c
, entry
);
950 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
952 struct command_entry
*c
;
955 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
958 /* don't add the same line */
959 c
= TAILQ_FIRST(list
);
961 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
964 c
= g_malloc0(sizeof *c
);
965 c
->line
= g_strdup_printf(" %s", l
);
968 TAILQ_INSERT_HEAD(list
, c
, entry
);
971 history_delete(list
, counter
);
973 if (history_autosave
&& file
) {
974 f
= fopen(file
, "w");
976 show_oops(NULL
, "couldn't write history %s", file
);
980 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
982 fprintf(f
, "%s\n", c
->line
);
990 history_read(struct command_list
*list
, char *file
, int *counter
)
993 char *s
, line
[65536];
995 if (list
== NULL
|| file
== NULL
)
998 f
= fopen(file
, "r");
1000 startpage_add("couldn't open history file %s", file
);
1005 s
= fgets(line
, sizeof line
, f
);
1006 if (s
== NULL
|| feof(f
) || ferror(f
))
1008 if ((s
= strchr(line
, '\n')) == NULL
) {
1009 startpage_add("invalid history file %s", file
);
1015 history_add(list
, NULL
, line
+ 1, counter
);
1023 /* marks and quickmarks array storage.
1024 * first a-z, then A-Z, then 0-9 */
1031 if (i
>= 0 && i
<= 'z' - 'a')
1035 if (i
>= 0 && i
<= 'Z' - 'A')
1050 if (m
>= 'a' && m
<= 'z')
1051 return ret
+ m
- 'a';
1053 ret
+= 'z' - 'a' + 1;
1054 if (m
>= 'A' && m
<= 'Z')
1055 return ret
+ m
- 'A';
1057 ret
+= 'Z' - 'A' + 1;
1058 if (m
>= '0' && m
<= '9')
1059 return ret
+ m
- '0';
1068 int saved_errno
, status
;
1071 saved_errno
= errno
;
1073 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1077 if (errno
!= ECHILD
) {
1079 clog_warn("sigchild: waitpid:");
1085 if (WIFEXITED(status
)) {
1086 if (WEXITSTATUS(status
) != 0) {
1088 clog_warnx("sigchild: child exit status: %d",
1089 WEXITSTATUS(status));
1094 clog_warnx("sigchild: child is terminated abnormally");
1099 errno
= saved_errno
;
1103 is_g_object_setting(GObject
*o
, char *str
)
1105 guint n_props
= 0, i
;
1106 GParamSpec
**proplist
;
1108 if (! G_IS_OBJECT(o
))
1111 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1114 for (i
=0; i
< n_props
; i
++) {
1115 if (! strcmp(proplist
[i
]->name
, str
))
1122 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1126 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1128 "<title>%s</title>\n"
1137 addstyles
? XT_PAGE_STYLE
: "",
1146 * Display a web page from a HTML string in memory, rather than from a URL
1149 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1151 char file
[PATH_MAX
];
1154 /* we set this to indicate we want to manually do navaction */
1156 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1158 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1160 /* set t->xtp_meaning */
1161 for (i
= 0; i
< LENGTH(about_list
); i
++)
1162 if (!strcmp(title
, about_list
[i
].name
)) {
1167 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
1169 #if GTK_CHECK_VERSION(2, 20, 0)
1170 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1171 gtk_widget_hide(t
->spinner
);
1173 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1174 xt_icon_from_file(t
, file
);
1179 get_current_tab(void)
1183 TAILQ_FOREACH(t
, &tabs
, entry
) {
1184 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1188 warnx("%s: no current tab", __func__
);
1194 set_status(struct tab
*t
, gchar
*s
, int status
)
1202 case XT_STATUS_LOADING
:
1203 type
= g_strdup_printf("Loading: %s", s
);
1206 case XT_STATUS_LINK
:
1207 type
= g_strdup_printf("Link: %s", s
);
1209 t
->status
= g_strdup(gtk_entry_get_text(
1210 GTK_ENTRY(t
->sbe
.statusbar
)));
1214 type
= g_strdup_printf("%s", s
);
1216 t
->status
= g_strdup(type
);
1220 t
->status
= g_strdup(s
);
1222 case XT_STATUS_NOTHING
:
1227 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1233 hide_cmd(struct tab
*t
)
1235 history_at
= NULL
; /* just in case */
1236 search_at
= NULL
; /* just in case */
1237 gtk_widget_hide(t
->cmd
);
1241 show_cmd(struct tab
*t
)
1245 gtk_widget_hide(t
->oops
);
1246 gtk_widget_show(t
->cmd
);
1250 hide_buffers(struct tab
*t
)
1252 gtk_widget_hide(t
->buffers
);
1253 gtk_list_store_clear(buffers_store
);
1263 sort_tabs_by_page_num(struct tab
***stabs
)
1268 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1270 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1272 TAILQ_FOREACH(t
, &tabs
, entry
)
1273 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1279 buffers_make_list(void)
1282 const gchar
*title
= NULL
;
1284 struct tab
**stabs
= NULL
;
1286 num_tabs
= sort_tabs_by_page_num(&stabs
);
1288 for (i
= 0; i
< num_tabs
; i
++)
1290 gtk_list_store_append(buffers_store
, &iter
);
1291 title
= get_title(stabs
[i
], FALSE
);
1292 gtk_list_store_set(buffers_store
, &iter
,
1293 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1303 show_buffers(struct tab
*t
)
1305 buffers_make_list();
1306 gtk_widget_show(t
->buffers
);
1307 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1311 toggle_buffers(struct tab
*t
)
1313 if (gtk_widget_get_visible(t
->buffers
))
1320 buffers(struct tab
*t
, struct karg
*args
)
1328 hide_oops(struct tab
*t
)
1330 gtk_widget_hide(t
->oops
);
1334 show_oops(struct tab
*at
, const char *fmt
, ...)
1338 struct tab
*t
= NULL
;
1344 if ((t
= get_current_tab()) == NULL
)
1350 if (vasprintf(&msg
, fmt
, ap
) == -1)
1351 errx(1, "show_oops failed");
1354 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1355 gtk_widget_hide(t
->cmd
);
1356 gtk_widget_show(t
->oops
);
1363 get_as_string(struct settings
*s
)
1374 warnx("get_as_string skip %s\n", s
->name
);
1375 } else if (s
->type
== XT_S_INT
)
1376 r
= g_strdup_printf("%d", *s
->ival
);
1377 else if (s
->type
== XT_S_STR
)
1378 r
= g_strdup(*s
->sval
);
1379 else if (s
->type
== XT_S_FLOAT
)
1380 r
= g_strdup_printf("%f", *s
->fval
);
1382 r
= g_strdup_printf("INVALID TYPE");
1388 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1393 for (i
= 0; i
< LENGTH(rs
); i
++) {
1394 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1395 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1397 s
= get_as_string(&rs
[i
]);
1398 cb(&rs
[i
], s
, cb_args
);
1405 set_browser_mode(struct settings
*s
, char *val
)
1407 if (!strcmp(val
, "whitelist")) {
1408 browser_mode
= XT_BM_WHITELIST
;
1409 allow_volatile_cookies
= 0;
1410 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1411 cookies_enabled
= 1;
1412 enable_cookie_whitelist
= 1;
1413 read_only_cookies
= 0;
1414 save_rejected_cookies
= 0;
1415 session_timeout
= 3600;
1417 enable_js_whitelist
= 1;
1418 enable_localstorage
= 0;
1419 } else if (!strcmp(val
, "normal")) {
1420 browser_mode
= XT_BM_NORMAL
;
1421 allow_volatile_cookies
= 0;
1422 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1423 cookies_enabled
= 1;
1424 enable_cookie_whitelist
= 0;
1425 read_only_cookies
= 0;
1426 save_rejected_cookies
= 0;
1427 session_timeout
= 3600;
1429 enable_js_whitelist
= 0;
1430 enable_localstorage
= 1;
1431 } else if (!strcmp(val
, "kiosk")) {
1432 browser_mode
= XT_BM_KIOSK
;
1433 allow_volatile_cookies
= 0;
1434 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1435 cookies_enabled
= 1;
1436 enable_cookie_whitelist
= 0;
1437 read_only_cookies
= 0;
1438 save_rejected_cookies
= 0;
1439 session_timeout
= 3600;
1441 enable_js_whitelist
= 0;
1442 enable_localstorage
= 1;
1452 get_browser_mode(struct settings
*s
)
1456 if (browser_mode
== XT_BM_WHITELIST
)
1457 r
= g_strdup("whitelist");
1458 else if (browser_mode
== XT_BM_NORMAL
)
1459 r
= g_strdup("normal");
1460 else if (browser_mode
== XT_BM_KIOSK
)
1461 r
= g_strdup("kiosk");
1469 set_cookie_policy(struct settings
*s
, char *val
)
1471 if (!strcmp(val
, "no3rdparty"))
1472 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1473 else if (!strcmp(val
, "accept"))
1474 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1475 else if (!strcmp(val
, "reject"))
1476 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1484 get_cookie_policy(struct settings
*s
)
1488 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1489 r
= g_strdup("no3rdparty");
1490 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1491 r
= g_strdup("accept");
1492 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1493 r
= g_strdup("reject");
1501 get_default_script(struct settings
*s
)
1503 if (default_script
[0] == '\0')
1505 return (g_strdup(default_script
));
1509 set_default_script(struct settings
*s
, char *val
)
1512 snprintf(default_script
, sizeof default_script
, "%s/%s",
1513 pwd
->pw_dir
, &val
[1]);
1515 strlcpy(default_script
, val
, sizeof default_script
);
1521 get_download_dir(struct settings
*s
)
1523 if (download_dir
[0] == '\0')
1525 return (g_strdup(download_dir
));
1529 set_download_dir(struct settings
*s
, char *val
)
1532 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1533 pwd
->pw_dir
, &val
[1]);
1535 strlcpy(download_dir
, val
, sizeof download_dir
);
1542 * We use these to prevent people putting xxxt:// URLs on
1543 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1545 #define XT_XTP_SES_KEY_SZ 8
1546 #define XT_XTP_SES_KEY_HEX_FMT \
1547 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1548 char *dl_session_key
; /* downloads */
1549 char *hl_session_key
; /* history list */
1550 char *cl_session_key
; /* cookie list */
1551 char *fl_session_key
; /* favorites list */
1553 char work_dir
[PATH_MAX
];
1554 char certs_dir
[PATH_MAX
];
1555 char cache_dir
[PATH_MAX
];
1556 char sessions_dir
[PATH_MAX
];
1557 char cookie_file
[PATH_MAX
];
1558 SoupURI
*proxy_uri
= NULL
;
1559 SoupSession
*session
;
1560 SoupCookieJar
*s_cookiejar
;
1561 SoupCookieJar
*p_cookiejar
;
1562 char rc_fname
[PATH_MAX
];
1564 struct mime_type_list mtl
;
1565 struct alias_list aliases
;
1568 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1569 void delete_tab(struct tab
*);
1570 void setzoom_webkit(struct tab
*, int);
1571 int run_script(struct tab
*, char *);
1572 int download_rb_cmp(struct download
*, struct download
*);
1573 gboolean
cmd_execute(struct tab
*t
, char *str
);
1576 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1578 return (strcmp(h1
->uri
, h2
->uri
));
1580 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1583 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1585 return (strcmp(d1
->d
, d2
->d
));
1587 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1590 get_work_dir(struct settings
*s
)
1592 if (work_dir
[0] == '\0')
1594 return (g_strdup(work_dir
));
1598 set_work_dir(struct settings
*s
, char *val
)
1601 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1602 pwd
->pw_dir
, &val
[1]);
1604 strlcpy(work_dir
, val
, sizeof work_dir
);
1610 get_tab_style(struct settings
*s
)
1612 if (tab_style
== XT_TABS_NORMAL
)
1613 return (g_strdup("normal"));
1615 return (g_strdup("compact"));
1619 set_tab_style(struct settings
*s
, char *val
)
1621 if (!strcmp(val
, "normal"))
1622 tab_style
= XT_TABS_NORMAL
;
1623 else if (!strcmp(val
, "compact"))
1624 tab_style
= XT_TABS_COMPACT
;
1632 * generate a session key to secure xtp commands.
1633 * pass in a ptr to the key in question and it will
1634 * be modified in place.
1637 generate_xtp_session_key(char **key
)
1639 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1645 /* make a new one */
1646 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1647 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1648 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1649 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1651 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1655 * validate a xtp session key.
1659 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1661 if (strcmp(trusted
, untrusted
) != 0) {
1662 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1671 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1673 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1675 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1677 struct valid_url_types
{
1688 valid_url_type(char *url
)
1692 for (i
= 0; i
< LENGTH(vut
); i
++)
1693 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1700 print_cookie(char *msg
, SoupCookie
*c
)
1706 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1707 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1708 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1709 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1710 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1711 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1712 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1713 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1714 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1715 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1719 walk_alias(struct settings
*s
,
1720 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1725 if (s
== NULL
|| cb
== NULL
) {
1726 show_oops(NULL
, "walk_alias invalid parameters");
1730 TAILQ_FOREACH(a
, &aliases
, entry
) {
1731 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1732 cb(s
, str
, cb_args
);
1738 match_alias(char *url_in
)
1742 char *url_out
= NULL
, *search
, *enc_arg
;
1744 search
= g_strdup(url_in
);
1746 if (strsep(&arg
, " \t") == NULL
) {
1747 show_oops(NULL
, "match_alias: NULL URL");
1751 TAILQ_FOREACH(a
, &aliases
, entry
) {
1752 if (!strcmp(search
, a
->a_name
))
1757 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1760 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1761 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1764 url_out
= g_strdup_printf(a
->a_uri
, "");
1772 guess_url_type(char *url_in
)
1775 char *url_out
= NULL
, *enc_search
= NULL
;
1778 /* substitute aliases */
1779 url_out
= match_alias(url_in
);
1780 if (url_out
!= NULL
)
1783 /* see if we are an about page */
1784 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1785 for (i
= 0; i
< LENGTH(about_list
); i
++)
1786 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1787 about_list
[i
].name
)) {
1788 url_out
= g_strdup(url_in
);
1792 if (guess_search
&& url_regex
&&
1793 !(g_str_has_prefix(url_in
, "http://") ||
1794 g_str_has_prefix(url_in
, "https://"))) {
1795 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1796 /* invalid URI so search instead */
1797 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1798 url_out
= g_strdup_printf(search_string
, enc_search
);
1804 /* XXX not sure about this heuristic */
1805 if (stat(url_in
, &sb
) == 0)
1806 url_out
= g_strdup_printf("file://%s", url_in
);
1808 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1810 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1816 load_uri(struct tab
*t
, gchar
*uri
)
1819 gchar
*newuri
= NULL
;
1825 /* Strip leading spaces. */
1826 while (*uri
&& isspace(*uri
))
1829 if (strlen(uri
) == 0) {
1834 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1836 if (valid_url_type(uri
)) {
1837 newuri
= guess_url_type(uri
);
1841 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1842 for (i
= 0; i
< LENGTH(about_list
); i
++)
1843 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1844 bzero(&args
, sizeof args
);
1845 about_list
[i
].func(t
, &args
);
1846 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1850 show_oops(t
, "invalid about page");
1854 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1856 webkit_web_view_load_uri(t
->wv
, uri
);
1863 get_uri(struct tab
*t
)
1865 const gchar
*uri
= NULL
;
1867 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1869 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1870 uri
= webkit_web_view_get_uri(t
->wv
);
1872 /* use tmp_uri to make sure it is g_freed */
1875 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1876 about_list
[t
->xtp_meaning
].name
);
1883 get_title(struct tab
*t
, bool window
)
1885 const gchar
*set
= NULL
, *title
= NULL
;
1886 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1888 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1889 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1892 title
= webkit_web_view_get_title(t
->wv
);
1893 if ((set
= title
? title
: get_uri(t
)))
1897 set
= window
? XT_NAME
: "(untitled)";
1903 add_alias(struct settings
*s
, char *line
)
1906 struct alias
*a
= NULL
;
1908 if (s
== NULL
|| line
== NULL
) {
1909 show_oops(NULL
, "add_alias invalid parameters");
1914 a
= g_malloc(sizeof(*a
));
1916 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1917 show_oops(NULL
, "add_alias: incomplete alias definition");
1920 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1921 show_oops(NULL
, "add_alias: invalid alias definition");
1925 a
->a_name
= g_strdup(alias
);
1926 a
->a_uri
= g_strdup(l
);
1928 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1930 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1940 add_mime_type(struct settings
*s
, char *line
)
1944 struct mime_type
*m
= NULL
;
1945 int downloadfirst
= 0;
1947 /* XXX this could be smarter */
1949 if (line
== NULL
|| strlen(line
) == 0) {
1950 show_oops(NULL
, "add_mime_type invalid parameters");
1959 m
= g_malloc(sizeof(*m
));
1961 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1962 show_oops(NULL
, "add_mime_type: invalid mime_type");
1965 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1966 mime_type
[strlen(mime_type
) - 1] = '\0';
1971 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1972 show_oops(NULL
, "add_mime_type: invalid mime_type");
1976 m
->mt_type
= g_strdup(mime_type
);
1977 m
->mt_action
= g_strdup(l
);
1978 m
->mt_download
= downloadfirst
;
1980 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1981 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1983 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1993 find_mime_type(char *mime_type
)
1995 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1997 TAILQ_FOREACH(m
, &mtl
, entry
) {
1998 if (m
->mt_default
&&
1999 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
2002 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
2015 walk_mime_type(struct settings
*s
,
2016 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2018 struct mime_type
*m
;
2021 if (s
== NULL
|| cb
== NULL
) {
2022 show_oops(NULL
, "walk_mime_type invalid parameters");
2026 TAILQ_FOREACH(m
, &mtl
, entry
) {
2027 str
= g_strdup_printf("%s%s --> %s",
2029 m
->mt_default
? "*" : "",
2031 cb(s
, str
, cb_args
);
2037 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2043 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2046 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2048 /* treat *.moo.com the same as .moo.com */
2049 if (str
[0] == '*' && str
[1] == '.')
2051 else if (str
[0] == '.')
2056 /* slice off port number */
2057 p
= g_strrstr(str
, ":");
2061 d
= g_malloc(sizeof *d
);
2063 d
->d
= g_strdup_printf(".%s", str
);
2065 d
->d
= g_strdup(str
);
2068 if (RB_INSERT(domain_list
, wl
, d
))
2071 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2082 add_cookie_wl(struct settings
*s
, char *entry
)
2084 wl_add(entry
, &c_wl
, 1);
2089 walk_cookie_wl(struct settings
*s
,
2090 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2094 if (s
== NULL
|| cb
== NULL
) {
2095 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2099 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2100 cb(s
, d
->d
, cb_args
);
2104 walk_js_wl(struct settings
*s
,
2105 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2109 if (s
== NULL
|| cb
== NULL
) {
2110 show_oops(NULL
, "walk_js_wl invalid parameters");
2114 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2115 cb(s
, d
->d
, cb_args
);
2119 add_js_wl(struct settings
*s
, char *entry
)
2121 wl_add(entry
, &js_wl
, 1 /* persistent */);
2126 wl_find(const gchar
*search
, struct domain_list
*wl
)
2129 struct domain
*d
= NULL
, dfind
;
2132 if (search
== NULL
|| wl
== NULL
)
2134 if (strlen(search
) < 2)
2137 if (search
[0] != '.')
2138 s
= g_strdup_printf(".%s", search
);
2140 s
= g_strdup(search
);
2142 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2145 d
= RB_FIND(domain_list
, wl
, &dfind
);
2159 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2165 if (s
== NULL
|| wl
== NULL
)
2168 if (!strncmp(s
, "http://", strlen("http://")))
2169 s
= &s
[strlen("http://")];
2170 else if (!strncmp(s
, "https://", strlen("https://")))
2171 s
= &s
[strlen("https://")];
2176 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2177 /* chop string at first slash */
2178 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2181 r
= wl_find(ss
, wl
);
2190 settings_add(char *var
, char *val
)
2197 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2198 if (strcmp(var
, rs
[i
].name
))
2202 if (rs
[i
].s
->set(&rs
[i
], val
))
2203 errx(1, "invalid value for %s: %s", var
, val
);
2207 switch (rs
[i
].type
) {
2216 errx(1, "invalid sval for %s",
2230 errx(1, "invalid type for %s", var
);
2239 config_parse(char *filename
, int runtime
)
2242 char *line
, *cp
, *var
, *val
;
2243 size_t len
, lineno
= 0;
2245 char file
[PATH_MAX
];
2248 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2250 if (filename
== NULL
)
2253 if (runtime
&& runtime_settings
[0] != '\0') {
2254 snprintf(file
, sizeof file
, "%s/%s",
2255 work_dir
, runtime_settings
);
2256 if (stat(file
, &sb
)) {
2257 warnx("runtime file doesn't exist, creating it");
2258 if ((f
= fopen(file
, "w")) == NULL
)
2260 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2264 strlcpy(file
, filename
, sizeof file
);
2266 if ((config
= fopen(file
, "r")) == NULL
) {
2267 warn("config_parse: cannot open %s", filename
);
2272 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2273 if (feof(config
) || ferror(config
))
2277 cp
+= (long)strspn(cp
, WS
);
2278 if (cp
[0] == '\0') {
2284 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2285 startpage_add("invalid configuration file entry: %s",
2288 cp
+= (long)strspn(cp
, WS
);
2290 if ((val
= strsep(&cp
, "\0")) == NULL
)
2293 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",
2295 handled
= settings_add(var
, val
);
2298 startpage_add("invalid configuration file entry"
2299 ": %s=%s", var
, val
);
2309 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2315 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2319 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2322 JSStringGetUTF8CString(jsref
, s
, l
);
2323 JSStringRelease(jsref
);
2329 disable_hints(struct tab
*t
)
2331 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2332 bzero(t
->hint_num
, sizeof t
->hint_num
);
2333 run_script(t
, "vimprobable_clear()");
2335 t
->hint_mode
= XT_HINT_NONE
;
2339 enable_hints(struct tab
*t
)
2341 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2342 run_script(t
, "vimprobable_show_hints()");
2344 t
->hint_mode
= XT_HINT_NONE
;
2347 #define XT_JS_OPEN ("open;")
2348 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2349 #define XT_JS_FIRE ("fire;")
2350 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2351 #define XT_JS_FOUND ("found;")
2352 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2355 run_script(struct tab
*t
, char *s
)
2357 JSGlobalContextRef ctx
;
2358 WebKitWebFrame
*frame
;
2360 JSValueRef val
, exception
;
2363 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2364 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2366 frame
= webkit_web_view_get_main_frame(t
->wv
);
2367 ctx
= webkit_web_frame_get_global_context(frame
);
2369 str
= JSStringCreateWithUTF8CString(s
);
2370 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2371 NULL
, 0, &exception
);
2372 JSStringRelease(str
);
2374 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2376 es
= js_ref_to_string(ctx
, exception
);
2377 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2381 es
= js_ref_to_string(ctx
, val
);
2382 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2384 /* handle return value right here */
2385 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2388 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2391 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2392 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2393 &es
[XT_JS_FIRE_LEN
]);
2398 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2399 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2410 hint(struct tab
*t
, struct karg
*args
)
2413 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2415 if (t
->hints_on
== 0)
2424 apply_style(struct tab
*t
)
2426 g_object_set(G_OBJECT(t
->settings
),
2427 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2431 userstyle(struct tab
*t
, struct karg
*args
)
2433 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2437 g_object_set(G_OBJECT(t
->settings
),
2438 "user-stylesheet-uri", NULL
, (char *)NULL
);
2447 * Doesn't work fully, due to the following bug:
2448 * https://bugs.webkit.org/show_bug.cgi?id=51747
2451 restore_global_history(void)
2453 char file
[PATH_MAX
];
2458 const char delim
[3] = {'\\', '\\', '\0'};
2460 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2462 if ((f
= fopen(file
, "r")) == NULL
) {
2463 warnx("%s: fopen", __func__
);
2468 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2469 if (feof(f
) || ferror(f
))
2472 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2473 if (feof(f
) || ferror(f
)) {
2475 warnx("%s: broken history file\n", __func__
);
2479 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2480 webkit_web_history_item_new_with_data(uri
, title
);
2481 h
= g_malloc(sizeof(struct history
));
2482 h
->uri
= g_strdup(uri
);
2483 h
->title
= g_strdup(title
);
2484 RB_INSERT(history_list
, &hl
, h
);
2485 completion_add_uri(h
->uri
);
2487 warnx("%s: failed to restore history\n", __func__
);
2503 save_global_history_to_disk(struct tab
*t
)
2505 char file
[PATH_MAX
];
2509 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2511 if ((f
= fopen(file
, "w")) == NULL
) {
2512 show_oops(t
, "%s: global history file: %s",
2513 __func__
, strerror(errno
));
2517 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2518 if (h
->uri
&& h
->title
)
2519 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2528 quit(struct tab
*t
, struct karg
*args
)
2530 if (save_global_history
)
2531 save_global_history_to_disk(t
);
2539 restore_sessions_list(void)
2542 struct dirent
*dp
= NULL
;
2545 sdir
= opendir(sessions_dir
);
2547 while ((dp
= readdir(sdir
)) != NULL
)
2548 if (dp
->d_type
== DT_REG
) {
2549 s
= g_malloc(sizeof(struct session
));
2550 s
->name
= g_strdup(dp
->d_name
);
2551 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2558 open_tabs(struct tab
*t
, struct karg
*a
)
2560 char file
[PATH_MAX
];
2564 struct tab
*ti
, *tt
;
2569 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2570 if ((f
= fopen(file
, "r")) == NULL
)
2573 ti
= TAILQ_LAST(&tabs
, tab_list
);
2576 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2577 if (feof(f
) || ferror(f
))
2580 /* retrieve session name */
2581 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2582 strlcpy(named_session
,
2583 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2584 sizeof named_session
);
2588 if (uri
&& strlen(uri
))
2589 create_new_tab(uri
, NULL
, 1, -1);
2595 /* close open tabs */
2596 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2598 tt
= TAILQ_FIRST(&tabs
);
2618 restore_saved_tabs(void)
2620 char file
[PATH_MAX
];
2621 int unlink_file
= 0;
2626 snprintf(file
, sizeof file
, "%s/%s",
2627 sessions_dir
, XT_RESTART_TABS_FILE
);
2628 if (stat(file
, &sb
) == -1)
2629 a
.s
= XT_SAVED_TABS_FILE
;
2632 a
.s
= XT_RESTART_TABS_FILE
;
2635 a
.i
= XT_SES_DONOTHING
;
2636 rv
= open_tabs(NULL
, &a
);
2645 save_tabs(struct tab
*t
, struct karg
*a
)
2647 char file
[PATH_MAX
];
2649 int num_tabs
= 0, i
;
2650 struct tab
**stabs
= NULL
;
2655 snprintf(file
, sizeof file
, "%s/%s",
2656 sessions_dir
, named_session
);
2658 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2660 if ((f
= fopen(file
, "w")) == NULL
) {
2661 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2665 /* save session name */
2666 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2668 /* Save tabs, in the order they are arranged in the notebook. */
2669 num_tabs
= sort_tabs_by_page_num(&stabs
);
2671 for (i
= 0; i
< num_tabs
; i
++)
2673 if (get_uri(stabs
[i
]) != NULL
)
2674 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2675 else if (gtk_entry_get_text(GTK_ENTRY(
2676 stabs
[i
]->uri_entry
)))
2677 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2678 stabs
[i
]->uri_entry
)));
2683 /* try and make sure this gets to disk NOW. XXX Backup first? */
2684 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2685 show_oops(t
, "May not have managed to save session: %s",
2695 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2707 run_page_script(struct tab
*t
, struct karg
*args
)
2710 char *tmp
, script
[PATH_MAX
];
2712 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2713 if (tmp
[0] == '\0') {
2714 show_oops(t
, "no script specified");
2718 if ((uri
= get_uri(t
)) == NULL
) {
2719 show_oops(t
, "tab is empty, not running script");
2724 snprintf(script
, sizeof script
, "%s/%s",
2725 pwd
->pw_dir
, &tmp
[1]);
2727 strlcpy(script
, tmp
, sizeof script
);
2731 show_oops(t
, "can't fork to run script");
2741 execlp(script
, script
, uri
, (void *)NULL
);
2751 yank_uri(struct tab
*t
, struct karg
*args
)
2754 GtkClipboard
*clipboard
;
2756 if ((uri
= get_uri(t
)) == NULL
)
2759 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2760 gtk_clipboard_set_text(clipboard
, uri
, -1);
2766 paste_uri(struct tab
*t
, struct karg
*args
)
2768 GtkClipboard
*clipboard
;
2769 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2771 gchar
*p
= NULL
, *uri
;
2773 /* try primary clipboard first */
2774 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2775 p
= gtk_clipboard_wait_for_text(clipboard
);
2777 /* if it failed get whatever text is in cut_buffer0 */
2778 if (p
== NULL
&& xterm_workaround
)
2779 if (gdk_property_get(gdk_get_default_root_window(),
2781 gdk_atom_intern("STRING", FALSE
),
2783 1024 * 1024 /* picked out of my butt */,
2789 /* yes sir, we need to NUL the string */
2795 while (*uri
&& isspace(*uri
))
2797 if (strlen(uri
) == 0) {
2798 show_oops(t
, "empty paste buffer");
2801 if (guess_search
== 0 && valid_url_type(uri
)) {
2802 /* we can be clever and paste this in search box */
2803 show_oops(t
, "not a valid URL");
2807 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2809 else if (args
->i
== XT_PASTE_NEW_TAB
)
2810 create_new_tab(uri
, NULL
, 1, -1);
2821 find_domain(const gchar
*s
, int toplevel
)
2829 uri
= soup_uri_new(s
);
2831 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2835 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2836 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2837 while(--p
>= uri
->host
&& *p
!= '.');
2844 ret
= g_strdup_printf(".%s", p
);
2852 toggle_cwl(struct tab
*t
, struct karg
*args
)
2863 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2865 if (uri
== NULL
|| dom
== NULL
||
2866 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2867 show_oops(t
, "Can't toggle domain in cookie white list");
2870 d
= wl_find(dom
, &c_wl
);
2877 if (args
->i
& XT_WL_TOGGLE
)
2879 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2881 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2885 /* enable cookies for domain */
2886 wl_add(dom
, &c_wl
, 0);
2888 /* disable cookies for domain */
2889 RB_REMOVE(domain_list
, &c_wl
, d
);
2891 if (args
->i
& XT_WL_RELOAD
)
2892 webkit_web_view_reload(t
->wv
);
2900 toggle_js(struct tab
*t
, struct karg
*args
)
2910 g_object_get(G_OBJECT(t
->settings
),
2911 "enable-scripts", &es
, (char *)NULL
);
2912 if (args
->i
& XT_WL_TOGGLE
)
2914 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2916 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2922 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2924 if (uri
== NULL
|| dom
== NULL
||
2925 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2926 show_oops(t
, "Can't toggle domain in JavaScript white list");
2931 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2932 wl_add(dom
, &js_wl
, 0 /* session */);
2934 d
= wl_find(dom
, &js_wl
);
2936 RB_REMOVE(domain_list
, &js_wl
, d
);
2937 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2939 g_object_set(G_OBJECT(t
->settings
),
2940 "enable-scripts", es
, (char *)NULL
);
2941 g_object_set(G_OBJECT(t
->settings
),
2942 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2943 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2945 if (args
->i
& XT_WL_RELOAD
)
2946 webkit_web_view_reload(t
->wv
);
2954 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2958 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2961 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2966 toggle_src(struct tab
*t
, struct karg
*args
)
2973 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2974 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2975 webkit_web_view_reload(t
->wv
);
2981 focus_webview(struct tab
*t
)
2986 /* only grab focus if we are visible */
2987 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2988 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2992 focus(struct tab
*t
, struct karg
*args
)
2994 if (t
== NULL
|| args
== NULL
)
3000 if (args
->i
== XT_FOCUS_URI
)
3001 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
3002 else if (args
->i
== XT_FOCUS_SEARCH
)
3003 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
3009 stats(struct tab
*t
, struct karg
*args
)
3011 char *page
, *body
, *s
, line
[64 * 1024];
3012 long long unsigned int line_count
= 0;
3016 show_oops(NULL
, "stats invalid parameters");
3019 if (save_rejected_cookies
) {
3020 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
3022 s
= fgets(line
, sizeof line
, r_cookie_f
);
3023 if (s
== NULL
|| feof(r_cookie_f
) ||
3029 snprintf(line
, sizeof line
,
3030 "<br/>Cookies blocked(*) total: %llu", line_count
);
3032 show_oops(t
, "Can't open blocked cookies file: %s",
3036 body
= g_strdup_printf(
3037 "Cookies blocked(*) this session: %llu"
3039 "<p><small><b>*</b> results vary based on settings</small></p>",
3043 page
= get_html_page("Statistics", body
, "", 0);
3046 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3053 marco(struct tab
*t
, struct karg
*args
)
3055 char *page
, line
[64 * 1024];
3059 show_oops(NULL
, "marco invalid parameters");
3062 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3064 page
= get_html_page("Marco Sez...", line
, "", 0);
3066 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3073 blank(struct tab
*t
, struct karg
*args
)
3076 show_oops(NULL
, "blank invalid parameters");
3078 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3084 about(struct tab
*t
, struct karg
*args
)
3089 show_oops(NULL
, "about invalid parameters");
3091 body
= g_strdup_printf("<b>Version: %s</b>"
3092 #ifdef XXXTERM_BUILDSTR
3093 "<br><b>Build: %s</b>"
3098 "<li>Marco Peereboom <marco@peereboom.us></li>"
3099 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3100 "<li>Edd Barrett <vext01@gmail.com> </li>"
3101 "<li>Todd T. Fries <todd@fries.net> </li>"
3102 "<li>Raphael Graf <r@undefined.ch> </li>"
3104 "Copyrights and licenses can be found on the XXXTerm "
3105 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3107 #ifdef XXXTERM_BUILDSTR
3108 version
, XXXTERM_BUILDSTR
3114 page
= get_html_page("About", body
, "", 0);
3117 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3124 help(struct tab
*t
, struct karg
*args
)
3126 char *page
, *head
, *body
;
3129 show_oops(NULL
, "help invalid parameters");
3131 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3132 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3134 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3135 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3136 "cgi-bin/man-cgi?xxxterm</a>";
3138 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3140 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3147 startpage(struct tab
*t
, struct karg
*args
)
3149 char *page
, *body
, *b
;
3153 show_oops(NULL
, "startpage invalid parameters");
3155 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3157 TAILQ_FOREACH(s
, &spl
, entry
) {
3159 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3163 page
= get_html_page("Startup Exception", body
, "", 0);
3166 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3173 startpage_add(const char *fmt
, ...)
3183 if (vasprintf(&msg
, fmt
, ap
) == -1)
3184 errx(1, "startpage_add failed");
3187 s
= g_malloc0(sizeof *s
);
3190 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3194 * update all favorite tabs apart from one. Pass NULL if
3195 * you want to update all.
3198 update_favorite_tabs(struct tab
*apart_from
)
3201 if (!updating_fl_tabs
) {
3202 updating_fl_tabs
= 1; /* stop infinite recursion */
3203 TAILQ_FOREACH(t
, &tabs
, entry
)
3204 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3205 && (t
!= apart_from
))
3206 xtp_page_fl(t
, NULL
);
3207 updating_fl_tabs
= 0;
3211 /* show a list of favorites (bookmarks) */
3213 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3215 char file
[PATH_MAX
];
3217 char *uri
= NULL
, *title
= NULL
;
3218 size_t len
, lineno
= 0;
3220 char *body
, *tmp
, *page
= NULL
;
3221 const char delim
[3] = {'\\', '\\', '\0'};
3223 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3226 warn("%s: bad param", __func__
);
3228 /* new session key */
3229 if (!updating_fl_tabs
)
3230 generate_xtp_session_key(&fl_session_key
);
3232 /* open favorites */
3233 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3234 if ((f
= fopen(file
, "r")) == NULL
) {
3235 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3240 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3241 "<th style='width: 40px'>#</th><th>Link</th>"
3242 "<th style='width: 40px'>Rm</th></tr>\n");
3245 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3247 if (strlen(title
) == 0 || title
[0] == '#') {
3253 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3254 if (feof(f
) || ferror(f
)) {
3255 show_oops(t
, "favorites file corrupt");
3261 body
= g_strdup_printf("%s<tr>"
3263 "<td><a href='%s'>%s</a></td>"
3264 "<td style='text-align: center'>"
3265 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3267 body
, i
, uri
, title
,
3268 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3280 /* if none, say so */
3283 body
= g_strdup_printf("%s<tr>"
3284 "<td colspan='3' style='text-align: center'>"
3285 "No favorites - To add one use the 'favadd' command."
3286 "</td></tr>", body
);
3291 body
= g_strdup_printf("%s</table>", body
);
3301 page
= get_html_page("Favorites", body
, "", 1);
3302 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3306 update_favorite_tabs(t
);
3315 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3316 size_t cert_count
, char *title
)
3318 gnutls_datum_t cinfo
;
3322 body
= g_strdup("");
3324 for (i
= 0; i
< cert_count
; i
++) {
3325 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3330 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3331 body
, i
, cinfo
.data
);
3332 gnutls_free(cinfo
.data
);
3336 tmp
= get_html_page(title
, body
, "", 0);
3339 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3344 ca_cmd(struct tab
*t
, struct karg
*args
)
3347 int rv
= 1, certs
= 0, certs_read
;
3350 gnutls_x509_crt_t
*c
= NULL
;
3351 char *certs_buf
= NULL
, *s
;
3353 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3354 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3358 if (fstat(fileno(f
), &sb
) == -1) {
3359 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3363 certs_buf
= g_malloc(sb
.st_size
+ 1);
3364 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3365 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3368 certs_buf
[sb
.st_size
] = '\0';
3371 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3373 s
+= strlen("BEGIN CERTIFICATE");
3376 bzero(&dt
, sizeof dt
);
3377 dt
.data
= (unsigned char *)certs_buf
;
3378 dt
.size
= sb
.st_size
;
3379 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3380 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3381 GNUTLS_X509_FMT_PEM
, 0);
3382 if (certs_read
<= 0) {
3383 show_oops(t
, "No cert(s) available");
3386 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3399 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
3403 struct addrinfo hints
, *res
= NULL
, *ai
;
3404 int rv
= -1, s
= -1, on
, error
;
3406 static gchar myerror
[256]; /* this is not thread safe */
3409 *error_str
= myerror
;
3410 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3411 *error_str
= "invalid URI";
3415 su
= soup_uri_new(uri
);
3417 *error_str
= "invalid soup URI";
3420 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3421 *error_str
= "invalid HTTPS URI";
3425 snprintf(port
, sizeof port
, "%d", su
->port
);
3426 bzero(&hints
, sizeof(struct addrinfo
));
3427 hints
.ai_flags
= AI_CANONNAME
;
3428 hints
.ai_family
= AF_UNSPEC
;
3429 hints
.ai_socktype
= SOCK_STREAM
;
3431 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3432 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
3433 gai_strerror(errno
));
3437 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3443 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3445 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3448 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3451 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3455 snprintf(myerror
, sizeof myerror
,
3456 "could not obtain certificates from: %s",
3462 strlcpy(domain
, su
->host
, domain_sz
);
3469 if (rv
== -1 && s
!= -1)
3476 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3479 gnutls_deinit(gsession
);
3481 gnutls_certificate_free_credentials(xcred
);
3487 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
3488 gnutls_certificate_credentials_t
*xc
)
3490 gnutls_certificate_credentials_t xcred
;
3491 gnutls_session_t gsession
;
3493 static gchar myerror
[1024]; /* this is not thread safe */
3495 if (gs
== NULL
|| xc
== NULL
)
3502 gnutls_certificate_allocate_credentials(&xcred
);
3503 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3504 GNUTLS_X509_FMT_PEM
);
3506 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3507 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3508 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3509 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3510 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3511 snprintf(myerror
, sizeof myerror
,
3512 "gnutls_handshake failed %d fatal %d %s",
3514 gnutls_error_is_fatal(rv
),
3515 gnutls_strerror_name(rv
));
3516 stop_tls(gsession
, xcred
);
3520 gnutls_credentials_type_t cred
;
3521 cred
= gnutls_auth_get_type(gsession
);
3522 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3523 snprintf(myerror
, sizeof myerror
,
3524 "gnutls_auth_get_type failed %d",
3526 stop_tls(gsession
, xcred
);
3534 *error_str
= myerror
;
3539 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3543 const gnutls_datum_t
*cl
;
3544 gnutls_x509_crt_t
*all_certs
;
3547 if (certs
== NULL
|| cert_count
== NULL
)
3549 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3551 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3555 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3556 for (i
= 0; i
< len
; i
++) {
3557 gnutls_x509_crt_init(&all_certs
[i
]);
3558 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3559 GNUTLS_X509_FMT_PEM
< 0)) {
3573 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3577 for (i
= 0; i
< cert_count
; i
++)
3578 gnutls_x509_crt_deinit(certs
[i
]);
3583 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3585 GdkColor c_text
, c_base
;
3587 gdk_color_parse(text
, &c_text
);
3588 gdk_color_parse(base
, &c_base
);
3590 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3591 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3592 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3593 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3595 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3596 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3597 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3598 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3602 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3603 size_t cert_count
, char *domain
)
3606 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3611 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3614 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3615 if ((f
= fopen(file
, "w")) == NULL
) {
3616 show_oops(t
, "Can't create cert file %s %s",
3617 file
, strerror(errno
));
3621 for (i
= 0; i
< cert_count
; i
++) {
3622 cert_buf_sz
= sizeof cert_buf
;
3623 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3624 cert_buf
, &cert_buf_sz
)) {
3625 show_oops(t
, "gnutls_x509_crt_export failed");
3628 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3629 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3634 /* not the best spot but oh well */
3635 gdk_color_parse("lightblue", &color
);
3636 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3637 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3650 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
3652 char domain
[8182], file
[PATH_MAX
];
3653 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3655 unsigned int error
= 0;
3657 size_t cert_buf_sz
, cert_count
;
3658 enum cert_trust rv
= CERT_UNTRUSTED
;
3659 static gchar serr
[80]; /* this isn't thread safe */
3660 gnutls_session_t gsession
;
3661 gnutls_x509_crt_t
*certs
;
3662 gnutls_certificate_credentials_t xcred
;
3664 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3668 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
3669 sizeof domain
)) == -1)
3672 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3675 if (start_tls(error_str
, s
, &gsession
, &xcred
))
3677 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3679 /* verify certs in case cert file doesn't exist */
3680 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3682 *error_str
= "Invalid certificates";
3687 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3688 *error_str
= "Can't get connection certificates";
3692 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3693 if ((f
= fopen(file
, "r")) == NULL
) {
3699 for (i
= 0; i
< cert_count
; i
++) {
3700 cert_buf_sz
= sizeof cert_buf
;
3701 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3702 cert_buf
, &cert_buf_sz
)) {
3705 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3706 rv
= CERT_BAD
; /* critical */
3709 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3710 rv
= CERT_BAD
; /* critical */
3719 free_connection_certs(certs
, cert_count
);
3721 /* we close the socket first for speed */
3725 /* only complain if we didn't save it locally */
3726 if (error
&& rv
!= CERT_LOCAL
) {
3727 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3728 if (error
& GNUTLS_CERT_INVALID
)
3729 strlcat(serr
, "invalid, ", sizeof serr
);
3730 if (error
& GNUTLS_CERT_REVOKED
)
3731 strlcat(serr
, "revoked, ", sizeof serr
);
3732 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3733 strlcat(serr
, "signer not found, ", sizeof serr
);
3734 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3735 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3736 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3737 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3738 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3739 strlcat(serr
, "not activated, ", sizeof serr
);
3740 if (error
& GNUTLS_CERT_EXPIRED
)
3741 strlcat(serr
, "expired, ", sizeof serr
);
3742 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3743 if (serr
[i
] == ',') {
3750 stop_tls(gsession
, xcred
);
3756 cert_cmd(struct tab
*t
, struct karg
*args
)
3758 const gchar
*uri
, *error_str
= NULL
;
3762 gnutls_session_t gsession
;
3763 gnutls_x509_crt_t
*certs
;
3764 gnutls_certificate_credentials_t xcred
;
3769 if (ssl_ca_file
== NULL
) {
3770 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3774 if ((uri
= get_uri(t
)) == NULL
) {
3775 show_oops(t
, "Invalid URI");
3779 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
3780 sizeof domain
)) == -1) {
3781 show_oops(t
, "%s", error_str
);
3786 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
3790 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3791 show_oops(t
, "get_connection_certs failed");
3795 if (args
->i
& XT_SHOW
)
3796 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3797 else if (args
->i
& XT_SAVE
)
3798 save_certs(t
, certs
, cert_count
, domain
);
3800 free_connection_certs(certs
, cert_count
);
3802 /* we close the socket first for speed */
3805 stop_tls(gsession
, xcred
);
3806 if (error_str
&& strlen(error_str
))
3807 show_oops(t
, "%s", error_str
);
3812 remove_cookie(int index
)
3818 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3820 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3822 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3826 print_cookie("remove cookie", c
);
3827 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3832 soup_cookies_free(cf
);
3838 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3843 body
= g_strdup("");
3846 if (args
->i
& XT_WL_PERSISTENT
) {
3848 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3850 RB_FOREACH(d
, domain_list
, wl
) {
3854 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3860 if (args
->i
& XT_WL_SESSION
) {
3862 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3864 RB_FOREACH(d
, domain_list
, wl
) {
3868 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3873 tmp
= get_html_page(title
, body
, "", 0);
3876 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3878 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3884 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3886 char file
[PATH_MAX
];
3888 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3896 if (t
== NULL
|| args
== NULL
)
3899 if (runtime_settings
[0] == '\0')
3902 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3903 if ((f
= fopen(file
, "r+")) == NULL
)
3907 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3908 if (uri
== NULL
|| dom
== NULL
||
3909 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3910 show_oops(t
, "Can't add domain to %s white list",
3911 js
? "JavaScript" : "cookie");
3915 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3918 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3921 if (!strcmp(line
, lt
))
3927 fprintf(f
, "%s\n", lt
);
3932 d
= wl_find(dom
, &js_wl
);
3934 settings_add("js_wl", dom
);
3935 d
= wl_find(dom
, &js_wl
);
3939 d
= wl_find(dom
, &c_wl
);
3941 settings_add("cookie_wl", dom
);
3942 d
= wl_find(dom
, &c_wl
);
3946 /* find and add to persistent jar */
3947 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3948 for (;cf
; cf
= cf
->next
) {
3950 if (!strcmp(dom
, ci
->domain
) ||
3951 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3952 c
= soup_cookie_copy(ci
);
3953 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3956 soup_cookies_free(cf
);
3974 js_show_wl(struct tab
*t
, struct karg
*args
)
3976 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3977 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3983 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3985 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3986 wl_show(t
, args
, "Cookie White List", &c_wl
);
3992 cookie_cmd(struct tab
*t
, struct karg
*args
)
3994 if (args
->i
& XT_SHOW
)
3995 wl_show(t
, args
, "Cookie White List", &c_wl
);
3996 else if (args
->i
& XT_WL_TOGGLE
) {
3997 args
->i
|= XT_WL_RELOAD
;
3998 toggle_cwl(t
, args
);
3999 } else if (args
->i
& XT_SAVE
) {
4000 args
->i
|= XT_WL_RELOAD
;
4001 wl_save(t
, args
, 0);
4002 } else if (args
->i
& XT_DELETE
)
4003 show_oops(t
, "'cookie delete' currently unimplemented");
4009 js_cmd(struct tab
*t
, struct karg
*args
)
4011 if (args
->i
& XT_SHOW
)
4012 wl_show(t
, args
, "JavaScript White List", &js_wl
);
4013 else if (args
->i
& XT_SAVE
) {
4014 args
->i
|= XT_WL_RELOAD
;
4015 wl_save(t
, args
, 1);
4016 } else if (args
->i
& XT_WL_TOGGLE
) {
4017 args
->i
|= XT_WL_RELOAD
;
4019 } else if (args
->i
& XT_DELETE
)
4020 show_oops(t
, "'js delete' currently unimplemented");
4026 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4028 js_toggle_cb(t
->js_toggle
, t
);
4034 add_favorite(struct tab
*t
, struct karg
*args
)
4036 char file
[PATH_MAX
];
4039 size_t urilen
, linelen
;
4040 const gchar
*uri
, *title
;
4045 /* don't allow adding of xtp pages to favorites */
4046 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4047 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4051 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4052 if ((f
= fopen(file
, "r+")) == NULL
) {
4053 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4057 title
= get_title(t
, FALSE
);
4060 if (title
== NULL
|| uri
== NULL
) {
4061 show_oops(t
, "can't add page to favorites");
4065 urilen
= strlen(uri
);
4068 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4069 if (feof(f
) || ferror(f
))
4072 if (linelen
== urilen
&& !strcmp(line
, uri
))
4079 fprintf(f
, "\n%s\n%s", title
, uri
);
4085 update_favorite_tabs(NULL
);
4091 can_go_back_for_real(struct tab
*t
)
4094 WebKitWebHistoryItem
*item
;
4100 /* rely on webkit to make sure we can go backward when on an about page */
4102 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4103 return (webkit_web_view_can_go_back(t
->wv
));
4105 /* the back/forwars list is stupid so help determine if we can go back */
4106 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4108 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4109 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4117 can_go_forward_for_real(struct tab
*t
)
4120 WebKitWebHistoryItem
*item
;
4126 /* rely on webkit to make sure we can go forward when on an about page */
4128 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4129 return (webkit_web_view_can_go_forward(t
->wv
));
4131 /* the back/forwars list is stupid so help selecting a different item */
4132 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4134 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4135 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4143 go_back_for_real(struct tab
*t
)
4146 WebKitWebHistoryItem
*item
;
4154 webkit_web_view_go_back(t
->wv
);
4157 /* the back/forwars list is stupid so help selecting a different item */
4158 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4160 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4161 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4162 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4169 go_forward_for_real(struct tab
*t
)
4172 WebKitWebHistoryItem
*item
;
4180 webkit_web_view_go_forward(t
->wv
);
4183 /* the back/forwars list is stupid so help selecting a different item */
4184 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4186 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4187 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4188 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4195 navaction(struct tab
*t
, struct karg
*args
)
4197 WebKitWebHistoryItem
*item
;
4198 WebKitWebFrame
*frame
;
4200 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4201 t
->tab_id
, args
->i
);
4203 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4205 if (args
->i
== XT_NAV_BACK
)
4206 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4208 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4210 return (XT_CB_PASSTHROUGH
);
4211 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4213 return (XT_CB_PASSTHROUGH
);
4219 go_back_for_real(t
);
4221 case XT_NAV_FORWARD
:
4223 go_forward_for_real(t
);
4226 frame
= webkit_web_view_get_main_frame(t
->wv
);
4227 webkit_web_frame_reload(frame
);
4230 return (XT_CB_PASSTHROUGH
);
4234 move(struct tab
*t
, struct karg
*args
)
4236 GtkAdjustment
*adjust
;
4237 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4243 case XT_MOVE_BOTTOM
:
4245 case XT_MOVE_PAGEDOWN
:
4246 case XT_MOVE_PAGEUP
:
4247 case XT_MOVE_HALFDOWN
:
4248 case XT_MOVE_HALFUP
:
4249 case XT_MOVE_PERCENT
:
4250 adjust
= t
->adjust_v
;
4253 adjust
= t
->adjust_h
;
4257 pos
= gtk_adjustment_get_value(adjust
);
4258 ps
= gtk_adjustment_get_page_size(adjust
);
4259 upper
= gtk_adjustment_get_upper(adjust
);
4260 lower
= gtk_adjustment_get_lower(adjust
);
4261 si
= gtk_adjustment_get_step_increment(adjust
);
4262 pi
= gtk_adjustment_get_page_increment(adjust
);
4265 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4266 "max %f si %f pi %f\n",
4267 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4268 pos
, ps
, upper
, lower
, max
, si
, pi
);
4274 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4279 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4281 case XT_MOVE_BOTTOM
:
4282 case XT_MOVE_FARRIGHT
:
4283 gtk_adjustment_set_value(adjust
, max
);
4286 case XT_MOVE_FARLEFT
:
4287 gtk_adjustment_set_value(adjust
, lower
);
4289 case XT_MOVE_PAGEDOWN
:
4291 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4293 case XT_MOVE_PAGEUP
:
4295 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4297 case XT_MOVE_HALFDOWN
:
4299 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4301 case XT_MOVE_HALFUP
:
4303 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4305 case XT_MOVE_PERCENT
:
4306 percent
= atoi(args
->s
) / 100.0;
4307 pos
= max
* percent
;
4308 if (pos
< 0.0 || pos
> max
)
4310 gtk_adjustment_set_value(adjust
, pos
);
4313 return (XT_CB_PASSTHROUGH
);
4316 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4318 return (XT_CB_HANDLED
);
4322 url_set_visibility(void)
4326 TAILQ_FOREACH(t
, &tabs
, entry
)
4327 if (show_url
== 0) {
4328 gtk_widget_hide(t
->toolbar
);
4331 gtk_widget_show(t
->toolbar
);
4335 notebook_tab_set_visibility(void)
4337 if (show_tabs
== 0) {
4338 gtk_widget_hide(tab_bar
);
4339 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4341 if (tab_style
== XT_TABS_NORMAL
) {
4342 gtk_widget_hide(tab_bar
);
4343 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4344 } else if (tab_style
== XT_TABS_COMPACT
) {
4345 gtk_widget_show(tab_bar
);
4346 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4352 statusbar_set_visibility(void)
4356 TAILQ_FOREACH(t
, &tabs
, entry
)
4357 if (show_statusbar
== 0) {
4358 gtk_widget_hide(t
->statusbar_box
);
4361 gtk_widget_show(t
->statusbar_box
);
4365 url_set(struct tab
*t
, int enable_url_entry
)
4370 show_url
= enable_url_entry
;
4372 if (enable_url_entry
) {
4373 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4374 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4375 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4377 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4378 GTK_ENTRY_ICON_PRIMARY
);
4380 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4381 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4382 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4383 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4389 fullscreen(struct tab
*t
, struct karg
*args
)
4391 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4394 return (XT_CB_PASSTHROUGH
);
4396 if (show_url
== 0) {
4404 url_set_visibility();
4405 notebook_tab_set_visibility();
4407 return (XT_CB_HANDLED
);
4411 statustoggle(struct tab
*t
, struct karg
*args
)
4413 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4415 if (show_statusbar
== 1) {
4417 statusbar_set_visibility();
4418 } else if (show_statusbar
== 0) {
4420 statusbar_set_visibility();
4422 return (XT_CB_HANDLED
);
4426 urlaction(struct tab
*t
, struct karg
*args
)
4428 int rv
= XT_CB_HANDLED
;
4430 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4433 return (XT_CB_PASSTHROUGH
);
4437 if (show_url
== 0) {
4439 url_set_visibility();
4443 if (show_url
== 1) {
4445 url_set_visibility();
4453 tabaction(struct tab
*t
, struct karg
*args
)
4455 int rv
= XT_CB_HANDLED
;
4456 char *url
= args
->s
;
4460 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4463 return (XT_CB_PASSTHROUGH
);
4467 if (strlen(url
) > 0)
4468 create_new_tab(url
, NULL
, 1, args
->precount
);
4470 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4473 if (args
->precount
< 0)
4476 TAILQ_FOREACH(tt
, &tabs
, entry
)
4477 if (tt
->tab_id
== args
->precount
- 1) {
4482 case XT_TAB_DELQUIT
:
4483 if (gtk_notebook_get_n_pages(notebook
) > 1)
4489 if (strlen(url
) > 0)
4492 rv
= XT_CB_PASSTHROUGH
;
4498 if (show_tabs
== 0) {
4500 notebook_tab_set_visibility();
4504 if (show_tabs
== 1) {
4506 notebook_tab_set_visibility();
4509 case XT_TAB_NEXTSTYLE
:
4510 if (tab_style
== XT_TABS_NORMAL
) {
4511 tab_style
= XT_TABS_COMPACT
;
4512 recolor_compact_tabs();
4515 tab_style
= XT_TABS_NORMAL
;
4516 notebook_tab_set_visibility();
4518 case XT_TAB_UNDO_CLOSE
:
4519 if (undo_count
== 0) {
4520 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4525 u
= TAILQ_FIRST(&undos
);
4526 create_new_tab(u
->uri
, u
, 1, -1);
4528 TAILQ_REMOVE(&undos
, u
, entry
);
4530 /* u->history is freed in create_new_tab() */
4535 rv
= XT_CB_PASSTHROUGH
;
4549 resizetab(struct tab
*t
, struct karg
*args
)
4551 if (t
== NULL
|| args
== NULL
) {
4552 show_oops(NULL
, "resizetab invalid parameters");
4553 return (XT_CB_PASSTHROUGH
);
4556 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4557 t
->tab_id
, args
->i
);
4559 setzoom_webkit(t
, args
->i
);
4561 return (XT_CB_HANDLED
);
4565 movetab(struct tab
*t
, struct karg
*args
)
4569 if (t
== NULL
|| args
== NULL
) {
4570 show_oops(NULL
, "movetab invalid parameters");
4571 return (XT_CB_PASSTHROUGH
);
4574 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4575 t
->tab_id
, args
->i
);
4577 if (args
->i
>= XT_TAB_INVALID
)
4578 return (XT_CB_PASSTHROUGH
);
4580 if (TAILQ_EMPTY(&tabs
))
4581 return (XT_CB_PASSTHROUGH
);
4583 n
= gtk_notebook_get_n_pages(notebook
);
4584 dest
= gtk_notebook_get_current_page(notebook
);
4588 if (args
->precount
< 0)
4589 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4591 dest
= args
->precount
- 1;
4595 if (args
->precount
< 0)
4598 dest
-= args
->precount
% n
;
4611 return (XT_CB_PASSTHROUGH
);
4614 if (dest
< 0 || dest
>= n
)
4615 return (XT_CB_PASSTHROUGH
);
4616 if (t
->tab_id
== dest
) {
4617 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4618 return (XT_CB_HANDLED
);
4621 set_current_tab(dest
);
4623 return (XT_CB_HANDLED
);
4629 command(struct tab
*t
, struct karg
*args
)
4631 char *s
= NULL
, *ss
= NULL
;
4635 if (t
== NULL
|| args
== NULL
) {
4636 show_oops(NULL
, "command invalid parameters");
4637 return (XT_CB_PASSTHROUGH
);
4648 if (cmd_prefix
== 0)
4651 ss
= g_strdup_printf(":%d", cmd_prefix
);
4662 case XT_CMD_OPEN_CURRENT
:
4665 case XT_CMD_TABNEW_CURRENT
:
4666 if (!s
) /* FALL THROUGH? */
4668 if ((uri
= get_uri(t
)) != NULL
) {
4669 ss
= g_strdup_printf("%s%s", s
, uri
);
4674 show_oops(t
, "command: invalid opcode %d", args
->i
);
4675 return (XT_CB_PASSTHROUGH
);
4678 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4680 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4681 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4682 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4684 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4685 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4690 return (XT_CB_HANDLED
);
4694 * Return a new string with a download row (in html)
4695 * appended. Old string is freed.
4698 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4701 WebKitDownloadStatus stat
;
4702 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4704 char cur_sz
[FMT_SCALED_STRSIZE
];
4705 char tot_sz
[FMT_SCALED_STRSIZE
];
4708 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4710 /* All actions wil take this form:
4711 * xxxt://class/seskey
4713 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4714 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4716 stat
= webkit_download_get_status(dl
->download
);
4719 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4720 status_html
= g_strdup_printf("Finished");
4721 cmd_html
= g_strdup_printf(
4722 "<a href='%s%d/%d'>Remove</a>",
4723 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4725 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4726 /* gather size info */
4727 progress
= 100 * webkit_download_get_progress(dl
->download
);
4730 webkit_download_get_current_size(dl
->download
), cur_sz
);
4732 webkit_download_get_total_size(dl
->download
), tot_sz
);
4734 status_html
= g_strdup_printf(
4735 "<div style='width: 100%%' align='center'>"
4736 "<div class='progress-outer'>"
4737 "<div class='progress-inner' style='width: %.2f%%'>"
4738 "</div></div></div>"
4739 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4740 progress
, cur_sz
, tot_sz
, progress
);
4742 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4743 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4747 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4748 status_html
= g_strdup_printf("Cancelled");
4749 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4750 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4752 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4753 status_html
= g_strdup_printf("Error!");
4754 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4755 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4757 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4758 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4759 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4760 status_html
= g_strdup_printf("Starting");
4763 show_oops(t
, "%s: unknown download status", __func__
);
4766 new_html
= g_strdup_printf(
4767 "%s\n<tr><td>%s</td><td>%s</td>"
4768 "<td style='text-align:center'>%s</td></tr>\n",
4769 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4770 status_html
, cmd_html
);
4774 g_free(status_html
);
4785 * update all download tabs apart from one. Pass NULL if
4786 * you want to update all.
4789 update_download_tabs(struct tab
*apart_from
)
4792 if (!updating_dl_tabs
) {
4793 updating_dl_tabs
= 1; /* stop infinite recursion */
4794 TAILQ_FOREACH(t
, &tabs
, entry
)
4795 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4796 && (t
!= apart_from
))
4797 xtp_page_dl(t
, NULL
);
4798 updating_dl_tabs
= 0;
4803 * update all cookie tabs apart from one. Pass NULL if
4804 * you want to update all.
4807 update_cookie_tabs(struct tab
*apart_from
)
4810 if (!updating_cl_tabs
) {
4811 updating_cl_tabs
= 1; /* stop infinite recursion */
4812 TAILQ_FOREACH(t
, &tabs
, entry
)
4813 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4814 && (t
!= apart_from
))
4815 xtp_page_cl(t
, NULL
);
4816 updating_cl_tabs
= 0;
4821 * update all history tabs apart from one. Pass NULL if
4822 * you want to update all.
4825 update_history_tabs(struct tab
*apart_from
)
4829 if (!updating_hl_tabs
) {
4830 updating_hl_tabs
= 1; /* stop infinite recursion */
4831 TAILQ_FOREACH(t
, &tabs
, entry
)
4832 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4833 && (t
!= apart_from
))
4834 xtp_page_hl(t
, NULL
);
4835 updating_hl_tabs
= 0;
4839 /* cookie management XTP page */
4841 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4843 char *body
, *page
, *tmp
;
4844 int i
= 1; /* all ids start 1 */
4845 GSList
*sc
, *pc
, *pc_start
;
4847 char *type
, *table_headers
, *last_domain
;
4849 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4852 show_oops(NULL
, "%s invalid parameters", __func__
);
4856 /* Generate a new session key */
4857 if (!updating_cl_tabs
)
4858 generate_xtp_session_key(&cl_session_key
);
4861 table_headers
= g_strdup_printf("<table><tr>"
4864 "<th style='width:200px'>Value</th>"
4868 "<th>HTTP<br />only</th>"
4869 "<th style='width:40px'>Rm</th></tr>\n");
4871 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4872 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4876 last_domain
= strdup("");
4877 for (; sc
; sc
= sc
->next
) {
4880 if (strcmp(last_domain
, c
->domain
) != 0) {
4883 last_domain
= strdup(c
->domain
);
4887 body
= g_strdup_printf("%s</table>"
4889 body
, c
->domain
, table_headers
);
4893 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4894 c
->domain
, table_headers
);
4899 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4900 if (soup_cookie_equal(pc
->data
, c
)) {
4901 type
= "Session + Persistent";
4906 body
= g_strdup_printf(
4909 "<td style='word-wrap:normal'>%s</td>"
4911 " <textarea rows='4'>%s</textarea>"
4917 "<td style='text-align:center'>"
4918 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4925 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4940 soup_cookies_free(sc
);
4941 soup_cookies_free(pc
);
4943 /* small message if there are none */
4945 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4946 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4949 body
= g_strdup_printf("%s</table>", body
);
4952 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4954 g_free(table_headers
);
4955 g_free(last_domain
);
4957 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4958 update_cookie_tabs(t
);
4966 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4968 char *body
, *page
, *tmp
;
4970 int i
= 1; /* all ids start 1 */
4972 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4975 show_oops(NULL
, "%s invalid parameters", __func__
);
4979 /* Generate a new session key */
4980 if (!updating_hl_tabs
)
4981 generate_xtp_session_key(&hl_session_key
);
4984 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4985 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4987 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4989 body
= g_strdup_printf(
4991 "<td><a href='%s'>%s</a></td>"
4993 "<td style='text-align: center'>"
4994 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4995 body
, h
->uri
, h
->uri
, h
->title
,
4996 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4997 XT_XTP_HL_REMOVE
, i
);
5003 /* small message if there are none */
5006 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5007 "colspan='3'>No History</td></tr>\n", body
);
5012 body
= g_strdup_printf("%s</table>", body
);
5015 page
= get_html_page("History", body
, "", TRUE
);
5019 * update all history manager tabs as the xtp session
5020 * key has now changed. No need to update the current tab.
5021 * Already did that above.
5023 update_history_tabs(t
);
5025 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5032 * Generate a web page detailing the status of any downloads
5035 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5037 struct download
*dl
;
5038 char *body
, *page
, *tmp
;
5042 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5045 show_oops(NULL
, "%s invalid parameters", __func__
);
5050 * Generate a new session key for next page instance.
5051 * This only happens for the top level call to xtp_page_dl()
5052 * in which case updating_dl_tabs is 0.
5054 if (!updating_dl_tabs
)
5055 generate_xtp_session_key(&dl_session_key
);
5057 /* header - with refresh so as to update */
5058 if (refresh_interval
>= 1)
5059 ref
= g_strdup_printf(
5060 "<meta http-equiv='refresh' content='%u"
5061 ";url=%s%d/%s/%d' />\n",
5070 body
= g_strdup_printf("<div align='center'>"
5071 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5072 "</p><table><tr><th style='width: 60%%'>"
5073 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5074 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5076 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5077 body
= xtp_page_dl_row(t
, body
, dl
);
5081 /* message if no downloads in list */
5084 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5085 " style='text-align: center'>"
5086 "No downloads</td></tr>\n", body
);
5091 body
= g_strdup_printf("%s</table></div>", body
);
5094 page
= get_html_page("Downloads", body
, ref
, 1);
5099 * update all download manager tabs as the xtp session
5100 * key has now changed. No need to update the current tab.
5101 * Already did that above.
5103 update_download_tabs(t
);
5105 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5112 search(struct tab
*t
, struct karg
*args
)
5116 if (t
== NULL
|| args
== NULL
) {
5117 show_oops(NULL
, "search invalid parameters");
5122 case XT_SEARCH_NEXT
:
5123 d
= t
->search_forward
;
5125 case XT_SEARCH_PREV
:
5126 d
= !t
->search_forward
;
5129 return (XT_CB_PASSTHROUGH
);
5132 if (t
->search_text
== NULL
) {
5133 if (global_search
== NULL
)
5134 return (XT_CB_PASSTHROUGH
);
5136 d
= t
->search_forward
= TRUE
;
5137 t
->search_text
= g_strdup(global_search
);
5138 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5139 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5143 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5144 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5146 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5148 return (XT_CB_HANDLED
);
5151 struct settings_args
{
5157 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5160 struct settings_args
*sa
= cb_args
;
5165 if (s
->flags
& XT_SF_RUNTIME
)
5171 *sa
->body
= g_strdup_printf(
5173 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5174 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5186 set_show(struct tab
*t
, struct karg
*args
)
5188 char *body
, *page
, *tmp
;
5190 struct settings_args sa
;
5192 bzero(&sa
, sizeof sa
);
5196 body
= g_strdup_printf("<div align='center'><table><tr>"
5197 "<th align='left'>Setting</th>"
5198 "<th align='left'>Value</th></tr>\n");
5200 settings_walk(print_setting
, &sa
);
5203 /* small message if there are none */
5206 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5207 "colspan='2'>No settings</td></tr>\n", body
);
5212 body
= g_strdup_printf("%s</table></div>", body
);
5215 page
= get_html_page("Settings", body
, "", 0);
5219 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5223 return (XT_CB_PASSTHROUGH
);
5227 set(struct tab
*t
, struct karg
*args
)
5232 if (args
== NULL
|| args
->s
== NULL
)
5233 return (set_show(t
, args
));
5236 p
= g_strstrip(args
->s
);
5239 return (set_show(t
, args
));
5241 /* we got some sort of string */
5242 val
= g_strrstr(p
, "=");
5245 val
= g_strchomp(val
);
5248 for (i
= 0; i
< LENGTH(rs
); i
++) {
5249 if (strcmp(rs
[i
].name
, p
))
5252 if (rs
[i
].activate
) {
5253 if (rs
[i
].activate(val
))
5254 show_oops(t
, "%s invalid value %s",
5257 show_oops(t
, ":set %s = %s", p
, val
);
5260 show_oops(t
, "not a runtime option: %s", p
);
5264 show_oops(t
, "unknown option: %s", p
);
5268 for (i
= 0; i
< LENGTH(rs
); i
++) {
5269 if (strcmp(rs
[i
].name
, p
))
5272 /* XXX this could use some cleanup */
5273 switch (rs
[i
].type
) {
5276 show_oops(t
, "%s = %d",
5277 rs
[i
].name
, *rs
[i
].ival
);
5278 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5279 show_oops(t
, "%s = %s",
5281 rs
[i
].s
->get(&rs
[i
]));
5282 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5283 show_oops(t
, "%s = ...", rs
[i
].name
);
5285 show_oops(t
, "%s = ", rs
[i
].name
);
5289 show_oops(t
, "%s = %f",
5290 rs
[i
].name
, *rs
[i
].fval
);
5291 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5292 show_oops(t
, "%s = %s",
5294 rs
[i
].s
->get(&rs
[i
]));
5295 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5296 show_oops(t
, "%s = ...", rs
[i
].name
);
5298 show_oops(t
, "%s = ", rs
[i
].name
);
5301 if (rs
[i
].sval
&& *rs
[i
].sval
)
5302 show_oops(t
, "%s = %s",
5303 rs
[i
].name
, *rs
[i
].sval
);
5304 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5305 show_oops(t
, "%s = %s",
5307 rs
[i
].s
->get(&rs
[i
]));
5308 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5309 show_oops(t
, "%s = ...", rs
[i
].name
);
5311 show_oops(t
, "%s = ", rs
[i
].name
);
5314 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5320 show_oops(t
, "unknown option: %s", p
);
5323 return (XT_CB_PASSTHROUGH
);
5327 session_save(struct tab
*t
, char *filename
)
5333 if (strlen(filename
) == 0)
5336 if (filename
[0] == '.' || filename
[0] == '/')
5340 if (save_tabs(t
, &a
))
5342 strlcpy(named_session
, filename
, sizeof named_session
);
5344 /* add the new session to the list of sessions */
5345 s
= g_malloc(sizeof(struct session
));
5346 s
->name
= g_strdup(filename
);
5347 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5355 session_open(struct tab
*t
, char *filename
)
5360 if (strlen(filename
) == 0)
5363 if (filename
[0] == '.' || filename
[0] == '/')
5367 a
.i
= XT_SES_CLOSETABS
;
5368 if (open_tabs(t
, &a
))
5371 strlcpy(named_session
, filename
, sizeof named_session
);
5379 session_delete(struct tab
*t
, char *filename
)
5381 char file
[PATH_MAX
];
5385 if (strlen(filename
) == 0)
5388 if (filename
[0] == '.' || filename
[0] == '/')
5391 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5395 if (!strcmp(filename
, named_session
))
5396 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5397 sizeof named_session
);
5399 /* remove session from sessions list */
5400 TAILQ_FOREACH(s
, &sessions
, entry
) {
5401 if (!strcmp(s
->name
, filename
))
5406 TAILQ_REMOVE(&sessions
, s
, entry
);
5407 g_free((gpointer
) s
->name
);
5416 session_cmd(struct tab
*t
, struct karg
*args
)
5418 char *filename
= args
->s
;
5423 if (args
->i
& XT_SHOW
)
5424 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5425 XT_SAVED_TABS_FILE
: named_session
);
5426 else if (args
->i
& XT_SAVE
) {
5427 if (session_save(t
, filename
)) {
5428 show_oops(t
, "Can't save session: %s",
5429 filename
? filename
: "INVALID");
5432 } else if (args
->i
& XT_OPEN
) {
5433 if (session_open(t
, filename
)) {
5434 show_oops(t
, "Can't open session: %s",
5435 filename
? filename
: "INVALID");
5438 } else if (args
->i
& XT_DELETE
) {
5439 if (session_delete(t
, filename
)) {
5440 show_oops(t
, "Can't delete session: %s",
5441 filename
? filename
: "INVALID");
5446 return (XT_CB_PASSTHROUGH
);
5450 * Make a hardcopy of the page
5453 print_page(struct tab
*t
, struct karg
*args
)
5455 WebKitWebFrame
*frame
;
5457 GtkPrintOperation
*op
;
5458 GtkPrintOperationAction action
;
5459 GtkPrintOperationResult print_res
;
5460 GError
*g_err
= NULL
;
5461 int marg_l
, marg_r
, marg_t
, marg_b
;
5463 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5465 ps
= gtk_page_setup_new();
5466 op
= gtk_print_operation_new();
5467 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5468 frame
= webkit_web_view_get_main_frame(t
->wv
);
5470 /* the default margins are too small, so we will bump them */
5471 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5472 XT_PRINT_EXTRA_MARGIN
;
5473 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5474 XT_PRINT_EXTRA_MARGIN
;
5475 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5476 XT_PRINT_EXTRA_MARGIN
;
5477 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5478 XT_PRINT_EXTRA_MARGIN
;
5481 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5482 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5483 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5484 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5486 gtk_print_operation_set_default_page_setup(op
, ps
);
5488 /* this appears to free 'op' and 'ps' */
5489 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5491 /* check it worked */
5492 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5493 show_oops(NULL
, "can't print: %s", g_err
->message
);
5494 g_error_free (g_err
);
5502 go_home(struct tab
*t
, struct karg
*args
)
5509 set_encoding(struct tab
*t
, struct karg
*args
)
5513 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5514 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5516 e
= webkit_web_view_get_encoding(t
->wv
);
5517 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5519 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5525 restart(struct tab
*t
, struct karg
*args
)
5529 a
.s
= XT_RESTART_TABS_FILE
;
5531 execvp(start_argv
[0], start_argv
);
5537 #define CTRL GDK_CONTROL_MASK
5538 #define MOD1 GDK_MOD1_MASK
5539 #define SHFT GDK_SHIFT_MASK
5541 /* inherent to GTK not all keys will be caught at all times */
5542 /* XXX sort key bindings */
5543 struct key_binding
{
5548 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5550 { "cookiejar", MOD1
, 0, GDK_j
},
5551 { "downloadmgr", MOD1
, 0, GDK_d
},
5552 { "history", MOD1
, 0, GDK_h
},
5553 { "print", CTRL
, 0, GDK_p
},
5554 { "search", 0, 0, GDK_slash
},
5555 { "searchb", 0, 0, GDK_question
},
5556 { "statustoggle", CTRL
, 0, GDK_n
},
5557 { "command", 0, 0, GDK_colon
},
5558 { "qa", CTRL
, 0, GDK_q
},
5559 { "restart", MOD1
, 0, GDK_q
},
5560 { "js toggle", CTRL
, 0, GDK_j
},
5561 { "cookie toggle", MOD1
, 0, GDK_c
},
5562 { "togglesrc", CTRL
, 0, GDK_s
},
5563 { "yankuri", 0, 0, GDK_y
},
5564 { "pasteuricur", 0, 0, GDK_p
},
5565 { "pasteurinew", 0, 0, GDK_P
},
5566 { "toplevel toggle", 0, 0, GDK_F4
},
5567 { "help", 0, 0, GDK_F1
},
5568 { "run_script", MOD1
, 0, GDK_r
},
5571 { "searchnext", 0, 0, GDK_n
},
5572 { "searchprevious", 0, 0, GDK_N
},
5575 { "focusaddress", 0, 0, GDK_F6
},
5576 { "focussearch", 0, 0, GDK_F7
},
5579 { "hinting", 0, 0, GDK_f
},
5581 /* custom stylesheet */
5582 { "userstyle", 0, 0, GDK_i
},
5585 { "goback", 0, 0, GDK_BackSpace
},
5586 { "goback", MOD1
, 0, GDK_Left
},
5587 { "goforward", SHFT
, 0, GDK_BackSpace
},
5588 { "goforward", MOD1
, 0, GDK_Right
},
5589 { "reload", 0, 0, GDK_F5
},
5590 { "reload", CTRL
, 0, GDK_r
},
5591 { "reload", CTRL
, 0, GDK_l
},
5592 { "favorites", MOD1
, 1, GDK_f
},
5594 /* vertical movement */
5595 { "scrolldown", 0, 0, GDK_j
},
5596 { "scrolldown", 0, 0, GDK_Down
},
5597 { "scrollup", 0, 0, GDK_Up
},
5598 { "scrollup", 0, 0, GDK_k
},
5599 { "scrollbottom", 0, 0, GDK_G
},
5600 { "scrollbottom", 0, 0, GDK_End
},
5601 { "scrolltop", 0, 0, GDK_Home
},
5602 { "scrollpagedown", 0, 0, GDK_space
},
5603 { "scrollpagedown", CTRL
, 0, GDK_f
},
5604 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5605 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5606 { "scrollpageup", 0, 0, GDK_Page_Up
},
5607 { "scrollpageup", CTRL
, 0, GDK_b
},
5608 { "scrollhalfup", CTRL
, 0, GDK_u
},
5609 /* horizontal movement */
5610 { "scrollright", 0, 0, GDK_l
},
5611 { "scrollright", 0, 0, GDK_Right
},
5612 { "scrollleft", 0, 0, GDK_Left
},
5613 { "scrollleft", 0, 0, GDK_h
},
5614 { "scrollfarright", 0, 0, GDK_dollar
},
5615 { "scrollfarleft", 0, 0, GDK_0
},
5618 { "tabnew", CTRL
, 0, GDK_t
},
5619 { "999tabnew", CTRL
, 0, GDK_T
},
5620 { "tabclose", CTRL
, 1, GDK_w
},
5621 { "tabundoclose", 0, 0, GDK_U
},
5622 { "tabnext 1", CTRL
, 0, GDK_1
},
5623 { "tabnext 2", CTRL
, 0, GDK_2
},
5624 { "tabnext 3", CTRL
, 0, GDK_3
},
5625 { "tabnext 4", CTRL
, 0, GDK_4
},
5626 { "tabnext 5", CTRL
, 0, GDK_5
},
5627 { "tabnext 6", CTRL
, 0, GDK_6
},
5628 { "tabnext 7", CTRL
, 0, GDK_7
},
5629 { "tabnext 8", CTRL
, 0, GDK_8
},
5630 { "tabnext 9", CTRL
, 0, GDK_9
},
5631 { "tabfirst", CTRL
, 0, GDK_less
},
5632 { "tablast", CTRL
, 0, GDK_greater
},
5633 { "tabprevious", CTRL
, 0, GDK_Left
},
5634 { "tabnext", CTRL
, 0, GDK_Right
},
5635 { "focusout", CTRL
, 0, GDK_minus
},
5636 { "focusin", CTRL
, 0, GDK_plus
},
5637 { "focusin", CTRL
, 0, GDK_equal
},
5638 { "focusreset", CTRL
, 0, GDK_0
},
5640 /* command aliases (handy when -S flag is used) */
5641 { "promptopen", 0, 0, GDK_F9
},
5642 { "promptopencurrent", 0, 0, GDK_F10
},
5643 { "prompttabnew", 0, 0, GDK_F11
},
5644 { "prompttabnewcurrent",0, 0, GDK_F12
},
5646 TAILQ_HEAD(keybinding_list
, key_binding
);
5649 walk_kb(struct settings
*s
,
5650 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5652 struct key_binding
*k
;
5655 if (s
== NULL
|| cb
== NULL
) {
5656 show_oops(NULL
, "walk_kb invalid parameters");
5660 TAILQ_FOREACH(k
, &kbl
, entry
) {
5666 if (gdk_keyval_name(k
->key
) == NULL
)
5669 strlcat(str
, k
->cmd
, sizeof str
);
5670 strlcat(str
, ",", sizeof str
);
5672 if (k
->mask
& GDK_SHIFT_MASK
)
5673 strlcat(str
, "S-", sizeof str
);
5674 if (k
->mask
& GDK_CONTROL_MASK
)
5675 strlcat(str
, "C-", sizeof str
);
5676 if (k
->mask
& GDK_MOD1_MASK
)
5677 strlcat(str
, "M1-", sizeof str
);
5678 if (k
->mask
& GDK_MOD2_MASK
)
5679 strlcat(str
, "M2-", sizeof str
);
5680 if (k
->mask
& GDK_MOD3_MASK
)
5681 strlcat(str
, "M3-", sizeof str
);
5682 if (k
->mask
& GDK_MOD4_MASK
)
5683 strlcat(str
, "M4-", sizeof str
);
5684 if (k
->mask
& GDK_MOD5_MASK
)
5685 strlcat(str
, "M5-", sizeof str
);
5687 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5688 cb(s
, str
, cb_args
);
5693 init_keybindings(void)
5696 struct key_binding
*k
;
5698 for (i
= 0; i
< LENGTH(keys
); i
++) {
5699 k
= g_malloc0(sizeof *k
);
5700 k
->cmd
= keys
[i
].cmd
;
5701 k
->mask
= keys
[i
].mask
;
5702 k
->use_in_entry
= keys
[i
].use_in_entry
;
5703 k
->key
= keys
[i
].key
;
5704 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5706 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5707 k
->cmd
? k
->cmd
: "unnamed key");
5712 keybinding_clearall(void)
5714 struct key_binding
*k
, *next
;
5716 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5717 next
= TAILQ_NEXT(k
, entry
);
5721 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5722 k
->cmd
? k
->cmd
: "unnamed key");
5723 TAILQ_REMOVE(&kbl
, k
, entry
);
5729 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5731 struct key_binding
*k
;
5732 guint keyval
, mask
= 0;
5735 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5737 /* Keys which are to be used in entry have been prefixed with an
5738 * exclamation mark. */
5742 /* find modifier keys */
5743 if (strstr(key
, "S-"))
5744 mask
|= GDK_SHIFT_MASK
;
5745 if (strstr(key
, "C-"))
5746 mask
|= GDK_CONTROL_MASK
;
5747 if (strstr(key
, "M1-"))
5748 mask
|= GDK_MOD1_MASK
;
5749 if (strstr(key
, "M2-"))
5750 mask
|= GDK_MOD2_MASK
;
5751 if (strstr(key
, "M3-"))
5752 mask
|= GDK_MOD3_MASK
;
5753 if (strstr(key
, "M4-"))
5754 mask
|= GDK_MOD4_MASK
;
5755 if (strstr(key
, "M5-"))
5756 mask
|= GDK_MOD5_MASK
;
5759 for (i
= strlen(key
) - 1; i
> 0; i
--)
5763 /* validate keyname */
5764 keyval
= gdk_keyval_from_name(key
);
5765 if (keyval
== GDK_VoidSymbol
) {
5766 warnx("invalid keybinding name %s", key
);
5769 /* must run this test too, gtk+ doesn't handle 10 for example */
5770 if (gdk_keyval_name(keyval
) == NULL
) {
5771 warnx("invalid keybinding name %s", key
);
5775 /* Remove eventual dupes. */
5776 TAILQ_FOREACH(k
, &kbl
, entry
)
5777 if (k
->key
== keyval
&& k
->mask
== mask
) {
5778 TAILQ_REMOVE(&kbl
, k
, entry
);
5784 k
= g_malloc0(sizeof *k
);
5785 k
->cmd
= g_strdup(cmd
);
5787 k
->use_in_entry
= use_in_entry
;
5790 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5795 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5796 k
->cmd
, gdk_keyval_name(keyval
));
5798 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5804 add_kb(struct settings
*s
, char *entry
)
5808 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5810 /* clearall is special */
5811 if (!strcmp(entry
, "clearall")) {
5812 keybinding_clearall();
5816 kb
= strstr(entry
, ",");
5822 return (keybinding_add(entry
, key
, key
[0] == '!'));
5828 int (*func
)(struct tab
*, struct karg
*);
5832 { "command", 0, command
, ':', 0 },
5833 { "search", 0, command
, '/', 0 },
5834 { "searchb", 0, command
, '?', 0 },
5835 { "togglesrc", 0, toggle_src
, 0, 0 },
5837 /* yanking and pasting */
5838 { "yankuri", 0, yank_uri
, 0, 0 },
5839 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5840 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5841 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5844 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5845 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5848 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5849 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5852 { "hinting", 0, hint
, 0, 0 },
5854 /* custom stylesheet */
5855 { "userstyle", 0, userstyle
, 0, 0 },
5858 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5859 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5860 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5862 /* vertical movement */
5863 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5864 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5865 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5866 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5867 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5868 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5869 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5870 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5871 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5872 /* horizontal movement */
5873 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5874 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5875 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5876 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5878 { "favorites", 0, xtp_page_fl
, 0, 0 },
5879 { "fav", 0, xtp_page_fl
, 0, 0 },
5880 { "favadd", 0, add_favorite
, 0, 0 },
5882 { "qall", 0, quit
, 0, 0 },
5883 { "quitall", 0, quit
, 0, 0 },
5884 { "w", 0, save_tabs
, 0, 0 },
5885 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5886 { "help", 0, help
, 0, 0 },
5887 { "about", 0, about
, 0, 0 },
5888 { "stats", 0, stats
, 0, 0 },
5889 { "version", 0, about
, 0, 0 },
5892 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5893 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5894 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5895 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5896 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5897 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5898 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5899 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5900 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5901 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5902 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5904 /* cookie command */
5905 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5906 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5907 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5908 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5909 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5910 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5911 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5912 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5913 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5914 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5915 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5917 /* toplevel (domain) command */
5918 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5919 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5922 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5925 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5926 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5927 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5929 { "ca", 0, ca_cmd
, 0, 0 },
5930 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5931 { "dl", 0, xtp_page_dl
, 0, 0 },
5932 { "h", 0, xtp_page_hl
, 0, 0 },
5933 { "history", 0, xtp_page_hl
, 0, 0 },
5934 { "home", 0, go_home
, 0, 0 },
5935 { "restart", 0, restart
, 0, 0 },
5936 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5937 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5938 { "statustoggle", 0, statustoggle
, 0, 0 },
5939 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5941 { "print", 0, print_page
, 0, 0 },
5944 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5945 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5946 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5947 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5948 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5949 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5950 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5951 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5952 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5953 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5954 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5955 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5956 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5957 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5958 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5959 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5960 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5961 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5962 { "buffers", 0, buffers
, 0, 0 },
5963 { "ls", 0, buffers
, 0, 0 },
5964 { "tabs", 0, buffers
, 0, 0 },
5965 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
5967 /* command aliases (handy when -S flag is used) */
5968 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5969 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5970 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5971 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5974 { "set", 0, set
, 0, XT_SETARG
},
5976 { "fullscreen", 0, fullscreen
, 0, 0 },
5977 { "f", 0, fullscreen
, 0, 0 },
5980 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5981 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
5982 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
5983 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5984 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5991 } cmd_status
= {-1, 0};
5994 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5997 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
6004 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6011 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6013 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
6019 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
6021 a
.i
= XT_NAV_FORWARD
;
6031 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6033 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6035 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6042 * cancel, remove, etc. downloads
6045 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6047 struct download find
, *d
= NULL
;
6049 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6051 /* some commands require a valid download id */
6052 if (cmd
!= XT_XTP_DL_LIST
) {
6053 /* lookup download in question */
6055 d
= RB_FIND(download_list
, &downloads
, &find
);
6058 show_oops(t
, "%s: no such download", __func__
);
6063 /* decide what to do */
6065 case XT_XTP_DL_CANCEL
:
6066 webkit_download_cancel(d
->download
);
6068 case XT_XTP_DL_REMOVE
:
6069 webkit_download_cancel(d
->download
); /* just incase */
6070 g_object_unref(d
->download
);
6071 RB_REMOVE(download_list
, &downloads
, d
);
6073 case XT_XTP_DL_LIST
:
6077 show_oops(t
, "%s: unknown command", __func__
);
6080 xtp_page_dl(t
, NULL
);
6084 * Actions on history, only does one thing for now, but
6085 * we provide the function for future actions
6088 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6090 struct history
*h
, *next
;
6094 case XT_XTP_HL_REMOVE
:
6095 /* walk backwards, as listed in reverse */
6096 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6097 next
= RB_PREV(history_list
, &hl
, h
);
6099 RB_REMOVE(history_list
, &hl
, h
);
6100 g_free((gpointer
) h
->title
);
6101 g_free((gpointer
) h
->uri
);
6108 case XT_XTP_HL_LIST
:
6109 /* Nothing - just xtp_page_hl() below */
6112 show_oops(t
, "%s: unknown command", __func__
);
6116 xtp_page_hl(t
, NULL
);
6119 /* remove a favorite */
6121 remove_favorite(struct tab
*t
, int index
)
6123 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6124 char *new_favs
, *tmp
;
6129 /* open favorites */
6130 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6132 if ((f
= fopen(file
, "r")) == NULL
) {
6133 show_oops(t
, "%s: can't open favorites: %s",
6134 __func__
, strerror(errno
));
6138 /* build a string which will become the new favroites file */
6139 new_favs
= g_strdup("");
6142 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6143 if (feof(f
) || ferror(f
))
6145 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6152 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6153 if (feof(f
) || ferror(f
)) {
6154 show_oops(t
, "%s: can't parse favorites %s",
6155 __func__
, strerror(errno
));
6160 /* as long as this isn't the one we are deleting add to file */
6163 new_favs
= g_strdup_printf("%s%s\n%s\n",
6164 new_favs
, title
, uri
);
6176 /* write back new favorites file */
6177 if ((f
= fopen(file
, "w")) == NULL
) {
6178 show_oops(t
, "%s: can't open favorites: %s",
6179 __func__
, strerror(errno
));
6183 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
6184 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
6197 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6200 case XT_XTP_FL_LIST
:
6201 /* nothing, just the below call to xtp_page_fl() */
6203 case XT_XTP_FL_REMOVE
:
6204 remove_favorite(t
, arg
);
6207 show_oops(t
, "%s: invalid favorites command", __func__
);
6211 xtp_page_fl(t
, NULL
);
6215 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6218 case XT_XTP_CL_LIST
:
6219 /* nothing, just xtp_page_cl() */
6221 case XT_XTP_CL_REMOVE
:
6225 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6229 xtp_page_cl(t
, NULL
);
6232 /* link an XTP class to it's session key and handler function */
6233 struct xtp_despatch
{
6236 void (*handle_func
)(struct tab
*, uint8_t, int);
6239 struct xtp_despatch xtp_despatches
[] = {
6240 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6241 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6242 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6243 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6244 { XT_XTP_INVALID
, NULL
, NULL
}
6248 * is the url xtp protocol? (xxxt://)
6249 * if so, parse and despatch correct bahvior
6252 parse_xtp_url(struct tab
*t
, const char *url
)
6254 char *dup
= NULL
, *p
, *last
= NULL
;
6255 uint8_t n_tokens
= 0;
6256 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6257 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6262 * tokens array meaning:
6264 * tokens[1] = session key
6265 * tokens[2] = action
6266 * tokens[3] = optional argument
6269 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6271 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6274 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6276 /* split out the url */
6277 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6278 (p
= strtok_r(NULL
, "/", &last
))) {
6280 tokens
[n_tokens
++] = p
;
6283 /* should be atleast three fields 'class/seskey/command/arg' */
6287 dsp
= xtp_despatches
;
6288 req_class
= atoi(tokens
[0]);
6289 while (dsp
->xtp_class
) {
6290 if (dsp
->xtp_class
== req_class
) {
6297 /* did we find one atall? */
6298 if (dsp_match
== NULL
) {
6299 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6303 /* check session key and call despatch function */
6304 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6305 ret
= TRUE
; /* all is well, this was a valid xtp request */
6306 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6319 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6321 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6323 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6326 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6331 show_oops(t
, "activate_uri_entry_cb no uri");
6335 uri
+= strspn(uri
, "\t ");
6337 /* if xxxt:// treat specially */
6338 if (parse_xtp_url(t
, uri
))
6341 /* otherwise continue to load page normally */
6342 load_uri(t
, (gchar
*)uri
);
6347 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6349 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6350 char *newuri
= NULL
;
6353 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6356 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6360 if (search_string
== NULL
) {
6361 show_oops(t
, "no search_string");
6365 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6367 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6368 newuri
= g_strdup_printf(search_string
, enc_search
);
6372 webkit_web_view_load_uri(t
->wv
, newuri
);
6380 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6382 struct domain
*d
= NULL
;
6385 if (uri
== NULL
|| t
== NULL
)
6388 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6393 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6394 es
? "enable" : "disable", uri
);
6396 g_object_set(G_OBJECT(t
->settings
),
6397 "enable-html5-local-storage", es
, (char *)NULL
);
6398 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6402 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6404 struct domain
*d
= NULL
;
6407 if (uri
== NULL
|| t
== NULL
)
6410 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6415 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6416 es
? "enable" : "disable", uri
);
6418 g_object_set(G_OBJECT(t
->settings
),
6419 "enable-scripts", es
, (char *)NULL
);
6420 g_object_set(G_OBJECT(t
->settings
),
6421 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6422 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6424 button_set_stockid(t
->js_toggle
,
6425 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6429 color_address_bar(gpointer p
)
6432 struct tab
*tt
, *t
= p
;
6433 gchar
*col_str
= XT_COLOR_WHITE
;
6434 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
6437 gdk_threads_enter();
6439 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6441 /* make sure t still exists */
6444 TAILQ_FOREACH(tt
, &tabs
, entry
)
6450 if ((uri
= get_uri(t
)) == NULL
)
6455 gdk_threads_leave();
6458 col_str
= XT_COLOR_YELLOW
;
6459 switch (load_compare_cert(u
, &error_str
)) {
6461 col_str
= XT_COLOR_BLUE
;
6464 col_str
= XT_COLOR_GREEN
;
6466 case CERT_UNTRUSTED
:
6467 col_str
= XT_COLOR_YELLOW
;
6470 col_str
= XT_COLOR_RED
;
6475 gdk_threads_enter();
6477 /* make sure t isn't deleted */
6478 TAILQ_FOREACH(tt
, &tabs
, entry
)
6485 /* test to see if the user navigated away and canceled the thread */
6486 if (t
->thread
!= g_thread_self())
6488 if ((uri
= get_uri(t
)) == NULL
) {
6492 if (strcmp(uri
, u
)) {
6493 /* make sure we are still the same url */
6499 gdk_color_parse(col_str
, &color
);
6500 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6502 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6503 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6505 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6507 if (error_str
&& error_str
[0] != '\0')
6508 show_oops(t
, "%s", error_str
);
6513 /* t is invalid at this point */
6515 g_free((gpointer
)u
);
6517 gdk_threads_leave();
6522 show_ca_status(struct tab
*t
, const char *uri
)
6525 gchar
*col_str
= XT_COLOR_WHITE
;
6527 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6528 ssl_strict_certs
, ssl_ca_file
, uri
);
6535 if (ssl_ca_file
== NULL
) {
6536 if (g_str_has_prefix(uri
, "http://"))
6538 if (g_str_has_prefix(uri
, "https://")) {
6539 col_str
= XT_COLOR_RED
;
6544 if (g_str_has_prefix(uri
, "http://") ||
6545 !g_str_has_prefix(uri
, "https://"))
6549 * It is not necessary to see if the thread is already running.
6550 * If the thread is in progress setting it to something else aborts it
6554 /* thread the coloring of the address bar */
6555 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
6557 color_address_bar(t
);
6563 gdk_color_parse(col_str
, &color
);
6564 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6566 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6567 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6569 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6574 free_favicon(struct tab
*t
)
6576 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6577 __func__
, t
->icon_download
, t
->icon_request
);
6579 if (t
->icon_request
)
6580 g_object_unref(t
->icon_request
);
6581 if (t
->icon_dest_uri
)
6582 g_free(t
->icon_dest_uri
);
6584 t
->icon_request
= NULL
;
6585 t
->icon_dest_uri
= NULL
;
6589 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6591 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6592 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6594 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6595 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6597 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6598 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6602 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6604 GdkPixbuf
*pb_scaled
;
6606 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6607 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6608 GDK_INTERP_BILINEAR
);
6612 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6613 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6615 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6616 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6618 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6619 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6621 if (pb_scaled
!= pb
)
6622 g_object_unref(pb_scaled
);
6626 xt_icon_from_file(struct tab
*t
, char *file
)
6630 if (g_str_has_prefix(file
, "file://"))
6631 file
+= strlen("file://");
6633 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6635 xt_icon_from_pixbuf(t
, pb
);
6638 xt_icon_from_name(t
, "text-html");
6642 is_valid_icon(char *file
)
6645 const char *mime_type
;
6649 gf
= g_file_new_for_path(file
);
6650 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6652 mime_type
= g_file_info_get_content_type(fi
);
6653 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6654 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6655 g_strcmp0(mime_type
, "image/png") == 0 ||
6656 g_strcmp0(mime_type
, "image/gif") == 0 ||
6657 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6665 set_favicon_from_file(struct tab
*t
, char *file
)
6669 if (t
== NULL
|| file
== NULL
)
6672 if (g_str_has_prefix(file
, "file://"))
6673 file
+= strlen("file://");
6674 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6676 if (!stat(file
, &sb
)) {
6677 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6678 /* corrupt icon so trash it */
6679 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6682 /* no need to set icon to default here */
6686 xt_icon_from_file(t
, file
);
6690 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6693 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6694 struct tab
*tt
= NULL
, *t
= NULL
;
6697 * find the webview instead of passing in the tab as it could have been
6698 * deleted from underneath us.
6700 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6709 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6710 __func__
, t
->tab_id
, status
);
6713 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6715 t
->icon_download
= NULL
;
6718 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6721 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6724 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6726 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6727 __func__
, t
->tab_id
);
6728 t
->icon_download
= NULL
;
6731 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6734 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6735 __func__
, t
->icon_dest_uri
);
6736 set_favicon_from_file(t
, t
->icon_dest_uri
);
6737 /* these will be freed post callback */
6738 t
->icon_request
= NULL
;
6739 t
->icon_download
= NULL
;
6747 abort_favicon_download(struct tab
*t
)
6749 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6751 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6752 if (t
->icon_download
) {
6753 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6754 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6755 webkit_download_cancel(t
->icon_download
);
6756 t
->icon_download
= NULL
;
6761 xt_icon_from_name(t
, "text-html");
6765 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6767 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6769 if (uri
== NULL
|| t
== NULL
)
6772 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6773 /* take icon from WebKitIconDatabase */
6776 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6778 xt_icon_from_pixbuf(t
, pb
);
6781 xt_icon_from_name(t
, "text-html");
6782 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6783 /* download icon to cache dir */
6784 gchar
*name_hash
, file
[PATH_MAX
];
6787 if (t
->icon_request
) {
6788 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6792 /* check to see if we got the icon in cache */
6793 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6794 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6797 if (!stat(file
, &sb
)) {
6798 if (sb
.st_size
> 0) {
6799 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6801 set_favicon_from_file(t
, file
);
6805 /* corrupt icon so trash it */
6806 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6811 /* create download for icon */
6812 t
->icon_request
= webkit_network_request_new(uri
);
6813 if (t
->icon_request
== NULL
) {
6814 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6819 t
->icon_download
= webkit_download_new(t
->icon_request
);
6820 if (t
->icon_download
== NULL
)
6823 /* we have to free icon_dest_uri later */
6824 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6825 webkit_download_set_destination_uri(t
->icon_download
,
6828 if (webkit_download_get_status(t
->icon_download
) ==
6829 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6830 g_object_unref(t
->icon_request
);
6831 g_free(t
->icon_dest_uri
);
6832 t
->icon_request
= NULL
;
6833 t
->icon_dest_uri
= NULL
;
6837 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6838 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6840 webkit_download_start(t
->icon_download
);
6845 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6847 const gchar
*uri
= NULL
, *title
= NULL
;
6848 struct history
*h
, find
;
6852 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6853 webkit_web_view_get_load_status(wview
),
6854 get_uri(t
) ? get_uri(t
) : "NOTHING");
6857 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6861 switch (webkit_web_view_get_load_status(wview
)) {
6862 case WEBKIT_LOAD_PROVISIONAL
:
6864 abort_favicon_download(t
);
6865 #if GTK_CHECK_VERSION(2, 20, 0)
6866 gtk_widget_show(t
->spinner
);
6867 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6869 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6871 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6873 /* assume we are a new address */
6874 gdk_color_parse("white", &color
);
6875 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6876 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6878 /* take focus if we are visible */
6882 /* kill color thread */
6887 case WEBKIT_LOAD_COMMITTED
:
6892 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6898 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6900 /* check if js white listing is enabled */
6901 if (enable_cookie_whitelist
)
6902 check_and_set_cookie(uri
, t
);
6903 if (enable_js_whitelist
)
6904 check_and_set_js(uri
, t
);
6910 /* we know enough to autosave the session */
6911 if (session_autosave
) {
6916 show_ca_status(t
, uri
);
6919 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6923 case WEBKIT_LOAD_FINISHED
:
6929 if (!strncmp(uri
, "http://", strlen("http://")) ||
6930 !strncmp(uri
, "https://", strlen("https://")) ||
6931 !strncmp(uri
, "file://", strlen("file://"))) {
6933 h
= RB_FIND(history_list
, &hl
, &find
);
6935 title
= get_title(t
, FALSE
);
6936 h
= g_malloc(sizeof *h
);
6937 h
->uri
= g_strdup(uri
);
6938 h
->title
= g_strdup(title
);
6939 RB_INSERT(history_list
, &hl
, h
);
6940 completion_add_uri(h
->uri
);
6941 update_history_tabs(NULL
);
6945 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6946 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6947 case WEBKIT_LOAD_FAILED
:
6950 #if GTK_CHECK_VERSION(2, 20, 0)
6951 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6952 gtk_widget_hide(t
->spinner
);
6955 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6960 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6962 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6963 can_go_back_for_real(t
));
6965 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6966 can_go_forward_for_real(t
));
6970 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6972 const gchar
*title
= NULL
, *win_title
= NULL
;
6974 title
= get_title(t
, FALSE
);
6975 win_title
= get_title(t
, TRUE
);
6976 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6977 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6978 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6979 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6983 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6985 run_script(t
, JS_HINTING
);
6989 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6991 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6992 progress
== 100 ? 0 : (double)progress
/ 100);
6993 if (show_url
== 0) {
6994 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6995 progress
== 100 ? 0 : (double)progress
/ 100);
6998 update_statusbar_position(NULL
, NULL
);
7002 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
7003 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
7004 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
7007 WebKitWebNavigationReason reason
;
7008 struct domain
*d
= NULL
;
7011 show_oops(NULL
, "webview_npd_cb invalid parameters");
7015 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
7017 webkit_network_request_get_uri(request
));
7019 uri
= (char *)webkit_network_request_get_uri(request
);
7021 /* if this is an xtp url, we don't load anything else */
7022 if (parse_xtp_url(t
, uri
))
7025 if (t
->ctrl_click
) {
7027 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
7028 webkit_web_policy_decision_ignore(pd
);
7029 return (TRUE
); /* we made the decission */
7033 * This is a little hairy but it comes down to this:
7034 * when we run in whitelist mode we have to assist the browser in
7035 * opening the URL that it would have opened in a new tab.
7037 reason
= webkit_web_navigation_action_get_reason(na
);
7038 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
7039 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7040 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
7041 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7043 webkit_web_policy_decision_use(pd
);
7044 return (TRUE
); /* we made the decision */
7051 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7054 struct domain
*d
= NULL
;
7056 WebKitWebView
*webview
= NULL
;
7058 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
7059 webkit_web_view_get_uri(wv
));
7062 /* open in current tab */
7064 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7065 uri
= webkit_web_view_get_uri(wv
);
7066 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7069 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7071 } else if (enable_scripts
== 1) {
7072 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7080 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
7083 struct domain
*d
= NULL
;
7085 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7087 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7088 uri
= webkit_web_view_get_uri(wv
);
7089 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7093 } else if (enable_scripts
== 1)
7100 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7102 /* we can not eat the event without throwing gtk off so defer it */
7104 /* catch middle click */
7105 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7110 /* catch ctrl click */
7111 if (e
->type
== GDK_BUTTON_RELEASE
&&
7112 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7117 return (XT_CB_PASSTHROUGH
);
7121 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7123 struct mime_type
*m
;
7125 m
= find_mime_type(mime_type
);
7133 show_oops(t
, "can't fork mime handler");
7143 execlp(m
->mt_action
, m
->mt_action
,
7144 webkit_network_request_get_uri(request
), (void *)NULL
);
7153 get_mime_type(const char *file
)
7156 char *mime_type
= NULL
;
7160 if (g_str_has_prefix(file
, "file://"))
7161 file
+= strlen("file://");
7163 gf
= g_file_new_for_path(file
);
7164 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7166 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
7167 mime_type
= g_strdup(m
);
7175 run_download_mimehandler(char *mime_type
, char *file
)
7177 struct mime_type
*m
;
7179 m
= find_mime_type(mime_type
);
7185 show_oops(NULL
, "can't fork download mime handler");
7195 if (g_str_has_prefix(file
, "file://"))
7196 file
+= strlen("file://");
7197 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7206 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7209 WebKitDownloadStatus status
;
7210 const char *file
= NULL
;
7213 if (download
== NULL
)
7215 status
= webkit_download_get_status(download
);
7216 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7219 file
= webkit_download_get_destination_uri(download
);
7222 mime
= get_mime_type(file
);
7226 run_download_mimehandler((char *)mime
, (char *)file
);
7231 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7232 WebKitNetworkRequest
*request
, char *mime_type
,
7233 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7236 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7240 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7241 t
->tab_id
, mime_type
);
7243 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7244 webkit_web_policy_decision_ignore(decision
);
7249 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7250 webkit_web_policy_decision_download(decision
);
7258 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7262 const gchar
*suggested_name
;
7263 gchar
*filename
= NULL
;
7265 struct download
*download_entry
;
7268 if (wk_download
== NULL
|| t
== NULL
) {
7269 show_oops(NULL
, "%s invalid parameters", __func__
);
7273 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7274 if (suggested_name
== NULL
)
7275 return (FALSE
); /* abort download */
7286 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7288 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7289 filename
: suggested_name
);
7291 } while (!stat(uri
+ strlen("file://"), &sb
));
7293 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7294 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7296 webkit_download_set_destination_uri(wk_download
, uri
);
7298 if (webkit_download_get_status(wk_download
) ==
7299 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7300 show_oops(t
, "%s: download failed to start", __func__
);
7302 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7304 /* connect "download first" mime handler */
7305 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7306 G_CALLBACK(download_status_changed_cb
), NULL
);
7308 download_entry
= g_malloc(sizeof(struct download
));
7309 download_entry
->download
= wk_download
;
7310 download_entry
->tab
= t
;
7311 download_entry
->id
= next_download_id
++;
7312 RB_INSERT(download_list
, &downloads
, download_entry
);
7313 /* get from history */
7314 g_object_ref(wk_download
);
7315 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7316 show_oops(t
, "Download of '%s' started...",
7317 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7326 /* sync other download manager tabs */
7327 update_download_tabs(NULL
);
7330 * NOTE: never redirect/render the current tab before this
7331 * function returns. This will cause the download to never start.
7333 return (ret
); /* start download */
7337 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7339 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7342 show_oops(NULL
, "webview_hover_cb");
7347 set_status(t
, uri
, XT_STATUS_LINK
);
7350 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7355 mark(struct tab
*t
, struct karg
*arg
)
7361 if ((index
= marktoindex(mark
)) == -1)
7364 if (arg
->i
== XT_MARK_SET
)
7365 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7366 else if (arg
->i
== XT_MARK_GOTO
) {
7367 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7368 show_oops(t
, "mark '%c' does not exist", mark
);
7371 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7372 something changes the document size */
7373 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7380 marks_clear(struct tab
*t
)
7384 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7385 t
->mark
[i
] = XT_INVALID_MARK
;
7391 char file
[PATH_MAX
];
7392 char *line
= NULL
, *p
;
7397 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7398 if ((f
= fopen(file
, "r+")) == NULL
) {
7399 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7403 for (i
= 1; ; i
++) {
7404 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7406 if (strlen(line
) == 0 || line
[0] == '#') {
7412 p
= strtok(line
, " \t");
7414 if (p
== NULL
|| strlen(p
) != 1 ||
7415 (index
= marktoindex(*p
)) == -1) {
7416 warnx("corrupt quickmarks file, line %d", i
);
7420 p
= strtok(NULL
, " \t");
7421 if (qmarks
[index
] != NULL
)
7422 g_free(qmarks
[index
]);
7423 qmarks
[index
] = g_strdup(p
);
7434 char file
[PATH_MAX
];
7438 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7439 if ((f
= fopen(file
, "r+")) == NULL
) {
7440 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7444 for (i
= 0; i
< XT_NOMARKS
; i
++)
7445 if (qmarks
[i
] != NULL
)
7446 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7454 qmark(struct tab
*t
, struct karg
*arg
)
7459 mark
= arg
->s
[strlen(arg
->s
)-1];
7460 index
= marktoindex(mark
);
7466 if (qmarks
[index
] != NULL
)
7467 g_free(qmarks
[index
]);
7469 qmarks_load(); /* sync if multiple instances */
7470 qmarks
[index
] = g_strdup(get_uri(t
));
7474 if (qmarks
[index
] != NULL
)
7475 load_uri(t
, qmarks
[index
]);
7477 show_oops(t
, "quickmark \"%c\" does not exist",
7483 if (qmarks
[index
] != NULL
)
7484 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7486 show_oops(t
, "quickmark \"%c\" does not exist",
7497 go_up(struct tab
*t
, struct karg
*args
)
7503 levels
= atoi(args
->s
);
7507 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7508 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7510 tmp
+= strlen(XT_PROTO_DELIM
);
7512 /* if an uri starts with a slash, leave it alone (for file:///) */
7519 p
= strrchr(tmp
, '/');
7533 gototab(struct tab
*t
, struct karg
*args
)
7536 struct karg arg
= {0, NULL
, -1};
7538 tab
= atoi(args
->s
);
7540 arg
.i
= XT_TAB_NEXT
;
7549 zoom_amount(struct tab
*t
, struct karg
*arg
)
7551 struct karg narg
= {0, NULL
, -1};
7553 narg
.i
= atoi(arg
->s
);
7554 resizetab(t
, &narg
);
7560 flip_colon(struct tab
*t
, struct karg
*arg
)
7562 struct karg narg
= {0, NULL
, -1};
7565 if (t
== NULL
|| arg
== NULL
)
7568 p
= strstr(arg
->s
, ":");
7580 /* buffer commands receive the regex that triggered them in arg.s */
7581 char bcmd
[XT_BUFCMD_SZ
];
7585 #define XT_PRE_NO (0)
7586 #define XT_PRE_YES (1)
7587 #define XT_PRE_MAYBE (2)
7589 int (*func
)(struct tab
*, struct karg
*);
7593 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7594 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7595 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7596 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7597 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7598 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7599 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7600 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7601 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7602 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7603 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7604 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7605 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7606 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7607 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7608 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7609 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7610 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7614 buffercmd_init(void)
7618 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7619 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7620 REG_EXTENDED
| REG_NOSUB
))
7621 startpage_add("invalid buffercmd regex %s",
7622 buffercmds
[i
].regex
);
7626 buffercmd_abort(struct tab
*t
)
7630 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7631 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7634 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7635 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7639 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7641 struct karg arg
= {0, NULL
, -1};
7644 arg
.s
= g_strdup(bcmd
);
7646 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7647 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7657 buffercmd_addkey(struct tab
*t
, guint keyval
)
7660 char s
[XT_BUFCMD_SZ
];
7662 if (keyval
== GDK_Escape
) {
7664 return (XT_CB_HANDLED
);
7667 /* key with modifier or non-ascii character */
7668 if (!isascii(keyval
))
7669 return (XT_CB_PASSTHROUGH
);
7671 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7672 "to buffer \"%s\"\n", keyval
, bcmd
);
7674 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7675 if (bcmd
[i
] == '\0') {
7680 /* buffer full, ignore input */
7681 if (i
>= LENGTH(bcmd
) -1) {
7682 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7684 return (XT_CB_HANDLED
);
7687 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7689 /* find exact match */
7690 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7691 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7692 (size_t) 0, NULL
, 0) == 0) {
7693 buffercmd_execute(t
, &buffercmds
[i
]);
7697 /* find non exact matches to see if we need to abort ot not */
7698 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7699 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7702 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7703 if (isdigit(bcmd
[0])) {
7704 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7708 if (sscanf(bcmd
, "%s", s
) == 0)
7711 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7712 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7715 if (sscanf(bcmd
, "%s", s
) == 0)
7718 if (c
== -1 && buffercmds
[i
].precount
)
7720 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7723 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7724 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7727 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7732 return (XT_CB_HANDLED
);
7736 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7738 struct key_binding
*k
;
7740 /* handle keybindings if buffercmd is empty.
7741 if not empty, allow commands like C-n */
7742 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7743 TAILQ_FOREACH(k
, &kbl
, entry
)
7744 if (e
->keyval
== k
->key
7745 && (entry
? k
->use_in_entry
: 1)) {
7747 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7748 return (cmd_execute(t
, k
->cmd
));
7749 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7750 return (cmd_execute(t
, k
->cmd
));
7754 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7755 return buffercmd_addkey(t
, e
->keyval
);
7757 return (XT_CB_PASSTHROUGH
);
7761 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7763 char s
[2], buf
[128];
7764 const char *errstr
= NULL
;
7766 /* don't use w directly; use t->whatever instead */
7769 show_oops(NULL
, "wv_keypress_after_cb");
7770 return (XT_CB_PASSTHROUGH
);
7773 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7774 e
->keyval
, e
->state
, t
);
7778 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7780 return (XT_CB_HANDLED
);
7784 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7786 /* we have a string */
7788 /* we have a number */
7789 snprintf(buf
, sizeof buf
,
7790 "vimprobable_fire(%s)", t
->hint_num
);
7797 /* XXX unfuck this */
7798 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7799 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7800 /* last input was numerical */
7802 l
= strlen(t
->hint_num
);
7809 t
->hint_num
[l
] = '\0';
7813 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7814 /* last input was alphanumerical */
7816 l
= strlen(t
->hint_buf
);
7823 t
->hint_buf
[l
] = '\0';
7833 /* numerical input */
7834 if (CLEAN(e
->state
) == 0 &&
7835 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7836 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7837 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7838 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7839 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7843 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7844 "invalid link number\n");
7847 snprintf(buf
, sizeof buf
,
7848 "vimprobable_update_hints(%s)",
7850 t
->hint_mode
= XT_HINT_NUMERICAL
;
7854 /* empty the counter buffer */
7855 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7856 return (XT_CB_HANDLED
);
7859 /* alphanumerical input */
7860 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7861 e
->keyval
<= GDK_z
) ||
7862 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7863 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7864 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7865 e
->keyval
<= GDK_9
) ||
7866 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7867 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7868 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7869 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7870 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7871 " %s\n", t
->hint_buf
);
7873 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7876 snprintf(buf
, sizeof buf
,
7877 "vimprobable_show_hints('%s')", t
->hint_buf
);
7878 t
->hint_mode
= XT_HINT_ALPHANUM
;
7881 /* empty the counter buffer */
7882 bzero(t
->hint_num
, sizeof t
->hint_num
);
7883 return (XT_CB_HANDLED
);
7886 return (XT_CB_HANDLED
);
7889 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7890 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7891 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7894 return (handle_keypress(t
, e
, 0));
7898 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7902 /* Hide buffers, if they are visible, with escape. */
7903 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7904 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7905 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7907 return (XT_CB_HANDLED
);
7910 return (XT_CB_PASSTHROUGH
);
7914 search_continue(struct tab
*t
)
7916 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7917 gboolean rv
= FALSE
;
7921 if (strlen(c
) == 1) {
7922 webkit_web_view_unmark_text_matches(t
->wv
);
7927 t
->search_forward
= TRUE
;
7928 else if (c
[0] == '?')
7929 t
->search_forward
= FALSE
;
7939 search_cb(struct tab
*t
)
7941 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7944 if (search_continue(t
) == FALSE
)
7948 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7950 /* not found, mark red */
7951 gdk_color_parse(XT_COLOR_RED
, &color
);
7952 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7953 /* unmark and remove selection */
7954 webkit_web_view_unmark_text_matches(t
->wv
);
7955 /* my kingdom for a way to unselect text in webview */
7957 /* found, highlight all */
7958 webkit_web_view_unmark_text_matches(t
->wv
);
7959 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7960 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7961 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7962 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7970 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7972 const gchar
*c
= gtk_entry_get_text(w
);
7975 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7976 return (XT_CB_PASSTHROUGH
);
7979 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7980 e
->keyval
, e
->state
, t
);
7982 if (search_continue(t
) == FALSE
)
7985 /* if search length is > 4 then no longer play timeout games */
7986 if (strlen(c
) > 4) {
7988 g_source_remove(t
->search_id
);
7995 /* reestablish a new timer if the user types fast */
7997 g_source_remove(t
->search_id
);
7998 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
8001 return (XT_CB_PASSTHROUGH
);
8005 match_uri(const gchar
*uri
, const gchar
*key
) {
8008 gboolean match
= FALSE
;
8012 if (!strncmp(key
, uri
, len
))
8015 voffset
= strstr(uri
, "/") + 2;
8016 if (!strncmp(key
, voffset
, len
))
8018 else if (g_str_has_prefix(voffset
, "www.")) {
8019 voffset
= voffset
+ strlen("www.");
8020 if (!strncmp(key
, voffset
, len
))
8029 match_session(const gchar
*name
, const gchar
*key
) {
8032 sub
= strcasestr(name
, key
);
8038 cmd_getlist(int id
, char *key
)
8045 if (cmds
[id
].type
& XT_URLARG
) {
8046 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
8047 if (match_uri(h
->uri
, key
)) {
8048 cmd_status
.list
[c
] = (char *)h
->uri
;
8054 } else if (cmds
[id
].type
& XT_SESSARG
) {
8055 TAILQ_FOREACH(s
, &sessions
, entry
)
8056 if (match_session(s
->name
, key
)) {
8057 cmd_status
.list
[c
] = (char *)s
->name
;
8063 } else if (cmds
[id
].type
& XT_SETARG
) {
8064 for (i
= 0; i
< LENGTH(rs
); i
++)
8065 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
8066 cmd_status
.list
[c
++] = rs
[i
].name
;
8072 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
8074 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
8075 if (cmds
[i
].level
< dep
)
8077 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
8078 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
8079 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
8087 cmd_getnext(int dir
)
8089 cmd_status
.index
+= dir
;
8091 if (cmd_status
.index
< 0)
8092 cmd_status
.index
= cmd_status
.len
- 1;
8093 else if (cmd_status
.index
>= cmd_status
.len
)
8094 cmd_status
.index
= 0;
8096 return cmd_status
.list
[cmd_status
.index
];
8100 cmd_tokenize(char *s
, char *tokens
[])
8103 char *tok
, *last
= NULL
;
8104 size_t len
= strlen(s
);
8107 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8108 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8109 tok
= strtok_r(NULL
, " ", &last
), i
++)
8119 cmd_complete(struct tab
*t
, char *str
, int dir
)
8121 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8122 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8124 char *tok
, *match
, *s
= g_strdup(str
);
8126 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8129 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8132 for (i
= 0; isdigit(s
[i
]); i
++)
8135 for (; isspace(s
[i
]); i
++)
8140 levels
= cmd_tokenize(s
, tokens
);
8142 for (i
= 0; i
< levels
- 1; i
++) {
8145 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8146 if (cmds
[j
].level
< dep
)
8148 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8152 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8159 if (matchcount
== 1) {
8160 strlcat(res
, tok
, sizeof res
);
8161 strlcat(res
, " ", sizeof res
);
8171 if (cmd_status
.index
== -1)
8172 cmd_getlist(parent
, tokens
[i
]);
8174 if (cmd_status
.len
> 0) {
8175 match
= cmd_getnext(dir
);
8176 strlcat(res
, match
, sizeof res
);
8177 gtk_entry_set_text(w
, res
);
8178 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8185 cmd_execute(struct tab
*t
, char *str
)
8187 struct cmd
*cmd
= NULL
;
8188 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
8190 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8191 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8192 struct karg arg
= {0, NULL
, -1};
8197 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8203 while (isspace(s
[0]))
8206 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8207 prefix
= atoi(prefixstr
);
8211 for (tok
= strtok_r(s
, " ", &last
); tok
;
8212 tok
= strtok_r(NULL
, " ", &last
)) {
8214 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8215 if (cmds
[j
].level
< dep
)
8217 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8219 if (cmds
[j
].level
== dep
&&
8220 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8224 if (len
== strlen(cmds
[j
].cmd
)) {
8230 if (matchcount
== 1) {
8235 show_oops(t
, "Invalid command: %s", str
);
8241 show_oops(t
, "Empty command");
8247 arg
.precount
= prefix
;
8248 else if (cmd_prefix
> 0)
8249 arg
.precount
= cmd_prefix
;
8251 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8252 show_oops(t
, "No prefix allowed: %s", str
);
8256 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8257 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8258 if (arg
.s
== NULL
) {
8259 show_oops(t
, "Invalid command");
8262 arg
.precount
= atoi(arg
.s
);
8263 if (arg
.precount
<= 0) {
8264 if (arg
.s
[0] == '0')
8265 show_oops(t
, "Zero count");
8267 show_oops(t
, "Trailing characters");
8272 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8273 __func__
, arg
.precount
, arg
.s
);
8289 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8292 show_oops(NULL
, "entry_key_cb invalid parameters");
8293 return (XT_CB_PASSTHROUGH
);
8296 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8297 e
->keyval
, e
->state
, t
);
8301 if (e
->keyval
== GDK_Escape
) {
8302 /* don't use focus_webview(t) because we want to type :cmds */
8303 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8306 return (handle_keypress(t
, e
, 1));
8309 struct command_entry
*
8310 history_prev(struct command_list
*l
, struct command_entry
*at
)
8313 at
= TAILQ_LAST(l
, command_list
);
8315 at
= TAILQ_PREV(at
, command_list
, entry
);
8317 at
= TAILQ_LAST(l
, command_list
);
8323 struct command_entry
*
8324 history_next(struct command_list
*l
, struct command_entry
*at
)
8327 at
= TAILQ_FIRST(l
);
8329 at
= TAILQ_NEXT(at
, entry
);
8331 at
= TAILQ_FIRST(l
);
8338 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8340 int rv
= XT_CB_HANDLED
;
8341 const gchar
*c
= gtk_entry_get_text(w
);
8344 show_oops(NULL
, "cmd_keypress_cb parameters");
8345 return (XT_CB_PASSTHROUGH
);
8348 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8349 e
->keyval
, e
->state
, t
);
8353 e
->keyval
= GDK_Escape
;
8354 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8355 e
->keyval
= GDK_Escape
;
8357 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8358 e
->keyval
!= GDK_ISO_Left_Tab
)
8359 cmd_status
.index
= -1;
8361 switch (e
->keyval
) {
8364 cmd_complete(t
, (char *)&c
[1], 1);
8366 case GDK_ISO_Left_Tab
:
8368 cmd_complete(t
, (char *)&c
[1], -1);
8373 if ((search_at
= history_next(&shl
, search_at
))) {
8374 search_at
->line
[0] = c
[0];
8375 gtk_entry_set_text(w
, search_at
->line
);
8376 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8379 if ((history_at
= history_prev(&chl
, history_at
))) {
8380 history_at
->line
[0] = c
[0];
8381 gtk_entry_set_text(w
, history_at
->line
);
8382 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8389 if ((search_at
= history_next(&shl
, search_at
))) {
8390 search_at
->line
[0] = c
[0];
8391 gtk_entry_set_text(w
, search_at
->line
);
8392 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8395 if ((history_at
= history_next(&chl
, history_at
))) {
8396 history_at
->line
[0] = c
[0];
8397 gtk_entry_set_text(w
, history_at
->line
);
8398 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8404 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8412 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8413 webkit_web_view_unmark_text_matches(t
->wv
);
8417 rv
= XT_CB_PASSTHROUGH
;
8423 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8425 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8429 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8431 /* popup menu enabled */
8436 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8439 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8440 return (XT_CB_PASSTHROUGH
);
8443 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8444 t
->tab_id
, t
->popup
);
8446 /* if popup is enabled don't lose focus */
8449 return (XT_CB_PASSTHROUGH
);
8455 if (show_url
== 0 || t
->focus_wv
)
8458 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8460 return (XT_CB_PASSTHROUGH
);
8464 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8467 const gchar
*c
= gtk_entry_get_text(entry
);
8470 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8474 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8481 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8487 if (c
[0] == '/' || c
[0] == '?') {
8488 /* see if there is a timer pending */
8490 g_source_remove(t
->search_id
);
8495 if (t
->search_text
) {
8496 g_free(t
->search_text
);
8497 t
->search_text
= NULL
;
8500 t
->search_text
= g_strdup(s
);
8502 g_free(global_search
);
8503 global_search
= g_strdup(s
);
8504 t
->search_forward
= c
[0] == '/';
8506 history_add(&shl
, search_file
, s
, &search_history_count
);
8510 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8517 backward_cb(GtkWidget
*w
, struct tab
*t
)
8522 show_oops(NULL
, "backward_cb invalid parameters");
8526 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8533 forward_cb(GtkWidget
*w
, struct tab
*t
)
8538 show_oops(NULL
, "forward_cb invalid parameters");
8542 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8544 a
.i
= XT_NAV_FORWARD
;
8549 home_cb(GtkWidget
*w
, struct tab
*t
)
8552 show_oops(NULL
, "home_cb invalid parameters");
8556 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8562 stop_cb(GtkWidget
*w
, struct tab
*t
)
8564 WebKitWebFrame
*frame
;
8567 show_oops(NULL
, "stop_cb invalid parameters");
8571 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8573 frame
= webkit_web_view_get_main_frame(t
->wv
);
8574 if (frame
== NULL
) {
8575 show_oops(t
, "stop_cb: no frame");
8579 webkit_web_frame_stop_loading(frame
);
8580 abort_favicon_download(t
);
8584 setup_webkit(struct tab
*t
)
8586 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8587 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8588 FALSE
, (char *)NULL
);
8590 warnx("webkit does not have \"enable-dns-prefetching\" property");
8591 g_object_set(G_OBJECT(t
->settings
),
8592 "user-agent", t
->user_agent
, (char *)NULL
);
8593 g_object_set(G_OBJECT(t
->settings
),
8594 "enable-scripts", enable_scripts
, (char *)NULL
);
8595 g_object_set(G_OBJECT(t
->settings
),
8596 "enable-plugins", enable_plugins
, (char *)NULL
);
8597 g_object_set(G_OBJECT(t
->settings
),
8598 "javascript-can-open-windows-automatically", enable_scripts
,
8600 g_object_set(G_OBJECT(t
->settings
),
8601 "enable-html5-database", FALSE
, (char *)NULL
);
8602 g_object_set(G_OBJECT(t
->settings
),
8603 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8604 g_object_set(G_OBJECT(t
->settings
),
8605 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8606 g_object_set(G_OBJECT(t
->settings
),
8607 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8608 g_object_set(G_OBJECT(t
->wv
),
8609 "full-content-zoom", TRUE
, (char *)NULL
);
8611 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8615 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8617 struct tab
*ti
, *t
= NULL
;
8618 gdouble view_size
, value
, max
;
8621 TAILQ_FOREACH(ti
, &tabs
, entry
)
8622 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8630 if (adjustment
== NULL
)
8631 adjustment
= gtk_scrolled_window_get_vadjustment(
8632 GTK_SCROLLED_WINDOW(t
->browser_win
));
8634 view_size
= gtk_adjustment_get_page_size(adjustment
);
8635 value
= gtk_adjustment_get_value(adjustment
);
8636 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8639 position
= g_strdup("All");
8640 else if (value
== max
)
8641 position
= g_strdup("Bot");
8642 else if (value
== 0)
8643 position
= g_strdup("Top");
8645 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8647 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8654 create_browser(struct tab
*t
)
8658 GtkAdjustment
*adjustment
;
8661 show_oops(NULL
, "create_browser invalid parameters");
8665 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8666 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8667 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8668 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8670 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8671 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8672 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8674 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8675 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8678 t
->settings
= webkit_web_settings_new();
8680 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
8682 if (user_agent
== NULL
) {
8683 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8685 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8688 t
->user_agent
= g_strdup(user_agent
);
8690 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8693 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8694 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8695 G_CALLBACK(update_statusbar_position
), NULL
);
8707 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8708 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8709 gtk_widget_set_name(w
, "xxxterm");
8710 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8711 g_signal_connect(G_OBJECT(w
), "delete_event",
8712 G_CALLBACK (gtk_main_quit
), NULL
);
8718 create_kiosk_toolbar(struct tab
*t
)
8720 GtkWidget
*toolbar
= NULL
, *b
;
8722 b
= gtk_hbox_new(FALSE
, 0);
8724 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8726 /* backward button */
8727 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8728 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8729 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8730 G_CALLBACK(backward_cb
), t
);
8731 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8733 /* forward button */
8734 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8735 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8736 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8737 G_CALLBACK(forward_cb
), t
);
8738 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8741 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8742 gtk_widget_set_sensitive(t
->gohome
, true);
8743 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8744 G_CALLBACK(home_cb
), t
);
8745 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8747 /* create widgets but don't use them */
8748 t
->uri_entry
= gtk_entry_new();
8749 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8750 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8751 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8757 create_toolbar(struct tab
*t
)
8759 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8761 b
= gtk_hbox_new(FALSE
, 0);
8763 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8765 /* backward button */
8766 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8767 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8768 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8769 G_CALLBACK(backward_cb
), t
);
8770 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8772 /* forward button */
8773 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8774 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8775 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8776 G_CALLBACK(forward_cb
), t
);
8777 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8781 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8782 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8783 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8784 G_CALLBACK(stop_cb
), t
);
8785 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8789 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8790 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8791 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8792 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8793 G_CALLBACK(js_toggle_cb
), t
);
8794 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8796 t
->uri_entry
= gtk_entry_new();
8797 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8798 G_CALLBACK(activate_uri_entry_cb
), t
);
8799 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8800 G_CALLBACK(entry_key_cb
), t
);
8802 eb1
= gtk_hbox_new(FALSE
, 0);
8803 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8804 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8805 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8808 if (search_string
) {
8810 t
->search_entry
= gtk_entry_new();
8811 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8812 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8813 G_CALLBACK(activate_search_entry_cb
), t
);
8814 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8815 G_CALLBACK(entry_key_cb
), t
);
8816 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8817 eb2
= gtk_hbox_new(FALSE
, 0);
8818 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8819 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8821 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8828 create_buffers(struct tab
*t
)
8830 GtkCellRenderer
*renderer
;
8833 view
= gtk_tree_view_new();
8835 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8837 renderer
= gtk_cell_renderer_text_new();
8838 gtk_tree_view_insert_column_with_attributes
8839 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8841 renderer
= gtk_cell_renderer_text_new();
8842 gtk_tree_view_insert_column_with_attributes
8843 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8846 gtk_tree_view_set_model
8847 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8853 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8854 GtkTreeViewColumn
*col
, struct tab
*t
)
8859 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8861 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8864 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8865 set_current_tab(id
- 1);
8871 /* after tab reordering/creation/removal */
8878 TAILQ_FOREACH(t
, &tabs
, entry
) {
8879 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8880 if (t
->tab_id
> maxid
)
8883 gtk_widget_show(t
->tab_elems
.sep
);
8886 TAILQ_FOREACH(t
, &tabs
, entry
) {
8887 if (t
->tab_id
== maxid
) {
8888 gtk_widget_hide(t
->tab_elems
.sep
);
8894 /* after active tab change */
8896 recolor_compact_tabs(void)
8902 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8903 TAILQ_FOREACH(t
, &tabs
, entry
)
8904 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8907 curid
= gtk_notebook_get_current_page(notebook
);
8908 TAILQ_FOREACH(t
, &tabs
, entry
)
8909 if (t
->tab_id
== curid
) {
8910 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8911 gtk_widget_modify_fg(t
->tab_elems
.label
,
8912 GTK_STATE_NORMAL
, &color
);
8918 set_current_tab(int page_num
)
8920 buffercmd_abort(get_current_tab());
8921 gtk_notebook_set_current_page(notebook
, page_num
);
8922 recolor_compact_tabs();
8926 undo_close_tab_save(struct tab
*t
)
8930 struct undo
*u1
, *u2
;
8932 WebKitWebHistoryItem
*item
;
8934 if ((uri
= get_uri(t
)) == NULL
)
8937 u1
= g_malloc0(sizeof(struct undo
));
8938 u1
->uri
= g_strdup(uri
);
8940 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8942 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8943 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8946 /* forward history */
8947 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8951 u1
->history
= g_list_prepend(u1
->history
,
8952 webkit_web_history_item_copy(item
));
8953 items
= g_list_next(items
);
8958 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8959 u1
->history
= g_list_prepend(u1
->history
,
8960 webkit_web_history_item_copy(item
));
8964 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8968 u1
->history
= g_list_prepend(u1
->history
,
8969 webkit_web_history_item_copy(item
));
8970 items
= g_list_next(items
);
8973 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8975 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8976 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8977 TAILQ_REMOVE(&undos
, u2
, entry
);
8979 g_list_free(u2
->history
);
8988 delete_tab(struct tab
*t
)
8992 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8998 * no need to join thread here because it won't access t on completion
9002 TAILQ_REMOVE(&tabs
, t
, entry
);
9004 /* Halt all webkit activity. */
9005 abort_favicon_download(t
);
9006 webkit_web_view_stop_loading(t
->wv
);
9008 /* Save the tab, so we can undo the close. */
9009 undo_close_tab_save(t
);
9011 if (browser_mode
== XT_BM_KIOSK
) {
9012 gtk_widget_destroy(t
->uri_entry
);
9013 gtk_widget_destroy(t
->stop
);
9014 gtk_widget_destroy(t
->js_toggle
);
9017 gtk_widget_destroy(t
->tab_elems
.eventbox
);
9018 gtk_widget_destroy(t
->vbox
);
9022 g_source_remove(t
->search_id
);
9024 g_free(t
->user_agent
);
9025 g_free(t
->stylesheet
);
9029 if (TAILQ_EMPTY(&tabs
)) {
9030 if (browser_mode
== XT_BM_KIOSK
)
9031 create_new_tab(home
, NULL
, 1, -1);
9033 create_new_tab(NULL
, NULL
, 1, -1);
9036 /* recreate session */
9037 if (session_autosave
) {
9043 recolor_compact_tabs();
9047 update_statusbar_zoom(struct tab
*t
)
9050 char s
[16] = { '\0' };
9052 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9053 if ((zoom
<= 0.99 || zoom
>= 1.01))
9054 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
9055 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
9059 setzoom_webkit(struct tab
*t
, int adjust
)
9061 #define XT_ZOOMPERCENT 0.04
9066 show_oops(NULL
, "setzoom_webkit invalid parameters");
9070 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9071 if (adjust
== XT_ZOOM_IN
)
9072 zoom
+= XT_ZOOMPERCENT
;
9073 else if (adjust
== XT_ZOOM_OUT
)
9074 zoom
-= XT_ZOOMPERCENT
;
9075 else if (adjust
> 0)
9076 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
9078 show_oops(t
, "setzoom_webkit invalid zoom value");
9082 if (zoom
< XT_ZOOMPERCENT
)
9083 zoom
= XT_ZOOMPERCENT
;
9084 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
9085 update_statusbar_zoom(t
);
9089 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
9091 struct tab
*t
= (struct tab
*) data
;
9093 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
9095 switch (event
->button
) {
9097 set_current_tab(t
->tab_id
);
9108 append_tab(struct tab
*t
)
9113 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9114 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9118 create_sbe(int width
)
9122 sbe
= gtk_entry_new();
9123 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9124 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9125 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9126 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9127 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9128 gtk_widget_set_size_request(sbe
, width
, -1);
9134 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9139 WebKitWebHistoryItem
*item
;
9143 int sbe_p
= 0, sbe_b
= 0,
9146 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9148 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9149 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9153 t
= g_malloc0(sizeof *t
);
9155 if (title
== NULL
) {
9156 title
= "(untitled)";
9160 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9162 /* label + button for tab */
9163 b
= gtk_hbox_new(FALSE
, 0);
9166 #if GTK_CHECK_VERSION(2, 20, 0)
9167 t
->spinner
= gtk_spinner_new();
9169 t
->label
= gtk_label_new(title
);
9170 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9171 gtk_widget_set_size_request(t
->label
, 100, 0);
9172 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9173 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9174 gtk_widget_set_size_request(b
, 130, 0);
9176 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9177 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9178 #if GTK_CHECK_VERSION(2, 20, 0)
9179 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9183 if (browser_mode
== XT_BM_KIOSK
) {
9184 t
->toolbar
= create_kiosk_toolbar(t
);
9185 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9188 t
->toolbar
= create_toolbar(t
);
9190 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9198 t
->browser_win
= create_browser(t
);
9199 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9201 /* oops message for user feedback */
9202 t
->oops
= gtk_entry_new();
9203 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9204 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9205 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9206 gdk_color_parse(XT_COLOR_RED
, &color
);
9207 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9208 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9209 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9212 t
->cmd
= gtk_entry_new();
9213 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9214 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9215 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9216 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9219 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9221 t
->sbe
.statusbar
= gtk_entry_new();
9222 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9223 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9224 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9225 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9227 /* create these widgets only if specified in statusbar_elems */
9229 t
->sbe
.position
= create_sbe(40);
9230 t
->sbe
.zoom
= create_sbe(40);
9231 t
->sbe
.buffercmd
= create_sbe(60);
9233 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9235 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9238 /* gtk widgets cannot be added to a box twice. sbe_* variables
9239 make sure of this */
9240 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9244 GtkWidget
*sep
= gtk_vseparator_new();
9246 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9247 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9248 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9249 FALSE
, FALSE
, FALSE
);
9254 warnx("flag \"%c\" specified more than "
9255 "once in statusbar_elems\n", *p
);
9259 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9260 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9264 warnx("flag \"%c\" specified more than "
9265 "once in statusbar_elems\n", *p
);
9269 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9270 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9274 warnx("flag \"%c\" specified more than "
9275 "once in statusbar_elems\n", *p
);
9279 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9280 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9283 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9288 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9291 t
->buffers
= create_buffers(t
);
9292 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9294 /* xtp meaning is normal by default */
9295 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9297 /* set empty favicon */
9298 xt_icon_from_name(t
, "text-html");
9300 /* and show it all */
9301 gtk_widget_show_all(b
);
9302 gtk_widget_show_all(t
->vbox
);
9304 /* compact tab bar */
9305 t
->tab_elems
.label
= gtk_label_new(title
);
9306 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9307 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9308 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9309 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9311 t
->tab_elems
.eventbox
= gtk_event_box_new();
9312 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9313 t
->tab_elems
.sep
= gtk_vseparator_new();
9315 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9316 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9317 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9318 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9319 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9320 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9322 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9324 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9326 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9329 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9331 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9333 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9336 id
= position
>= 0 ? position
:
9337 gtk_notebook_get_current_page(notebook
) + 1;
9338 if (id
> gtk_notebook_get_n_pages(notebook
))
9341 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9342 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9343 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9344 t
->tab_elems
.eventbox
, id
);
9349 #if GTK_CHECK_VERSION(2, 20, 0)
9350 /* turn spinner off if we are a new tab without uri */
9352 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9353 gtk_widget_hide(t
->spinner
);
9356 /* make notebook tabs reorderable */
9357 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9359 /* compact tabs clickable */
9360 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9361 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9363 g_object_connect(G_OBJECT(t
->cmd
),
9364 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9365 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9366 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9367 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9368 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9371 /* reuse wv_button_cb to hide oops */
9372 g_object_connect(G_OBJECT(t
->oops
),
9373 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9376 g_signal_connect(t
->buffers
,
9377 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9378 g_object_connect(G_OBJECT(t
->buffers
),
9379 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9381 g_object_connect(G_OBJECT(t
->wv
),
9382 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9383 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9384 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9385 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9386 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9387 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9388 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9389 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9390 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9391 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9392 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9393 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9394 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9395 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9396 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9397 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9399 g_signal_connect(t
->wv
,
9400 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9401 g_signal_connect(t
->wv
,
9402 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9404 /* hijack the unused keys as if we were the browser */
9405 g_object_connect(G_OBJECT(t
->toolbar
),
9406 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9409 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9410 G_CALLBACK(tab_close_cb
), t
);
9413 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9414 /* restore the tab's history */
9415 if (u
&& u
->history
) {
9419 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9420 items
= g_list_next(items
);
9423 item
= g_list_nth_data(u
->history
, u
->back
);
9425 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9428 g_list_free(u
->history
);
9430 webkit_web_back_forward_list_clear(t
->bfl
);
9436 url_set_visibility();
9437 statusbar_set_visibility();
9440 set_current_tab(t
->tab_id
);
9441 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9445 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9449 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9456 recolor_compact_tabs();
9457 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9462 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9468 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9470 if (gtk_notebook_get_current_page(notebook
) == -1)
9473 TAILQ_FOREACH(t
, &tabs
, entry
) {
9474 if (t
->tab_id
== pn
) {
9475 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9478 uri
= get_title(t
, TRUE
);
9479 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9485 /* can't use focus_webview here */
9486 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9493 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9496 struct tab
*t
= NULL
, *tt
;
9500 TAILQ_FOREACH(tt
, &tabs
, entry
)
9501 if (tt
->tab_id
== pn
) {
9507 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9509 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9514 menuitem_response(struct tab
*t
)
9516 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9520 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9522 GtkWidget
*menu
, *menu_items
;
9523 GdkEventButton
*bevent
;
9527 if (event
->type
== GDK_BUTTON_PRESS
) {
9528 bevent
= (GdkEventButton
*) event
;
9529 menu
= gtk_menu_new();
9531 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9532 if ((uri
= get_uri(ti
)) == NULL
)
9533 /* XXX make sure there is something to print */
9534 /* XXX add gui pages in here to look purdy */
9536 menu_items
= gtk_menu_item_new_with_label(uri
);
9537 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9538 gtk_widget_show(menu_items
);
9540 g_signal_connect_swapped((menu_items
),
9541 "activate", G_CALLBACK(menuitem_response
),
9545 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9546 bevent
->button
, bevent
->time
);
9548 /* unref object so it'll free itself when popped down */
9549 #if !GTK_CHECK_VERSION(3, 0, 0)
9550 /* XXX does not need unref with gtk+3? */
9551 g_object_ref_sink(menu
);
9552 g_object_unref(menu
);
9555 return (TRUE
/* eat event */);
9558 return (FALSE
/* propagate */);
9562 icon_size_map(int icon_size
)
9564 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9565 icon_size
> GTK_ICON_SIZE_DIALOG
)
9566 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9572 create_button(char *name
, char *stockid
, int size
)
9574 GtkWidget
*button
, *image
;
9578 rcstring
= g_strdup_printf(
9579 "style \"%s-style\"\n"
9581 " GtkWidget::focus-padding = 0\n"
9582 " GtkWidget::focus-line-width = 0\n"
9586 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9587 gtk_rc_parse_string(rcstring
);
9589 button
= gtk_button_new();
9590 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9591 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9593 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9594 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9595 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9596 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9597 gtk_widget_set_name(button
, name
);
9598 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9604 button_set_stockid(GtkWidget
*button
, char *stockid
)
9608 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9609 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9610 gtk_button_set_image(GTK_BUTTON(button
), image
);
9614 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9617 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9620 if (xterm_workaround
== 0)
9624 * xterm doesn't play nice with clipboards because it clears the
9625 * primary when clicked. We rely on primary being set to properly
9626 * handle middle mouse button clicks (paste). So when someone clears
9627 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9628 * other application behavior (as in DON'T clear primary).
9631 p
= gtk_clipboard_wait_for_text(primary
);
9633 if (gdk_property_get(gdk_get_default_root_window(),
9635 gdk_atom_intern("STRING", FALSE
),
9637 1024 * 1024 /* picked out of my butt */,
9643 /* yes sir, we need to NUL the string */
9645 gtk_clipboard_set_text(primary
, p
, -1);
9659 char file
[PATH_MAX
];
9662 vbox
= gtk_vbox_new(FALSE
, 0);
9663 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9664 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9665 #if !GTK_CHECK_VERSION(3, 0, 0)
9666 /* XXX seems to be needed with gtk+2 */
9667 gtk_notebook_set_tab_hborder(notebook
, 0);
9668 gtk_notebook_set_tab_vborder(notebook
, 0);
9670 gtk_notebook_set_scrollable(notebook
, TRUE
);
9671 gtk_notebook_set_show_border(notebook
, FALSE
);
9672 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9674 abtn
= gtk_button_new();
9675 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9676 gtk_widget_set_size_request(arrow
, -1, -1);
9677 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9678 gtk_widget_set_size_request(abtn
, -1, 20);
9680 #if GTK_CHECK_VERSION(2, 20, 0)
9681 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9683 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9685 /* compact tab bar */
9686 tab_bar
= gtk_hbox_new(TRUE
, 0);
9688 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9689 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9690 gtk_widget_set_size_request(vbox
, -1, -1);
9692 g_object_connect(G_OBJECT(notebook
),
9693 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9695 g_object_connect(G_OBJECT(notebook
),
9696 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9697 NULL
, (char *)NULL
);
9698 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9699 G_CALLBACK(arrow_cb
), NULL
);
9701 main_window
= create_window();
9702 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9705 for (i
= 0; i
< LENGTH(icons
); i
++) {
9706 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9707 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9708 l
= g_list_append(l
, pb
);
9710 gtk_window_set_default_icon_list(l
);
9712 /* clipboard work around */
9713 if (xterm_workaround
)
9715 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9716 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9718 gtk_widget_show_all(abtn
);
9719 gtk_widget_show_all(main_window
);
9720 notebook_tab_set_visibility();
9724 set_hook(void **hook
, char *name
)
9727 errx(1, "set_hook");
9729 if (*hook
== NULL
) {
9730 *hook
= dlsym(RTLD_NEXT
, name
);
9732 errx(1, "can't hook %s", name
);
9736 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9738 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9740 g_return_val_if_fail(cookie1
, FALSE
);
9741 g_return_val_if_fail(cookie2
, FALSE
);
9743 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9744 !strcmp (cookie1
->value
, cookie2
->value
) &&
9745 !strcmp (cookie1
->path
, cookie2
->path
) &&
9746 !strcmp (cookie1
->domain
, cookie2
->domain
));
9750 transfer_cookies(void)
9753 SoupCookie
*sc
, *pc
;
9755 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9757 for (;cf
; cf
= cf
->next
) {
9759 sc
= soup_cookie_copy(pc
);
9760 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9763 soup_cookies_free(cf
);
9767 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9772 print_cookie("soup_cookie_jar_delete_cookie", c
);
9774 if (cookies_enabled
== 0)
9777 if (jar
== NULL
|| c
== NULL
)
9780 /* find and remove from persistent jar */
9781 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9783 for (;cf
; cf
= cf
->next
) {
9785 if (soup_cookie_equal(ci
, c
)) {
9786 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9791 soup_cookies_free(cf
);
9793 /* delete from session jar */
9794 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9798 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9800 struct domain
*d
= NULL
;
9804 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9805 jar
, p_cookiejar
, s_cookiejar
);
9807 if (cookies_enabled
== 0)
9810 /* see if we are up and running */
9811 if (p_cookiejar
== NULL
) {
9812 _soup_cookie_jar_add_cookie(jar
, cookie
);
9815 /* disallow p_cookiejar adds, shouldn't happen */
9816 if (jar
== p_cookiejar
)
9820 if (jar
== NULL
|| cookie
== NULL
)
9823 if (enable_cookie_whitelist
&&
9824 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9826 DNPRINTF(XT_D_COOKIE
,
9827 "soup_cookie_jar_add_cookie: reject %s\n",
9829 if (save_rejected_cookies
) {
9830 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9831 show_oops(NULL
, "can't open reject cookie file");
9834 fseek(r_cookie_f
, 0, SEEK_END
);
9835 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9836 cookie
->http_only
? "#HttpOnly_" : "",
9838 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9840 cookie
->secure
? "TRUE" : "FALSE",
9842 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9849 if (!allow_volatile_cookies
)
9853 if (cookie
->expires
== NULL
&& session_timeout
) {
9854 soup_cookie_set_expires(cookie
,
9855 soup_date_new_from_now(session_timeout
));
9856 print_cookie("modified add cookie", cookie
);
9859 /* see if we are white listed for persistence */
9860 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9861 /* add to persistent jar */
9862 c
= soup_cookie_copy(cookie
);
9863 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9864 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9867 /* add to session jar */
9868 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9869 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9875 char file
[PATH_MAX
];
9877 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9878 "soup_cookie_jar_add_cookie");
9879 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9880 "soup_cookie_jar_delete_cookie");
9882 if (cookies_enabled
== 0)
9886 * the following code is intricate due to overriding several libsoup
9888 * do not alter order of these operations.
9891 /* rejected cookies */
9892 if (save_rejected_cookies
)
9893 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9896 /* persistent cookies */
9897 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9898 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9900 /* session cookies */
9901 s_cookiejar
= soup_cookie_jar_new();
9902 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9903 cookie_policy
, (void *)NULL
);
9906 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9910 setup_proxy(char *uri
)
9913 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9914 soup_uri_free(proxy_uri
);
9918 if (http_proxy
!= uri
) {
9925 http_proxy
= g_strdup(uri
);
9926 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9927 proxy_uri
= soup_uri_new(http_proxy
);
9928 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
9929 g_object_set(session
, "proxy-uri", proxy_uri
,
9935 set_http_proxy(char *proxy
)
9942 /* see if we need to clear it instead */
9943 if (strlen(proxy
) == 0) {
9948 uri
= soup_uri_new(proxy
);
9949 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9960 send_cmd_to_socket(char *cmd
)
9963 struct sockaddr_un sa
;
9965 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9966 warnx("%s: socket", __func__
);
9970 sa
.sun_family
= AF_UNIX
;
9971 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9972 work_dir
, XT_SOCKET_FILE
);
9975 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9976 warnx("%s: connect", __func__
);
9980 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9981 warnx("%s: send", __func__
);
9992 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9995 char str
[XT_MAX_URL_LENGTH
];
9996 socklen_t t
= sizeof(struct sockaddr_un
);
9997 struct sockaddr_un sa
;
10002 gint fd
= g_io_channel_unix_get_fd(source
);
10004 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
10009 if (getpeereid(s
, &uid
, &gid
) == -1) {
10010 warn("getpeereid");
10013 if (uid
!= getuid() || gid
!= getgid()) {
10014 warnx("unauthorized user");
10020 warnx("not a valid user");
10024 n
= recv(s
, str
, sizeof(str
), 0);
10028 tt
= TAILQ_LAST(&tabs
, tab_list
);
10029 cmd_execute(tt
, str
);
10036 int s
, len
, rv
= 1;
10037 struct sockaddr_un sa
;
10039 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10040 warn("is_running: socket");
10044 sa
.sun_family
= AF_UNIX
;
10045 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10046 work_dir
, XT_SOCKET_FILE
);
10047 len
= SUN_LEN(&sa
);
10049 /* connect to see if there is a listener */
10050 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
10051 rv
= 0; /* not running */
10053 rv
= 1; /* already running */
10064 struct sockaddr_un sa
;
10066 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10067 warn("build_socket: socket");
10071 sa
.sun_family
= AF_UNIX
;
10072 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10073 work_dir
, XT_SOCKET_FILE
);
10074 len
= SUN_LEN(&sa
);
10076 /* connect to see if there is a listener */
10077 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10078 /* no listener so we will */
10079 unlink(sa
.sun_path
);
10081 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10082 warn("build_socket: bind");
10086 if (listen(s
, 1) == -1) {
10087 warn("build_socket: listen");
10100 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10101 GtkTreeIter
*iter
, struct tab
*t
)
10105 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10106 load_uri(t
, value
);
10113 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10114 GtkTreeIter
*iter
, struct tab
*t
)
10118 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10119 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10120 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10127 completion_add_uri(const gchar
*uri
)
10131 /* add uri to list_store */
10132 gtk_list_store_append(completion_model
, &iter
);
10133 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10137 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10138 GtkTreeIter
*iter
, gpointer user_data
)
10141 gboolean match
= FALSE
;
10143 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10149 match
= match_uri(value
, key
);
10156 completion_add(struct tab
*t
)
10158 /* enable completion for tab */
10159 t
->completion
= gtk_entry_completion_new();
10160 gtk_entry_completion_set_text_column(t
->completion
, 0);
10161 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10162 gtk_entry_completion_set_model(t
->completion
,
10163 GTK_TREE_MODEL(completion_model
));
10164 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10166 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10167 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10168 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10169 G_CALLBACK(completion_select_cb
), t
);
10170 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10171 G_CALLBACK(completion_hover_cb
), t
);
10179 if (stat(dir
, &sb
)) {
10180 if (mkdir(dir
, S_IRWXU
) == -1)
10181 err(1, "mkdir %s", dir
);
10182 if (stat(dir
, &sb
))
10183 err(1, "stat %s", dir
);
10185 if (S_ISDIR(sb
.st_mode
) == 0)
10186 errx(1, "%s not a dir", dir
);
10187 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10188 warnx("fixing invalid permissions on %s", dir
);
10189 if (chmod(dir
, S_IRWXU
) == -1)
10190 err(1, "chmod %s", dir
);
10198 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10202 GStaticRecMutex my_gdk_mtx
= G_STATIC_REC_MUTEX_INIT
;
10203 volatile int mtx_depth
;
10207 * The linux flash plugin violates the gdk locking mechanism.
10208 * Work around the issue by using a recursive mutex with some match applied
10209 * to see if we hit a buggy condition.
10211 * The following code is painful so just don't read it. It really doesn't
10212 * make much sense but seems to work.
10217 g_static_rec_mutex_lock(&my_gdk_mtx
);
10220 if (mtx_depth
<= 0) {
10221 /* should not happen */
10222 show_oops(NULL
, "negative mutex locking bug, trying to "
10224 fprintf(stderr
, "negative mutex locking bug, trying to "
10226 g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10227 g_static_rec_mutex_lock(&my_gdk_mtx
);
10232 if (mtx_depth
!= 1) {
10233 /* decrease mutext depth to 1 */
10235 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10237 } while (mtx_depth
> 1);
10246 /* if mutex depth isn't 1 then something went bad */
10247 if (mtx_depth
!= 1) {
10248 x
= g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
10250 /* should not happen */
10251 show_oops(NULL
, "mutex unlocking bug, trying to "
10253 fprintf(stderr
, "mutex unlocking bug, trying to "
10257 if (mtx_complain
== 0) {
10258 show_oops(NULL
, "buggy mutex implementation detected, "
10259 "work around implemented");
10260 fprintf(stderr
, "buggy mutex implementation detected, "
10261 "work around implemented");
10268 g_static_rec_mutex_unlock(&my_gdk_mtx
);
10272 main(int argc
, char *argv
[])
10275 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10276 char conf
[PATH_MAX
] = { '\0' };
10277 char file
[PATH_MAX
];
10278 char *env_proxy
= NULL
;
10282 struct sigaction sact
;
10283 GIOChannel
*channel
;
10290 g_thread_init(NULL
);
10291 gdk_threads_set_lock_functions(mtx_lock
, mtx_unlock
);
10292 gdk_threads_init();
10293 gdk_threads_enter();
10295 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pthread
);
10297 gtk_init(&argc
, &argv
);
10299 gnutls_global_init();
10301 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10305 RB_INIT(&downloads
);
10307 TAILQ_INIT(&sessions
);
10310 TAILQ_INIT(&aliases
);
10311 TAILQ_INIT(&undos
);
10317 /* fiddle with ulimits */
10318 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10321 /* just use them all */
10322 rlp
.rlim_cur
= rlp
.rlim_max
;
10323 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10325 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10327 else if (rlp
.rlim_cur
<= 256)
10328 startpage_add("%s requires at least 256 file "
10329 "descriptors, currently it has up to %d available",
10330 __progname
, rlp
.rlim_cur
);
10333 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10342 errx(0 , "Version: %s", version
);
10345 strlcpy(conf
, optarg
, sizeof(conf
));
10348 strlcpy(named_session
, optarg
, sizeof(named_session
));
10367 init_keybindings();
10369 /* generate session keys for xtp pages */
10370 generate_xtp_session_key(&dl_session_key
);
10371 generate_xtp_session_key(&hl_session_key
);
10372 generate_xtp_session_key(&cl_session_key
);
10373 generate_xtp_session_key(&fl_session_key
);
10376 bzero(&sact
, sizeof(sact
));
10377 sigemptyset(&sact
.sa_mask
);
10378 sact
.sa_handler
= sigchild
;
10379 sact
.sa_flags
= SA_NOCLDSTOP
;
10380 sigaction(SIGCHLD
, &sact
, NULL
);
10382 /* set download dir */
10383 pwd
= getpwuid(getuid());
10385 errx(1, "invalid user %d", getuid());
10386 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10388 /* compile buffer command regexes */
10391 /* set default string settings */
10392 home
= g_strdup("https://www.cyphertite.com");
10393 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10394 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10395 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10396 cmd_font_name
= g_strdup("monospace normal 9");
10397 oops_font_name
= g_strdup("monospace normal 9");
10398 statusbar_font_name
= g_strdup("monospace normal 9");
10399 tabbar_font_name
= g_strdup("monospace normal 9");
10400 statusbar_elems
= g_strdup("BP");
10401 encoding
= g_strdup("ISO-8859-1");
10403 /* read config file */
10404 if (strlen(conf
) == 0)
10405 snprintf(conf
, sizeof conf
, "%s/.%s",
10406 pwd
->pw_dir
, XT_CONF_FILE
);
10407 config_parse(conf
, 0);
10410 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10411 oops_font
= pango_font_description_from_string(oops_font_name
);
10412 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10413 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10415 /* working directory */
10416 if (strlen(work_dir
) == 0)
10417 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10418 pwd
->pw_dir
, XT_DIR
);
10421 /* icon cache dir */
10422 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10423 xxx_dir(cache_dir
);
10426 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10427 xxx_dir(certs_dir
);
10430 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10431 work_dir
, XT_SESSIONS_DIR
);
10432 xxx_dir(sessions_dir
);
10434 /* runtime settings that can override config file */
10435 if (runtime_settings
[0] != '\0')
10436 config_parse(runtime_settings
, 1);
10439 if (!strcmp(download_dir
, pwd
->pw_dir
))
10440 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10441 xxx_dir(download_dir
);
10443 /* favorites file */
10444 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10445 if (stat(file
, &sb
)) {
10446 warnx("favorites file doesn't exist, creating it");
10447 if ((f
= fopen(file
, "w")) == NULL
)
10448 err(1, "favorites");
10452 /* quickmarks file */
10453 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10454 if (stat(file
, &sb
)) {
10455 warnx("quickmarks file doesn't exist, creating it");
10456 if ((f
= fopen(file
, "w")) == NULL
)
10457 err(1, "quickmarks");
10461 /* search history */
10462 if (history_autosave
) {
10463 snprintf(search_file
, sizeof search_file
, "%s/%s",
10464 work_dir
, XT_SEARCH_FILE
);
10465 if (stat(search_file
, &sb
)) {
10466 warnx("search history file doesn't exist, creating it");
10467 if ((f
= fopen(search_file
, "w")) == NULL
)
10468 err(1, "search_history");
10471 history_read(&shl
, search_file
, &search_history_count
);
10474 /* command history */
10475 if (history_autosave
) {
10476 snprintf(command_file
, sizeof command_file
, "%s/%s",
10477 work_dir
, XT_COMMAND_FILE
);
10478 if (stat(command_file
, &sb
)) {
10479 warnx("command history file doesn't exist, creating it");
10480 if ((f
= fopen(command_file
, "w")) == NULL
)
10481 err(1, "command_history");
10484 history_read(&chl
, command_file
, &cmd_history_count
);
10488 session
= webkit_get_default_session();
10493 if (stat(ssl_ca_file
, &sb
)) {
10494 warnx("no CA file: %s", ssl_ca_file
);
10495 g_free(ssl_ca_file
);
10496 ssl_ca_file
= NULL
;
10498 g_object_set(session
,
10499 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10500 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10504 /* guess_search regex */
10505 if (url_regex
== NULL
)
10506 url_regex
= g_strdup(XT_URL_REGEX
);
10508 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10509 startpage_add("invalid url regex %s", url_regex
);
10512 env_proxy
= getenv("http_proxy");
10514 setup_proxy(env_proxy
);
10516 setup_proxy(http_proxy
);
10519 send_cmd_to_socket(argv
[0]);
10523 /* set some connection parameters */
10524 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10525 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10528 /* see if there is already an xxxterm running */
10529 if (single_instance
&& is_running()) {
10531 warnx("already running");
10536 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10537 send_cmd_to_socket(cmd
);
10547 /* uri completion */
10548 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10551 buffers_store
= gtk_list_store_new
10552 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10558 notebook_tab_set_visibility();
10560 if (save_global_history
)
10561 restore_global_history();
10563 /* restore session list */
10564 restore_sessions_list();
10566 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10567 restore_saved_tabs();
10569 a
.s
= named_session
;
10570 a
.i
= XT_SES_DONOTHING
;
10571 open_tabs(NULL
, &a
);
10574 /* see if we have an exception */
10575 if (!TAILQ_EMPTY(&spl
)) {
10576 create_new_tab("about:startpage", NULL
, focus
, -1);
10581 create_new_tab(argv
[0], NULL
, focus
, -1);
10588 if (TAILQ_EMPTY(&tabs
))
10589 create_new_tab(home
, NULL
, 1, -1);
10592 if ((s
= build_socket()) != -1) {
10593 channel
= g_io_channel_unix_new(s
);
10594 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10600 gdk_threads_leave();
10601 g_static_rec_mutex_unlock_full(&my_gdk_mtx
); /* just in case */
10604 gnutls_global_deinit();