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 #elif defined(__FreeBSD__)
50 #include "freebsd/util.h"
56 #include <sys/queue.h>
57 #include <sys/resource.h>
58 #include <sys/socket.h>
64 #include <gdk/gdkkeysyms.h>
66 #if GTK_CHECK_VERSION(3,0,0)
67 /* we still use GDK_* instead of GDK_KEY_* */
68 #include <gdk/gdkkeysyms-compat.h>
71 #include <webkit/webkit.h>
72 #include <libsoup/soup.h>
73 #include <gnutls/gnutls.h>
74 #include <JavaScriptCore/JavaScript.h>
75 #include <gnutls/x509.h>
77 #include "javascript.h"
80 javascript.h borrowed from vimprobable2 under the following license:
82 Copyright (c) 2009 Leon Winter
83 Copyright (c) 2009 Hannes Schueller
84 Copyright (c) 2009 Matto Fransen
86 Permission is hereby granted, free of charge, to any person obtaining a copy
87 of this software and associated documentation files (the "Software"), to deal
88 in the Software without restriction, including without limitation the rights
89 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
90 copies of the Software, and to permit persons to whom the Software is
91 furnished to do so, subject to the following conditions:
93 The above copyright notice and this permission notice shall be included in
94 all copies or substantial portions of the Software.
96 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
97 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
98 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
99 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
100 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
101 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
105 static char *version
= "$xxxterm$";
107 /* hooked functions */
108 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
109 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
114 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
115 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
116 #define XT_D_MOVE 0x0001
117 #define XT_D_KEY 0x0002
118 #define XT_D_TAB 0x0004
119 #define XT_D_URL 0x0008
120 #define XT_D_CMD 0x0010
121 #define XT_D_NAV 0x0020
122 #define XT_D_DOWNLOAD 0x0040
123 #define XT_D_CONFIG 0x0080
124 #define XT_D_JS 0x0100
125 #define XT_D_FAVORITE 0x0200
126 #define XT_D_PRINTING 0x0400
127 #define XT_D_COOKIE 0x0800
128 #define XT_D_KEYBINDING 0x1000
129 #define XT_D_CLIP 0x2000
130 #define XT_D_BUFFERCMD 0x4000
131 u_int32_t swm_debug
= 0
149 #define DPRINTF(x...)
150 #define DNPRINTF(n,x...)
153 #define LENGTH(x) (sizeof x / sizeof x[0])
154 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
155 ~(GDK_BUTTON1_MASK) & \
156 ~(GDK_BUTTON2_MASK) & \
157 ~(GDK_BUTTON3_MASK) & \
158 ~(GDK_BUTTON4_MASK) & \
161 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
172 TAILQ_ENTRY(tab
) entry
;
174 GtkWidget
*tab_content
;
183 GtkWidget
*uri_entry
;
184 GtkWidget
*search_entry
;
186 GtkWidget
*browser_win
;
187 GtkWidget
*statusbar_box
;
189 GtkWidget
*statusbar
;
190 GtkWidget
*buffercmd
;
201 GtkWidget
*js_toggle
;
202 GtkEntryCompletion
*completion
;
206 WebKitWebHistoryItem
*item
;
207 WebKitWebBackForwardList
*bfl
;
210 WebKitNetworkRequest
*icon_request
;
211 WebKitDownload
*icon_download
;
212 gchar
*icon_dest_uri
;
214 /* adjustments for browser */
217 GtkAdjustment
*adjust_h
;
218 GtkAdjustment
*adjust_v
;
224 int xtp_meaning
; /* identifies dls/favorites */
230 #define XT_HINT_NONE (0)
231 #define XT_HINT_NUMERICAL (1)
232 #define XT_HINT_ALPHANUM (2)
236 /* custom stylesheet */
246 WebKitWebSettings
*settings
;
250 double mark
[XT_NOMARKS
];
252 TAILQ_HEAD(tab_list
, tab
);
255 RB_ENTRY(history
) entry
;
259 RB_HEAD(history_list
, history
);
262 RB_ENTRY(download
) entry
;
264 WebKitDownload
*download
;
267 RB_HEAD(download_list
, download
);
270 RB_ENTRY(domain
) entry
;
272 int handy
; /* app use */
274 RB_HEAD(domain_list
, domain
);
277 TAILQ_ENTRY(undo
) entry
;
280 int back
; /* Keeps track of how many back
281 * history items there are. */
283 TAILQ_HEAD(undo_tailq
, undo
);
287 TAILQ_ENTRY(sp
) entry
;
289 TAILQ_HEAD(sp_list
, sp
);
291 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
292 int next_download_id
= 1;
301 #define XT_NAME ("XXXTerm")
302 #define XT_DIR (".xxxterm")
303 #define XT_CACHE_DIR ("cache")
304 #define XT_CERT_DIR ("certs/")
305 #define XT_SESSIONS_DIR ("sessions/")
306 #define XT_CONF_FILE ("xxxterm.conf")
307 #define XT_FAVS_FILE ("favorites")
308 #define XT_QMARKS_FILE ("quickmarks")
309 #define XT_SAVED_TABS_FILE ("main_session")
310 #define XT_RESTART_TABS_FILE ("restart_tabs")
311 #define XT_SOCKET_FILE ("socket")
312 #define XT_HISTORY_FILE ("history")
313 #define XT_REJECT_FILE ("rejected.txt")
314 #define XT_COOKIE_FILE ("cookies.txt")
315 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
316 #define XT_CB_HANDLED (TRUE)
317 #define XT_CB_PASSTHROUGH (FALSE)
318 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
319 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
320 #define XT_DLMAN_REFRESH "10"
321 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
322 "td{overflow: hidden;" \
323 " padding: 2px 2px 2px 2px;" \
324 " border: 1px solid black;" \
325 " vertical-align:top;" \
326 " word-wrap: break-word}\n" \
327 "tr:hover{background: #ffff99}\n" \
328 "th{background-color: #cccccc;" \
329 " border: 1px solid black}\n" \
330 "table{width: 100%%;" \
331 " border: 1px black solid;" \
332 " border-collapse:collapse}\n" \
334 "border: 1px solid black;" \
337 ".progress-inner{float: left;" \
339 " background: green}\n" \
340 ".dlstatus{font-size: small;" \
341 " text-align: center}\n" \
343 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
344 #define XT_MAX_UNDO_CLOSE_TAB (32)
345 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
346 #define XT_PRINT_EXTRA_MARGIN 10
347 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
350 #define XT_COLOR_RED "#cc0000"
351 #define XT_COLOR_YELLOW "#ffff66"
352 #define XT_COLOR_BLUE "lightblue"
353 #define XT_COLOR_GREEN "#99ff66"
354 #define XT_COLOR_WHITE "white"
355 #define XT_COLOR_BLACK "black"
357 #define XT_COLOR_CT_BACKGROUND "#000000"
358 #define XT_COLOR_CT_INACTIVE "#dddddd"
359 #define XT_COLOR_CT_ACTIVE "#bbbb00"
360 #define XT_COLOR_CT_SEPARATOR "#555555"
362 #define XT_COLOR_SB_SEPARATOR "#555555"
364 #define XT_PROTO_DELIM "://"
367 * xxxterm "protocol" (xtp)
368 * We use this for managing stuff like downloads and favorites. They
369 * make magical HTML pages in memory which have xxxt:// links in order
370 * to communicate with xxxterm's internals. These links take the format:
371 * xxxt://class/session_key/action/arg
373 * Don't begin xtp class/actions as 0. atoi returns that on error.
375 * Typically we have not put addition of items in this framework, as
376 * adding items is either done via an ex-command or via a keybinding instead.
379 #define XT_XTP_STR "xxxt://"
381 /* XTP classes (xxxt://<class>) */
382 #define XT_XTP_INVALID 0 /* invalid */
383 #define XT_XTP_DL 1 /* downloads */
384 #define XT_XTP_HL 2 /* history */
385 #define XT_XTP_CL 3 /* cookies */
386 #define XT_XTP_FL 4 /* favorites */
388 /* XTP download actions */
389 #define XT_XTP_DL_LIST 1
390 #define XT_XTP_DL_CANCEL 2
391 #define XT_XTP_DL_REMOVE 3
393 /* XTP history actions */
394 #define XT_XTP_HL_LIST 1
395 #define XT_XTP_HL_REMOVE 2
397 /* XTP cookie actions */
398 #define XT_XTP_CL_LIST 1
399 #define XT_XTP_CL_REMOVE 2
401 /* XTP cookie actions */
402 #define XT_XTP_FL_LIST 1
403 #define XT_XTP_FL_REMOVE 2
406 #define XT_MOVE_INVALID (0)
407 #define XT_MOVE_DOWN (1)
408 #define XT_MOVE_UP (2)
409 #define XT_MOVE_BOTTOM (3)
410 #define XT_MOVE_TOP (4)
411 #define XT_MOVE_PAGEDOWN (5)
412 #define XT_MOVE_PAGEUP (6)
413 #define XT_MOVE_HALFDOWN (7)
414 #define XT_MOVE_HALFUP (8)
415 #define XT_MOVE_LEFT (9)
416 #define XT_MOVE_FARLEFT (10)
417 #define XT_MOVE_RIGHT (11)
418 #define XT_MOVE_FARRIGHT (12)
419 #define XT_MOVE_PERCENT (13)
421 #define XT_QMARK_SET (0)
422 #define XT_QMARK_OPEN (1)
423 #define XT_QMARK_TAB (2)
425 #define XT_MARK_SET (0)
426 #define XT_MARK_GOTO (1)
428 #define XT_TAB_LAST (-4)
429 #define XT_TAB_FIRST (-3)
430 #define XT_TAB_PREV (-2)
431 #define XT_TAB_NEXT (-1)
432 #define XT_TAB_INVALID (0)
433 #define XT_TAB_NEW (1)
434 #define XT_TAB_DELETE (2)
435 #define XT_TAB_DELQUIT (3)
436 #define XT_TAB_OPEN (4)
437 #define XT_TAB_UNDO_CLOSE (5)
438 #define XT_TAB_SHOW (6)
439 #define XT_TAB_HIDE (7)
440 #define XT_TAB_NEXTSTYLE (8)
442 #define XT_NAV_INVALID (0)
443 #define XT_NAV_BACK (1)
444 #define XT_NAV_FORWARD (2)
445 #define XT_NAV_RELOAD (3)
447 #define XT_FOCUS_INVALID (0)
448 #define XT_FOCUS_URI (1)
449 #define XT_FOCUS_SEARCH (2)
451 #define XT_SEARCH_INVALID (0)
452 #define XT_SEARCH_NEXT (1)
453 #define XT_SEARCH_PREV (2)
455 #define XT_PASTE_CURRENT_TAB (0)
456 #define XT_PASTE_NEW_TAB (1)
458 #define XT_ZOOM_IN (-1)
459 #define XT_ZOOM_OUT (-2)
460 #define XT_ZOOM_NORMAL (100)
462 #define XT_URL_SHOW (1)
463 #define XT_URL_HIDE (2)
465 #define XT_WL_TOGGLE (1<<0)
466 #define XT_WL_ENABLE (1<<1)
467 #define XT_WL_DISABLE (1<<2)
468 #define XT_WL_FQDN (1<<3) /* default */
469 #define XT_WL_TOPLEVEL (1<<4)
470 #define XT_WL_PERSISTENT (1<<5)
471 #define XT_WL_SESSION (1<<6)
472 #define XT_WL_RELOAD (1<<7)
474 #define XT_SHOW (1<<7)
475 #define XT_DELETE (1<<8)
476 #define XT_SAVE (1<<9)
477 #define XT_OPEN (1<<10)
479 #define XT_CMD_OPEN (0)
480 #define XT_CMD_OPEN_CURRENT (1)
481 #define XT_CMD_TABNEW (2)
482 #define XT_CMD_TABNEW_CURRENT (3)
484 #define XT_STATUS_NOTHING (0)
485 #define XT_STATUS_LINK (1)
486 #define XT_STATUS_URI (2)
487 #define XT_STATUS_LOADING (3)
489 #define XT_SES_DONOTHING (0)
490 #define XT_SES_CLOSETABS (1)
492 #define XT_BM_NORMAL (0)
493 #define XT_BM_WHITELIST (1)
494 #define XT_BM_KIOSK (2)
496 #define XT_PREFIX (1<<0)
497 #define XT_USERARG (1<<1)
498 #define XT_URLARG (1<<2)
499 #define XT_INTARG (1<<3)
501 #define XT_TABS_NORMAL 0
502 #define XT_TABS_COMPACT 1
504 #define XT_BUFCMD_SZ (8)
512 TAILQ_ENTRY(mime_type
) entry
;
514 TAILQ_HEAD(mime_type_list
, mime_type
);
520 TAILQ_ENTRY(alias
) entry
;
522 TAILQ_HEAD(alias_list
, alias
);
524 /* settings that require restart */
525 int tabless
= 0; /* allow only 1 tab */
526 int enable_socket
= 0;
527 int single_instance
= 0; /* only allow one xxxterm to run */
528 int fancy_bar
= 1; /* fancy toolbar */
529 int browser_mode
= XT_BM_NORMAL
;
530 int enable_localstorage
= 0;
531 char *statusbar_elems
= NULL
;
533 /* runtime settings */
534 int show_tabs
= 1; /* show tabs on notebook */
535 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
536 int show_url
= 1; /* show url toolbar on notebook */
537 int show_statusbar
= 0; /* vimperator style status bar */
538 int ctrl_click_focus
= 0; /* ctrl click gets focus */
539 int cookies_enabled
= 1; /* enable cookies */
540 int read_only_cookies
= 0; /* enable to not write cookies */
541 int enable_scripts
= 1;
542 int enable_plugins
= 0;
543 gfloat default_zoom_level
= 1.0;
544 char default_script
[PATH_MAX
];
545 int window_height
= 768;
546 int window_width
= 1024;
547 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
548 int refresh_interval
= 10; /* download refresh interval */
549 int enable_cookie_whitelist
= 0;
550 int enable_js_whitelist
= 0;
551 int session_timeout
= 3600; /* cookie session timeout */
552 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
553 char *ssl_ca_file
= NULL
;
554 char *resource_dir
= NULL
;
555 gboolean ssl_strict_certs
= FALSE
;
556 int append_next
= 1; /* append tab after current tab */
558 char *search_string
= NULL
;
559 char *http_proxy
= NULL
;
560 char download_dir
[PATH_MAX
];
561 char runtime_settings
[PATH_MAX
]; /* override of settings */
562 int allow_volatile_cookies
= 0;
563 int save_global_history
= 0; /* save global history to disk */
564 char *user_agent
= NULL
;
565 int save_rejected_cookies
= 0;
566 int session_autosave
= 0;
567 int guess_search
= 0;
568 int dns_prefetch
= FALSE
;
569 gint max_connections
= 25;
570 gint max_host_connections
= 5;
571 gint enable_spell_checking
= 0;
572 char *spell_check_languages
= NULL
;
573 int xterm_workaround
= 0;
575 char *cmd_font_name
= NULL
;
576 char *oops_font_name
= NULL
;
577 char *statusbar_font_name
= NULL
;
578 char *tabbar_font_name
= NULL
;
579 PangoFontDescription
*cmd_font
;
580 PangoFontDescription
*oops_font
;
581 PangoFontDescription
*statusbar_font
;
582 PangoFontDescription
*tabbar_font
;
583 char *qmarks
[XT_NOMARKS
];
585 int btn_down
; /* M1 down in any wv */
589 int set_browser_mode(struct settings
*, char *);
590 int set_cookie_policy(struct settings
*, char *);
591 int set_download_dir(struct settings
*, char *);
592 int set_default_script(struct settings
*, char *);
593 int set_runtime_dir(struct settings
*, char *);
594 int set_tab_style(struct settings
*, char *);
595 int set_work_dir(struct settings
*, char *);
596 int add_alias(struct settings
*, char *);
597 int add_mime_type(struct settings
*, char *);
598 int add_cookie_wl(struct settings
*, char *);
599 int add_js_wl(struct settings
*, char *);
600 int add_kb(struct settings
*, char *);
601 void button_set_stockid(GtkWidget
*, char *);
602 GtkWidget
* create_button(char *, char *, int);
604 char *get_browser_mode(struct settings
*);
605 char *get_cookie_policy(struct settings
*);
606 char *get_download_dir(struct settings
*);
607 char *get_default_script(struct settings
*);
608 char *get_runtime_dir(struct settings
*);
609 char *get_tab_style(struct settings
*);
610 char *get_work_dir(struct settings
*);
611 void startpage_add(const char *, ...);
613 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
614 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
615 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
616 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
617 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
619 void recalc_tabs(void);
620 void recolor_compact_tabs(void);
621 void set_current_tab(int page_num
);
622 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
623 void marks_clear(struct tab
*t
);
625 int set_http_proxy(char *);
628 int (*set
)(struct settings
*, char *);
629 char *(*get
)(struct settings
*);
630 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
633 struct special s_browser_mode
= {
639 struct special s_cookie
= {
645 struct special s_alias
= {
651 struct special s_mime
= {
657 struct special s_js
= {
663 struct special s_kb
= {
669 struct special s_cookie_wl
= {
675 struct special s_default_script
= {
681 struct special s_download_dir
= {
687 struct special s_work_dir
= {
693 struct special s_tab_style
= {
702 #define XT_S_INVALID (0)
705 #define XT_S_FLOAT (3)
707 #define XT_SF_RESTART (1<<0)
708 #define XT_SF_RUNTIME (1<<1)
713 int (*activate
)(char *);
715 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
716 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
717 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
718 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
719 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
720 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
721 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
722 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
723 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
724 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
725 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
726 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
727 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
728 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
729 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
730 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
731 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
732 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
733 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
734 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
735 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
736 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
737 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
738 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
739 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
740 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
741 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
742 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
743 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
744 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
745 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
746 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
747 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
748 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
749 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
750 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
751 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
752 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
753 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
754 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
755 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
756 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
757 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
758 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
759 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
762 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
763 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
764 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
765 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
767 /* runtime settings */
768 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
769 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
770 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
771 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
772 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
775 int about(struct tab
*, struct karg
*);
776 int blank(struct tab
*, struct karg
*);
777 int ca_cmd(struct tab
*, struct karg
*);
778 int cookie_show_wl(struct tab
*, struct karg
*);
779 int js_show_wl(struct tab
*, struct karg
*);
780 int help(struct tab
*, struct karg
*);
781 int set(struct tab
*, struct karg
*);
782 int stats(struct tab
*, struct karg
*);
783 int marco(struct tab
*, struct karg
*);
784 int startpage(struct tab
*, struct karg
*);
785 const char * marco_message(int *);
786 int xtp_page_cl(struct tab
*, struct karg
*);
787 int xtp_page_dl(struct tab
*, struct karg
*);
788 int xtp_page_fl(struct tab
*, struct karg
*);
789 int xtp_page_hl(struct tab
*, struct karg
*);
790 void xt_icon_from_file(struct tab
*, char *);
791 const gchar
*get_uri(struct tab
*);
792 const gchar
*get_title(struct tab
*, bool);
794 #define XT_URI_ABOUT ("about:")
795 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
796 #define XT_URI_ABOUT_ABOUT ("about")
797 #define XT_URI_ABOUT_BLANK ("blank")
798 #define XT_URI_ABOUT_CERTS ("certs")
799 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
800 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
801 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
802 #define XT_URI_ABOUT_FAVORITES ("favorites")
803 #define XT_URI_ABOUT_HELP ("help")
804 #define XT_URI_ABOUT_HISTORY ("history")
805 #define XT_URI_ABOUT_JSWL ("jswl")
806 #define XT_URI_ABOUT_SET ("set")
807 #define XT_URI_ABOUT_STATS ("stats")
808 #define XT_URI_ABOUT_MARCO ("marco")
809 #define XT_URI_ABOUT_STARTPAGE ("startpage")
813 int (*func
)(struct tab
*, struct karg
*);
815 { XT_URI_ABOUT_ABOUT
, about
},
816 { XT_URI_ABOUT_BLANK
, blank
},
817 { XT_URI_ABOUT_CERTS
, ca_cmd
},
818 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
819 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
820 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
821 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
822 { XT_URI_ABOUT_HELP
, help
},
823 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
824 { XT_URI_ABOUT_JSWL
, js_show_wl
},
825 { XT_URI_ABOUT_SET
, set
},
826 { XT_URI_ABOUT_STATS
, stats
},
827 { XT_URI_ABOUT_MARCO
, marco
},
828 { XT_URI_ABOUT_STARTPAGE
, startpage
},
831 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
832 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
833 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
834 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
835 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
836 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
837 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
840 extern char *__progname
;
843 GtkWidget
*main_window
;
844 GtkNotebook
*notebook
;
846 GtkWidget
*arrow
, *abtn
;
847 struct tab_list tabs
;
848 struct history_list hl
;
849 struct download_list downloads
;
850 struct domain_list c_wl
;
851 struct domain_list js_wl
;
852 struct undo_tailq undos
;
853 struct keybinding_list kbl
;
856 int updating_dl_tabs
= 0;
857 int updating_hl_tabs
= 0;
858 int updating_cl_tabs
= 0;
859 int updating_fl_tabs
= 0;
861 uint64_t blocked_cookies
= 0;
862 char named_session
[PATH_MAX
];
863 int icon_size_map(int);
865 GtkListStore
*completion_model
;
866 void completion_add(struct tab
*);
867 void completion_add_uri(const gchar
*);
868 GtkListStore
*buffers_store
;
869 void xxx_dir(char *);
871 /* marks and quickmarks array storage.
872 * first a-z, then A-Z, then 0-9 */
879 if (i
>= 0 && i
<= 'z' - 'a')
883 if (i
>= 0 && i
<= 'Z' - 'A')
898 if (m
>= 'a' && m
<= 'z')
899 return ret
+ m
- 'a';
901 ret
+= 'z' - 'a' + 1;
902 if (m
>= 'A' && m
<= 'Z')
903 return ret
+ m
- 'A';
905 ret
+= 'Z' - 'A' + 1;
906 if (m
>= '0' && m
<= '9')
907 return ret
+ m
- '0';
916 int saved_errno
, status
;
921 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
925 if (errno
!= ECHILD
) {
927 clog_warn("sigchild: waitpid:");
933 if (WIFEXITED(status
)) {
934 if (WEXITSTATUS(status
) != 0) {
936 clog_warnx("sigchild: child exit status: %d",
937 WEXITSTATUS(status));
942 clog_warnx("sigchild: child is terminated abnormally");
951 is_g_object_setting(GObject
*o
, char *str
)
953 guint n_props
= 0, i
;
954 GParamSpec
**proplist
;
956 if (! G_IS_OBJECT(o
))
959 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
962 for (i
=0; i
< n_props
; i
++) {
963 if (! strcmp(proplist
[i
]->name
, str
))
970 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
974 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
976 "<title>%s</title>\n"
985 addstyles
? XT_PAGE_STYLE
: "",
994 * Display a web page from a HTML string in memory, rather than from a URL
997 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1002 /* we set this to indicate we want to manually do navaction */
1004 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1006 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1008 /* set t->xtp_meaning */
1009 for (i
= 0; i
< LENGTH(about_list
); i
++)
1010 if (!strcmp(title
, about_list
[i
].name
)) {
1015 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1016 #if GTK_CHECK_VERSION(2, 20, 0)
1017 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1018 gtk_widget_hide(t
->spinner
);
1020 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1021 xt_icon_from_file(t
, file
);
1026 get_current_tab(void)
1030 TAILQ_FOREACH(t
, &tabs
, entry
) {
1031 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1035 warnx("%s: no current tab", __func__
);
1041 set_status(struct tab
*t
, gchar
*s
, int status
)
1049 case XT_STATUS_LOADING
:
1050 type
= g_strdup_printf("Loading: %s", s
);
1053 case XT_STATUS_LINK
:
1054 type
= g_strdup_printf("Link: %s", s
);
1056 t
->status
= g_strdup(gtk_entry_get_text(
1057 GTK_ENTRY(t
->sbe
.statusbar
)));
1061 type
= g_strdup_printf("%s", s
);
1063 t
->status
= g_strdup(type
);
1067 t
->status
= g_strdup(s
);
1069 case XT_STATUS_NOTHING
:
1074 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1080 hide_cmd(struct tab
*t
)
1082 gtk_widget_hide(t
->cmd
);
1086 show_cmd(struct tab
*t
)
1088 gtk_widget_hide(t
->oops
);
1089 gtk_widget_show(t
->cmd
);
1093 hide_buffers(struct tab
*t
)
1095 gtk_widget_hide(t
->buffers
);
1096 gtk_list_store_clear(buffers_store
);
1106 sort_tabs_by_page_num(struct tab
***stabs
)
1111 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1113 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1115 TAILQ_FOREACH(t
, &tabs
, entry
)
1116 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1122 buffers_make_list(void)
1125 const gchar
*title
= NULL
;
1127 struct tab
**stabs
= NULL
;
1129 num_tabs
= sort_tabs_by_page_num(&stabs
);
1131 for (i
= 0; i
< num_tabs
; i
++)
1133 gtk_list_store_append(buffers_store
, &iter
);
1134 title
= get_title(stabs
[i
], FALSE
);
1135 gtk_list_store_set(buffers_store
, &iter
,
1136 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1146 show_buffers(struct tab
*t
)
1148 buffers_make_list();
1149 gtk_widget_show(t
->buffers
);
1150 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1154 toggle_buffers(struct tab
*t
)
1156 if (gtk_widget_get_visible(t
->buffers
))
1163 buffers(struct tab
*t
, struct karg
*args
)
1171 hide_oops(struct tab
*t
)
1173 gtk_widget_hide(t
->oops
);
1177 show_oops(struct tab
*at
, const char *fmt
, ...)
1181 struct tab
*t
= NULL
;
1187 if ((t
= get_current_tab()) == NULL
)
1193 if (vasprintf(&msg
, fmt
, ap
) == -1)
1194 errx(1, "show_oops failed");
1197 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1198 gtk_widget_hide(t
->cmd
);
1199 gtk_widget_show(t
->oops
);
1206 get_as_string(struct settings
*s
)
1217 warnx("get_as_string skip %s\n", s
->name
);
1218 } else if (s
->type
== XT_S_INT
)
1219 r
= g_strdup_printf("%d", *s
->ival
);
1220 else if (s
->type
== XT_S_STR
)
1221 r
= g_strdup(*s
->sval
);
1222 else if (s
->type
== XT_S_FLOAT
)
1223 r
= g_strdup_printf("%f", *s
->fval
);
1225 r
= g_strdup_printf("INVALID TYPE");
1231 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1236 for (i
= 0; i
< LENGTH(rs
); i
++) {
1237 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1238 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1240 s
= get_as_string(&rs
[i
]);
1241 cb(&rs
[i
], s
, cb_args
);
1248 set_browser_mode(struct settings
*s
, char *val
)
1250 if (!strcmp(val
, "whitelist")) {
1251 browser_mode
= XT_BM_WHITELIST
;
1252 allow_volatile_cookies
= 0;
1253 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1254 cookies_enabled
= 1;
1255 enable_cookie_whitelist
= 1;
1256 read_only_cookies
= 0;
1257 save_rejected_cookies
= 0;
1258 session_timeout
= 3600;
1260 enable_js_whitelist
= 1;
1261 enable_localstorage
= 0;
1262 } else if (!strcmp(val
, "normal")) {
1263 browser_mode
= XT_BM_NORMAL
;
1264 allow_volatile_cookies
= 0;
1265 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1266 cookies_enabled
= 1;
1267 enable_cookie_whitelist
= 0;
1268 read_only_cookies
= 0;
1269 save_rejected_cookies
= 0;
1270 session_timeout
= 3600;
1272 enable_js_whitelist
= 0;
1273 enable_localstorage
= 1;
1274 } else if (!strcmp(val
, "kiosk")) {
1275 browser_mode
= XT_BM_KIOSK
;
1276 allow_volatile_cookies
= 0;
1277 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1278 cookies_enabled
= 1;
1279 enable_cookie_whitelist
= 0;
1280 read_only_cookies
= 0;
1281 save_rejected_cookies
= 0;
1282 session_timeout
= 3600;
1284 enable_js_whitelist
= 0;
1285 enable_localstorage
= 1;
1295 get_browser_mode(struct settings
*s
)
1299 if (browser_mode
== XT_BM_WHITELIST
)
1300 r
= g_strdup("whitelist");
1301 else if (browser_mode
== XT_BM_NORMAL
)
1302 r
= g_strdup("normal");
1303 else if (browser_mode
== XT_BM_KIOSK
)
1304 r
= g_strdup("kiosk");
1312 set_cookie_policy(struct settings
*s
, char *val
)
1314 if (!strcmp(val
, "no3rdparty"))
1315 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1316 else if (!strcmp(val
, "accept"))
1317 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1318 else if (!strcmp(val
, "reject"))
1319 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1327 get_cookie_policy(struct settings
*s
)
1331 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1332 r
= g_strdup("no3rdparty");
1333 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1334 r
= g_strdup("accept");
1335 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1336 r
= g_strdup("reject");
1344 get_default_script(struct settings
*s
)
1346 if (default_script
[0] == '\0')
1348 return (g_strdup(default_script
));
1352 set_default_script(struct settings
*s
, char *val
)
1355 snprintf(default_script
, sizeof default_script
, "%s/%s",
1356 pwd
->pw_dir
, &val
[1]);
1358 strlcpy(default_script
, val
, sizeof default_script
);
1364 get_download_dir(struct settings
*s
)
1366 if (download_dir
[0] == '\0')
1368 return (g_strdup(download_dir
));
1372 set_download_dir(struct settings
*s
, char *val
)
1375 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1376 pwd
->pw_dir
, &val
[1]);
1378 strlcpy(download_dir
, val
, sizeof download_dir
);
1385 * We use these to prevent people putting xxxt:// URLs on
1386 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1388 #define XT_XTP_SES_KEY_SZ 8
1389 #define XT_XTP_SES_KEY_HEX_FMT \
1390 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1391 char *dl_session_key
; /* downloads */
1392 char *hl_session_key
; /* history list */
1393 char *cl_session_key
; /* cookie list */
1394 char *fl_session_key
; /* favorites list */
1396 char work_dir
[PATH_MAX
];
1397 char certs_dir
[PATH_MAX
];
1398 char cache_dir
[PATH_MAX
];
1399 char sessions_dir
[PATH_MAX
];
1400 char cookie_file
[PATH_MAX
];
1401 SoupURI
*proxy_uri
= NULL
;
1402 SoupSession
*session
;
1403 SoupCookieJar
*s_cookiejar
;
1404 SoupCookieJar
*p_cookiejar
;
1405 char rc_fname
[PATH_MAX
];
1407 struct mime_type_list mtl
;
1408 struct alias_list aliases
;
1411 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1412 void delete_tab(struct tab
*);
1413 void setzoom_webkit(struct tab
*, int);
1414 int run_script(struct tab
*, char *);
1415 int download_rb_cmp(struct download
*, struct download
*);
1416 gboolean
cmd_execute(struct tab
*t
, char *str
);
1419 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1421 return (strcmp(h1
->uri
, h2
->uri
));
1423 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1426 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1428 return (strcmp(d1
->d
, d2
->d
));
1430 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1433 get_work_dir(struct settings
*s
)
1435 if (work_dir
[0] == '\0')
1437 return (g_strdup(work_dir
));
1441 set_work_dir(struct settings
*s
, char *val
)
1444 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1445 pwd
->pw_dir
, &val
[1]);
1447 strlcpy(work_dir
, val
, sizeof work_dir
);
1453 get_tab_style(struct settings
*s
)
1455 if (tab_style
== XT_TABS_NORMAL
)
1456 return (g_strdup("normal"));
1458 return (g_strdup("compact"));
1462 set_tab_style(struct settings
*s
, char *val
)
1464 if (!strcmp(val
, "normal"))
1465 tab_style
= XT_TABS_NORMAL
;
1466 else if (!strcmp(val
, "compact"))
1467 tab_style
= XT_TABS_COMPACT
;
1475 * generate a session key to secure xtp commands.
1476 * pass in a ptr to the key in question and it will
1477 * be modified in place.
1480 generate_xtp_session_key(char **key
)
1482 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1488 /* make a new one */
1489 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1490 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1491 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1492 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1494 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1498 * validate a xtp session key.
1502 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1504 if (strcmp(trusted
, untrusted
) != 0) {
1505 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1514 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1516 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1518 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1520 struct valid_url_types
{
1531 valid_url_type(char *url
)
1535 for (i
= 0; i
< LENGTH(vut
); i
++)
1536 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1543 print_cookie(char *msg
, SoupCookie
*c
)
1549 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1550 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1551 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1552 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1553 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1554 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1555 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1556 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1557 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1558 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1562 walk_alias(struct settings
*s
,
1563 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1568 if (s
== NULL
|| cb
== NULL
) {
1569 show_oops(NULL
, "walk_alias invalid parameters");
1573 TAILQ_FOREACH(a
, &aliases
, entry
) {
1574 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1575 cb(s
, str
, cb_args
);
1581 match_alias(char *url_in
)
1585 char *url_out
= NULL
, *search
, *enc_arg
;
1587 search
= g_strdup(url_in
);
1589 if (strsep(&arg
, " \t") == NULL
) {
1590 show_oops(NULL
, "match_alias: NULL URL");
1594 TAILQ_FOREACH(a
, &aliases
, entry
) {
1595 if (!strcmp(search
, a
->a_name
))
1600 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1603 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1604 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1607 url_out
= g_strdup_printf(a
->a_uri
, "");
1615 guess_url_type(char *url_in
)
1618 char *url_out
= NULL
, *enc_search
= NULL
;
1620 url_out
= match_alias(url_in
);
1621 if (url_out
!= NULL
)
1626 * If there is no dot nor slash in the string and it isn't a
1627 * path to a local file and doesn't resolves to an IP, assume
1628 * that the user wants to search for the string.
1631 if (strchr(url_in
, '.') == NULL
&&
1632 strchr(url_in
, '/') == NULL
&&
1633 stat(url_in
, &sb
) != 0 &&
1634 gethostbyname(url_in
) == NULL
) {
1636 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1637 url_out
= g_strdup_printf(search_string
, enc_search
);
1643 /* XXX not sure about this heuristic */
1644 if (stat(url_in
, &sb
) == 0)
1645 url_out
= g_strdup_printf("file://%s", url_in
);
1647 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1649 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1655 load_uri(struct tab
*t
, gchar
*uri
)
1658 gchar
*newuri
= NULL
;
1664 /* Strip leading spaces. */
1665 while (*uri
&& isspace(*uri
))
1668 if (strlen(uri
) == 0) {
1673 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1675 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1676 for (i
= 0; i
< LENGTH(about_list
); i
++)
1677 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1678 bzero(&args
, sizeof args
);
1679 about_list
[i
].func(t
, &args
);
1680 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1684 show_oops(t
, "invalid about page");
1688 if (valid_url_type(uri
)) {
1689 newuri
= guess_url_type(uri
);
1693 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1695 webkit_web_view_load_uri(t
->wv
, uri
);
1702 get_uri(struct tab
*t
)
1704 const gchar
*uri
= NULL
;
1706 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1708 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1709 uri
= webkit_web_view_get_uri(t
->wv
);
1711 /* use tmp_uri to make sure it is g_freed */
1714 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1715 about_list
[t
->xtp_meaning
].name
);
1722 get_title(struct tab
*t
, bool window
)
1724 const gchar
*set
= NULL
, *title
= NULL
;
1725 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1727 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1728 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1731 title
= webkit_web_view_get_title(t
->wv
);
1732 if ((set
= title
? title
: get_uri(t
)))
1736 set
= window
? XT_NAME
: "(untitled)";
1742 add_alias(struct settings
*s
, char *line
)
1745 struct alias
*a
= NULL
;
1747 if (s
== NULL
|| line
== NULL
) {
1748 show_oops(NULL
, "add_alias invalid parameters");
1753 a
= g_malloc(sizeof(*a
));
1755 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1756 show_oops(NULL
, "add_alias: incomplete alias definition");
1759 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1760 show_oops(NULL
, "add_alias: invalid alias definition");
1764 a
->a_name
= g_strdup(alias
);
1765 a
->a_uri
= g_strdup(l
);
1767 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1769 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1779 add_mime_type(struct settings
*s
, char *line
)
1783 struct mime_type
*m
= NULL
;
1784 int downloadfirst
= 0;
1786 /* XXX this could be smarter */
1788 if (line
== NULL
|| strlen(line
) == 0) {
1789 show_oops(NULL
, "add_mime_type invalid parameters");
1798 m
= g_malloc(sizeof(*m
));
1800 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1801 show_oops(NULL
, "add_mime_type: invalid mime_type");
1804 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1805 mime_type
[strlen(mime_type
) - 1] = '\0';
1810 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1811 show_oops(NULL
, "add_mime_type: invalid mime_type");
1815 m
->mt_type
= g_strdup(mime_type
);
1816 m
->mt_action
= g_strdup(l
);
1817 m
->mt_download
= downloadfirst
;
1819 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1820 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1822 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1832 find_mime_type(char *mime_type
)
1834 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1836 TAILQ_FOREACH(m
, &mtl
, entry
) {
1837 if (m
->mt_default
&&
1838 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1841 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1854 walk_mime_type(struct settings
*s
,
1855 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1857 struct mime_type
*m
;
1860 if (s
== NULL
|| cb
== NULL
) {
1861 show_oops(NULL
, "walk_mime_type invalid parameters");
1865 TAILQ_FOREACH(m
, &mtl
, entry
) {
1866 str
= g_strdup_printf("%s%s --> %s",
1868 m
->mt_default
? "*" : "",
1870 cb(s
, str
, cb_args
);
1876 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1881 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1884 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1886 /* treat *.moo.com the same as .moo.com */
1887 if (str
[0] == '*' && str
[1] == '.')
1889 else if (str
[0] == '.')
1894 d
= g_malloc(sizeof *d
);
1896 d
->d
= g_strdup_printf(".%s", str
);
1898 d
->d
= g_strdup(str
);
1901 if (RB_INSERT(domain_list
, wl
, d
))
1904 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1915 add_cookie_wl(struct settings
*s
, char *entry
)
1917 wl_add(entry
, &c_wl
, 1);
1922 walk_cookie_wl(struct settings
*s
,
1923 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1927 if (s
== NULL
|| cb
== NULL
) {
1928 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1932 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1933 cb(s
, d
->d
, cb_args
);
1937 walk_js_wl(struct settings
*s
,
1938 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1942 if (s
== NULL
|| cb
== NULL
) {
1943 show_oops(NULL
, "walk_js_wl invalid parameters");
1947 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1948 cb(s
, d
->d
, cb_args
);
1952 add_js_wl(struct settings
*s
, char *entry
)
1954 wl_add(entry
, &js_wl
, 1 /* persistent */);
1959 wl_find(const gchar
*search
, struct domain_list
*wl
)
1962 struct domain
*d
= NULL
, dfind
;
1965 if (search
== NULL
|| wl
== NULL
)
1967 if (strlen(search
) < 2)
1970 if (search
[0] != '.')
1971 s
= g_strdup_printf(".%s", search
);
1973 s
= g_strdup(search
);
1975 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1978 d
= RB_FIND(domain_list
, wl
, &dfind
);
1992 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1998 if (s
== NULL
|| wl
== NULL
)
2001 if (!strncmp(s
, "http://", strlen("http://")))
2002 s
= &s
[strlen("http://")];
2003 else if (!strncmp(s
, "https://", strlen("https://")))
2004 s
= &s
[strlen("https://")];
2009 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2010 /* chop string at first slash */
2011 if (s
[i
] == '/' || s
[i
] == '\0') {
2014 r
= wl_find(ss
, wl
);
2023 settings_add(char *var
, char *val
)
2030 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2031 if (strcmp(var
, rs
[i
].name
))
2035 if (rs
[i
].s
->set(&rs
[i
], val
))
2036 errx(1, "invalid value for %s: %s", var
, val
);
2040 switch (rs
[i
].type
) {
2049 errx(1, "invalid sval for %s",
2063 errx(1, "invalid type for %s", var
);
2072 config_parse(char *filename
, int runtime
)
2075 char *line
, *cp
, *var
, *val
;
2076 size_t len
, lineno
= 0;
2078 char file
[PATH_MAX
];
2081 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2083 if (filename
== NULL
)
2086 if (runtime
&& runtime_settings
[0] != '\0') {
2087 snprintf(file
, sizeof file
, "%s/%s",
2088 work_dir
, runtime_settings
);
2089 if (stat(file
, &sb
)) {
2090 warnx("runtime file doesn't exist, creating it");
2091 if ((f
= fopen(file
, "w")) == NULL
)
2093 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2097 strlcpy(file
, filename
, sizeof file
);
2099 if ((config
= fopen(file
, "r")) == NULL
) {
2100 warn("config_parse: cannot open %s", filename
);
2105 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2106 if (feof(config
) || ferror(config
))
2110 cp
+= (long)strspn(cp
, WS
);
2111 if (cp
[0] == '\0') {
2117 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2118 startpage_add("invalid configuration file entry: %s",
2121 cp
+= (long)strspn(cp
, WS
);
2123 if ((val
= strsep(&cp
, "\0")) == NULL
)
2126 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2127 handled
= settings_add(var
, val
);
2129 startpage_add("invalid configuration file entry: %s=%s",
2139 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2145 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2149 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2152 JSStringGetUTF8CString(jsref
, s
, l
);
2153 JSStringRelease(jsref
);
2159 disable_hints(struct tab
*t
)
2161 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2162 bzero(t
->hint_num
, sizeof t
->hint_num
);
2163 run_script(t
, "vimprobable_clear()");
2165 t
->hint_mode
= XT_HINT_NONE
;
2169 enable_hints(struct tab
*t
)
2171 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2172 run_script(t
, "vimprobable_show_hints()");
2174 t
->hint_mode
= XT_HINT_NONE
;
2177 #define XT_JS_OPEN ("open;")
2178 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2179 #define XT_JS_FIRE ("fire;")
2180 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2181 #define XT_JS_FOUND ("found;")
2182 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2185 run_script(struct tab
*t
, char *s
)
2187 JSGlobalContextRef ctx
;
2188 WebKitWebFrame
*frame
;
2190 JSValueRef val
, exception
;
2193 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2194 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2196 frame
= webkit_web_view_get_main_frame(t
->wv
);
2197 ctx
= webkit_web_frame_get_global_context(frame
);
2199 str
= JSStringCreateWithUTF8CString(s
);
2200 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2201 NULL
, 0, &exception
);
2202 JSStringRelease(str
);
2204 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2206 es
= js_ref_to_string(ctx
, exception
);
2207 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2211 es
= js_ref_to_string(ctx
, val
);
2212 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2214 /* handle return value right here */
2215 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2218 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2221 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2222 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2223 &es
[XT_JS_FIRE_LEN
]);
2228 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2229 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2240 hint(struct tab
*t
, struct karg
*args
)
2243 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2245 if (t
->hints_on
== 0)
2254 apply_style(struct tab
*t
)
2256 g_object_set(G_OBJECT(t
->settings
),
2257 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2261 userstyle(struct tab
*t
, struct karg
*args
)
2263 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2267 g_object_set(G_OBJECT(t
->settings
),
2268 "user-stylesheet-uri", NULL
, (char *)NULL
);
2277 * Doesn't work fully, due to the following bug:
2278 * https://bugs.webkit.org/show_bug.cgi?id=51747
2281 restore_global_history(void)
2283 char file
[PATH_MAX
];
2288 const char delim
[3] = {'\\', '\\', '\0'};
2290 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2292 if ((f
= fopen(file
, "r")) == NULL
) {
2293 warnx("%s: fopen", __func__
);
2298 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2299 if (feof(f
) || ferror(f
))
2302 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2303 if (feof(f
) || ferror(f
)) {
2305 warnx("%s: broken history file\n", __func__
);
2309 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2310 webkit_web_history_item_new_with_data(uri
, title
);
2311 h
= g_malloc(sizeof(struct history
));
2312 h
->uri
= g_strdup(uri
);
2313 h
->title
= g_strdup(title
);
2314 RB_INSERT(history_list
, &hl
, h
);
2315 completion_add_uri(h
->uri
);
2317 warnx("%s: failed to restore history\n", __func__
);
2333 save_global_history_to_disk(struct tab
*t
)
2335 char file
[PATH_MAX
];
2339 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2341 if ((f
= fopen(file
, "w")) == NULL
) {
2342 show_oops(t
, "%s: global history file: %s",
2343 __func__
, strerror(errno
));
2347 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2348 if (h
->uri
&& h
->title
)
2349 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2358 quit(struct tab
*t
, struct karg
*args
)
2360 if (save_global_history
)
2361 save_global_history_to_disk(t
);
2369 open_tabs(struct tab
*t
, struct karg
*a
)
2371 char file
[PATH_MAX
];
2375 struct tab
*ti
, *tt
;
2380 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2381 if ((f
= fopen(file
, "r")) == NULL
)
2384 ti
= TAILQ_LAST(&tabs
, tab_list
);
2387 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2388 if (feof(f
) || ferror(f
))
2391 /* retrieve session name */
2392 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2393 strlcpy(named_session
,
2394 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2395 sizeof named_session
);
2399 if (uri
&& strlen(uri
))
2400 create_new_tab(uri
, NULL
, 1, -1);
2406 /* close open tabs */
2407 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2409 tt
= TAILQ_FIRST(&tabs
);
2429 restore_saved_tabs(void)
2431 char file
[PATH_MAX
];
2432 int unlink_file
= 0;
2437 snprintf(file
, sizeof file
, "%s/%s",
2438 sessions_dir
, XT_RESTART_TABS_FILE
);
2439 if (stat(file
, &sb
) == -1)
2440 a
.s
= XT_SAVED_TABS_FILE
;
2443 a
.s
= XT_RESTART_TABS_FILE
;
2446 a
.i
= XT_SES_DONOTHING
;
2447 rv
= open_tabs(NULL
, &a
);
2456 save_tabs(struct tab
*t
, struct karg
*a
)
2458 char file
[PATH_MAX
];
2460 int num_tabs
= 0, i
;
2461 struct tab
**stabs
= NULL
;
2466 snprintf(file
, sizeof file
, "%s/%s",
2467 sessions_dir
, named_session
);
2469 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2471 if ((f
= fopen(file
, "w")) == NULL
) {
2472 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2476 /* save session name */
2477 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2479 /* Save tabs, in the order they are arranged in the notebook. */
2480 num_tabs
= sort_tabs_by_page_num(&stabs
);
2482 for (i
= 0; i
< num_tabs
; i
++)
2484 if (get_uri(stabs
[i
]) != NULL
)
2485 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2486 else if (gtk_entry_get_text(GTK_ENTRY(
2487 stabs
[i
]->uri_entry
)))
2488 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2489 stabs
[i
]->uri_entry
)));
2494 /* try and make sure this gets to disk NOW. XXX Backup first? */
2495 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2496 show_oops(t
, "May not have managed to save session: %s",
2506 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2518 run_page_script(struct tab
*t
, struct karg
*args
)
2521 char *tmp
, script
[PATH_MAX
];
2523 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2524 if (tmp
[0] == '\0') {
2525 show_oops(t
, "no script specified");
2529 if ((uri
= get_uri(t
)) == NULL
) {
2530 show_oops(t
, "tab is empty, not running script");
2535 snprintf(script
, sizeof script
, "%s/%s",
2536 pwd
->pw_dir
, &tmp
[1]);
2538 strlcpy(script
, tmp
, sizeof script
);
2542 show_oops(t
, "can't fork to run script");
2552 execlp(script
, script
, uri
, (void *)NULL
);
2562 yank_uri(struct tab
*t
, struct karg
*args
)
2565 GtkClipboard
*clipboard
;
2567 if ((uri
= get_uri(t
)) == NULL
)
2570 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2571 gtk_clipboard_set_text(clipboard
, uri
, -1);
2577 paste_uri(struct tab
*t
, struct karg
*args
)
2579 GtkClipboard
*clipboard
;
2580 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2582 gchar
*p
= NULL
, *uri
;
2584 /* try primary clipboard first */
2585 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2586 p
= gtk_clipboard_wait_for_text(clipboard
);
2588 /* if it failed get whatever text is in cut_buffer0 */
2589 if (p
== NULL
&& xterm_workaround
)
2590 if (gdk_property_get(gdk_get_default_root_window(),
2592 gdk_atom_intern("STRING", FALSE
),
2594 1024 * 1024 /* picked out of my butt */,
2600 /* yes sir, we need to NUL the string */
2606 while (*uri
&& isspace(*uri
))
2608 if (strlen(uri
) == 0) {
2609 show_oops(t
, "empty paste buffer");
2612 if (guess_search
== 0 && valid_url_type(uri
)) {
2613 /* we can be clever and paste this in search box */
2614 show_oops(t
, "not a valid URL");
2618 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2620 else if (args
->i
== XT_PASTE_NEW_TAB
)
2621 create_new_tab(uri
, NULL
, 1, -1);
2632 find_domain(const gchar
*s
, int toplevel
)
2640 uri
= soup_uri_new(s
);
2642 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2646 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2647 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2648 while(--p
>= uri
->host
&& *p
!= '.');
2655 if (uri
->port
== 80)
2656 ret
= g_strdup_printf(".%s", p
);
2658 ret
= g_strdup_printf(".%s:%d", p
, uri
->port
);
2666 toggle_cwl(struct tab
*t
, struct karg
*args
)
2677 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2679 if (uri
== NULL
|| dom
== NULL
||
2680 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2681 show_oops(t
, "Can't toggle domain in cookie white list");
2684 d
= wl_find(dom
, &c_wl
);
2691 if (args
->i
& XT_WL_TOGGLE
)
2693 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2695 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2699 /* enable cookies for domain */
2700 wl_add(dom
, &c_wl
, 0);
2702 /* disable cookies for domain */
2703 RB_REMOVE(domain_list
, &c_wl
, d
);
2705 if (args
->i
& XT_WL_RELOAD
)
2706 webkit_web_view_reload(t
->wv
);
2714 toggle_js(struct tab
*t
, struct karg
*args
)
2724 g_object_get(G_OBJECT(t
->settings
),
2725 "enable-scripts", &es
, (char *)NULL
);
2726 if (args
->i
& XT_WL_TOGGLE
)
2728 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2730 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2736 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2738 if (uri
== NULL
|| dom
== NULL
||
2739 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2740 show_oops(t
, "Can't toggle domain in JavaScript white list");
2745 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2746 wl_add(dom
, &js_wl
, 0 /* session */);
2748 d
= wl_find(dom
, &js_wl
);
2750 RB_REMOVE(domain_list
, &js_wl
, d
);
2751 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2753 g_object_set(G_OBJECT(t
->settings
),
2754 "enable-scripts", es
, (char *)NULL
);
2755 g_object_set(G_OBJECT(t
->settings
),
2756 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2757 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2759 if (args
->i
& XT_WL_RELOAD
)
2760 webkit_web_view_reload(t
->wv
);
2768 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2772 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2775 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2780 toggle_src(struct tab
*t
, struct karg
*args
)
2787 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2788 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2789 webkit_web_view_reload(t
->wv
);
2795 focus_webview(struct tab
*t
)
2800 /* only grab focus if we are visible */
2801 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2802 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2806 focus(struct tab
*t
, struct karg
*args
)
2808 if (t
== NULL
|| args
== NULL
)
2814 if (args
->i
== XT_FOCUS_URI
)
2815 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2816 else if (args
->i
== XT_FOCUS_SEARCH
)
2817 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2823 stats(struct tab
*t
, struct karg
*args
)
2825 char *page
, *body
, *s
, line
[64 * 1024];
2826 uint64_t line_count
= 0;
2830 show_oops(NULL
, "stats invalid parameters");
2833 if (save_rejected_cookies
) {
2834 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2836 s
= fgets(line
, sizeof line
, r_cookie_f
);
2837 if (s
== NULL
|| feof(r_cookie_f
) ||
2843 snprintf(line
, sizeof line
,
2844 "<br/>Cookies blocked(*) total: %llu", line_count
);
2846 show_oops(t
, "Can't open blocked cookies file: %s",
2850 body
= g_strdup_printf(
2851 "Cookies blocked(*) this session: %llu"
2853 "<p><small><b>*</b> results vary based on settings</small></p>",
2857 page
= get_html_page("Statistics", body
, "", 0);
2860 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2867 marco(struct tab
*t
, struct karg
*args
)
2869 char *page
, line
[64 * 1024];
2873 show_oops(NULL
, "marco invalid parameters");
2876 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2878 page
= get_html_page("Marco Sez...", line
, "", 0);
2880 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2887 blank(struct tab
*t
, struct karg
*args
)
2890 show_oops(NULL
, "blank invalid parameters");
2892 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2898 about(struct tab
*t
, struct karg
*args
)
2903 show_oops(NULL
, "about invalid parameters");
2905 body
= g_strdup_printf("<b>Version: %s</b><p>"
2908 "<li>Marco Peereboom <marco@peereboom.us></li>"
2909 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2910 "<li>Edd Barrett <vext01@gmail.com> </li>"
2911 "<li>Todd T. Fries <todd@fries.net> </li>"
2912 "<li>Raphael Graf <r@undefined.ch> </li>"
2914 "Copyrights and licenses can be found on the XXXTerm "
2915 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2919 page
= get_html_page("About", body
, "", 0);
2922 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2929 help(struct tab
*t
, struct karg
*args
)
2931 char *page
, *head
, *body
;
2934 show_oops(NULL
, "help invalid parameters");
2936 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2937 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2939 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
2940 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2941 "cgi-bin/man-cgi?xxxterm</a>";
2943 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
2945 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2952 startpage(struct tab
*t
, struct karg
*args
)
2954 char *page
, *body
, *b
;
2958 show_oops(NULL
, "startpage invalid parameters");
2960 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
2962 TAILQ_FOREACH(s
, &spl
, entry
) {
2964 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
2968 page
= get_html_page("Startup Exception", body
, "", 0);
2971 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
2978 startpage_add(const char *fmt
, ...)
2988 if (vasprintf(&msg
, fmt
, ap
) == -1)
2989 errx(1, "startpage_add failed");
2992 s
= g_malloc0(sizeof *s
);
2995 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
2999 * update all favorite tabs apart from one. Pass NULL if
3000 * you want to update all.
3003 update_favorite_tabs(struct tab
*apart_from
)
3006 if (!updating_fl_tabs
) {
3007 updating_fl_tabs
= 1; /* stop infinite recursion */
3008 TAILQ_FOREACH(t
, &tabs
, entry
)
3009 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3010 && (t
!= apart_from
))
3011 xtp_page_fl(t
, NULL
);
3012 updating_fl_tabs
= 0;
3016 /* show a list of favorites (bookmarks) */
3018 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3020 char file
[PATH_MAX
];
3022 char *uri
= NULL
, *title
= NULL
;
3023 size_t len
, lineno
= 0;
3025 char *body
, *tmp
, *page
= NULL
;
3026 const char delim
[3] = {'\\', '\\', '\0'};
3028 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3031 warn("%s: bad param", __func__
);
3033 /* new session key */
3034 if (!updating_fl_tabs
)
3035 generate_xtp_session_key(&fl_session_key
);
3037 /* open favorites */
3038 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3039 if ((f
= fopen(file
, "r")) == NULL
) {
3040 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3045 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3046 "<th style='width: 40px'>#</th><th>Link</th>"
3047 "<th style='width: 40px'>Rm</th></tr>\n");
3050 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3051 if (feof(f
) || ferror(f
))
3053 if (strlen(title
) == 0 || title
[0] == '#') {
3059 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3060 if (feof(f
) || ferror(f
)) {
3061 show_oops(t
, "favorites file corrupt");
3067 body
= g_strdup_printf("%s<tr>"
3069 "<td><a href='%s'>%s</a></td>"
3070 "<td style='text-align: center'>"
3071 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3073 body
, i
, uri
, title
,
3074 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3086 /* if none, say so */
3089 body
= g_strdup_printf("%s<tr>"
3090 "<td colspan='3' style='text-align: center'>"
3091 "No favorites - To add one use the 'favadd' command."
3092 "</td></tr>", body
);
3097 body
= g_strdup_printf("%s</table>", body
);
3107 page
= get_html_page("Favorites", body
, "", 1);
3108 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3112 update_favorite_tabs(t
);
3121 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3122 size_t cert_count
, char *title
)
3124 gnutls_datum_t cinfo
;
3128 body
= g_strdup("");
3130 for (i
= 0; i
< cert_count
; i
++) {
3131 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3136 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3137 body
, i
, cinfo
.data
);
3138 gnutls_free(cinfo
.data
);
3142 tmp
= get_html_page(title
, body
, "", 0);
3145 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3150 ca_cmd(struct tab
*t
, struct karg
*args
)
3153 int rv
= 1, certs
= 0, certs_read
;
3156 gnutls_x509_crt_t
*c
= NULL
;
3157 char *certs_buf
= NULL
, *s
;
3159 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3160 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3164 if (fstat(fileno(f
), &sb
) == -1) {
3165 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3169 certs_buf
= g_malloc(sb
.st_size
+ 1);
3170 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3171 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3174 certs_buf
[sb
.st_size
] = '\0';
3177 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3179 s
+= strlen("BEGIN CERTIFICATE");
3182 bzero(&dt
, sizeof dt
);
3183 dt
.data
= (unsigned char *)certs_buf
;
3184 dt
.size
= sb
.st_size
;
3185 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3186 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3187 GNUTLS_X509_FMT_PEM
, 0);
3188 if (certs_read
<= 0) {
3189 show_oops(t
, "No cert(s) available");
3192 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3205 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3209 struct addrinfo hints
, *res
= NULL
, *ai
;
3210 int rv
= -1, s
= -1, on
, error
;
3213 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3214 show_oops(t
, "invalid URI");
3218 su
= soup_uri_new(uri
);
3220 show_oops(t
, "invalid soup URI");
3223 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3224 show_oops(t
, "invalid HTTPS URI");
3228 snprintf(port
, sizeof port
, "%d", su
->port
);
3229 bzero(&hints
, sizeof(struct addrinfo
));
3230 hints
.ai_flags
= AI_CANONNAME
;
3231 hints
.ai_family
= AF_UNSPEC
;
3232 hints
.ai_socktype
= SOCK_STREAM
;
3234 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3235 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3239 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3240 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3243 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3245 show_oops(t
, "socket failed: %s", strerror(errno
));
3248 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3249 sizeof(on
)) == -1) {
3250 show_oops(t
, "setsockopt failed: %s", strerror(errno
));
3253 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == -1) {
3254 show_oops(t
, "connect failed: %s", strerror(errno
));
3262 strlcpy(domain
, su
->host
, domain_sz
);
3269 if (rv
== -1 && s
!= -1)
3276 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3279 gnutls_deinit(gsession
);
3281 gnutls_certificate_free_credentials(xcred
);
3287 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3288 gnutls_certificate_credentials_t
*xc
)
3290 gnutls_certificate_credentials_t xcred
;
3291 gnutls_session_t gsession
;
3294 if (gs
== NULL
|| xc
== NULL
)
3300 gnutls_certificate_allocate_credentials(&xcred
);
3301 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3302 GNUTLS_X509_FMT_PEM
);
3304 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3305 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3306 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3307 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3308 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3309 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3311 gnutls_error_is_fatal(rv
),
3312 gnutls_strerror_name(rv
));
3313 stop_tls(gsession
, xcred
);
3317 gnutls_credentials_type_t cred
;
3318 cred
= gnutls_auth_get_type(gsession
);
3319 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3320 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3321 stop_tls(gsession
, xcred
);
3333 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3337 const gnutls_datum_t
*cl
;
3338 gnutls_x509_crt_t
*all_certs
;
3341 if (certs
== NULL
|| cert_count
== NULL
)
3343 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3345 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3349 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3350 for (i
= 0; i
< len
; i
++) {
3351 gnutls_x509_crt_init(&all_certs
[i
]);
3352 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3353 GNUTLS_X509_FMT_PEM
< 0)) {
3367 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3371 for (i
= 0; i
< cert_count
; i
++)
3372 gnutls_x509_crt_deinit(certs
[i
]);
3377 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3379 GdkColor c_text
, c_base
;
3381 gdk_color_parse(text
, &c_text
);
3382 gdk_color_parse(base
, &c_base
);
3384 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3385 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3386 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3387 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3389 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3390 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3391 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3392 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3396 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3397 size_t cert_count
, char *domain
)
3400 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3405 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3408 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3409 if ((f
= fopen(file
, "w")) == NULL
) {
3410 show_oops(t
, "Can't create cert file %s %s",
3411 file
, strerror(errno
));
3415 for (i
= 0; i
< cert_count
; i
++) {
3416 cert_buf_sz
= sizeof cert_buf
;
3417 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3418 cert_buf
, &cert_buf_sz
)) {
3419 show_oops(t
, "gnutls_x509_crt_export failed");
3422 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3423 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3428 /* not the best spot but oh well */
3429 gdk_color_parse("lightblue", &color
);
3430 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3431 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3444 load_compare_cert(struct tab
*t
, struct karg
*args
)
3447 char domain
[8182], file
[PATH_MAX
];
3448 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3449 int s
= -1, i
, error
;
3451 size_t cert_buf_sz
, cert_count
;
3452 enum cert_trust rv
= CERT_UNTRUSTED
;
3454 gnutls_session_t gsession
;
3455 gnutls_x509_crt_t
*certs
;
3456 gnutls_certificate_credentials_t xcred
;
3458 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3463 if ((uri
= get_uri(t
)) == NULL
)
3465 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3467 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3469 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3472 if (start_tls(t
, s
, &gsession
, &xcred
))
3474 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3476 /* verify certs in case cert file doesn't exist */
3477 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3479 show_oops(t
, "Invalid certificates");
3484 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3485 show_oops(t
, "Can't get connection certificates");
3489 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3490 if ((f
= fopen(file
, "r")) == NULL
) {
3496 for (i
= 0; i
< cert_count
; i
++) {
3497 cert_buf_sz
= sizeof cert_buf
;
3498 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3499 cert_buf
, &cert_buf_sz
)) {
3502 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3503 rv
= CERT_BAD
; /* critical */
3506 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3507 rv
= CERT_BAD
; /* critical */
3516 free_connection_certs(certs
, cert_count
);
3518 /* we close the socket first for speed */
3522 /* only complain if we didn't save it locally */
3523 if (error
&& rv
!= CERT_LOCAL
) {
3524 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3525 if (error
& GNUTLS_CERT_INVALID
)
3526 strlcat(serr
, "invalid, ", sizeof serr
);
3527 if (error
& GNUTLS_CERT_REVOKED
)
3528 strlcat(serr
, "revoked, ", sizeof serr
);
3529 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3530 strlcat(serr
, "signer not found, ", sizeof serr
);
3531 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3532 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3533 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3534 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3535 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3536 strlcat(serr
, "not activated, ", sizeof serr
);
3537 if (error
& GNUTLS_CERT_EXPIRED
)
3538 strlcat(serr
, "expired, ", sizeof serr
);
3539 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3540 if (serr
[i
] == ',') {
3547 stop_tls(gsession
, xcred
);
3553 cert_cmd(struct tab
*t
, struct karg
*args
)
3559 gnutls_session_t gsession
;
3560 gnutls_x509_crt_t
*certs
;
3561 gnutls_certificate_credentials_t xcred
;
3566 if (ssl_ca_file
== NULL
) {
3567 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3571 if ((uri
= get_uri(t
)) == NULL
) {
3572 show_oops(t
, "Invalid URI");
3576 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3577 show_oops(t
, "Invalid certificate URI: %s", uri
);
3582 if (start_tls(t
, s
, &gsession
, &xcred
))
3586 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3587 show_oops(t
, "get_connection_certs failed");
3591 if (args
->i
& XT_SHOW
)
3592 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3593 else if (args
->i
& XT_SAVE
)
3594 save_certs(t
, certs
, cert_count
, domain
);
3596 free_connection_certs(certs
, cert_count
);
3598 /* we close the socket first for speed */
3601 stop_tls(gsession
, xcred
);
3607 remove_cookie(int index
)
3613 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3615 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3617 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3621 print_cookie("remove cookie", c
);
3622 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3627 soup_cookies_free(cf
);
3633 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3638 body
= g_strdup("");
3641 if (args
->i
& XT_WL_PERSISTENT
) {
3643 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3645 RB_FOREACH(d
, domain_list
, wl
) {
3649 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3655 if (args
->i
& XT_WL_SESSION
) {
3657 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3659 RB_FOREACH(d
, domain_list
, wl
) {
3663 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3668 tmp
= get_html_page(title
, body
, "", 0);
3671 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3673 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3679 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3681 char file
[PATH_MAX
];
3683 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3691 if (t
== NULL
|| args
== NULL
)
3694 if (runtime_settings
[0] == '\0')
3697 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3698 if ((f
= fopen(file
, "r+")) == NULL
)
3702 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3703 if (uri
== NULL
|| dom
== NULL
||
3704 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3705 show_oops(t
, "Can't add domain to %s white list",
3706 js
? "JavaScript" : "cookie");
3710 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3713 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3716 if (!strcmp(line
, lt
))
3722 fprintf(f
, "%s\n", lt
);
3727 d
= wl_find(dom
, &js_wl
);
3729 settings_add("js_wl", dom
);
3730 d
= wl_find(dom
, &js_wl
);
3734 d
= wl_find(dom
, &c_wl
);
3736 settings_add("cookie_wl", dom
);
3737 d
= wl_find(dom
, &c_wl
);
3741 /* find and add to persistent jar */
3742 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3743 for (;cf
; cf
= cf
->next
) {
3745 if (!strcmp(dom
, ci
->domain
) ||
3746 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3747 c
= soup_cookie_copy(ci
);
3748 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3751 soup_cookies_free(cf
);
3769 js_show_wl(struct tab
*t
, struct karg
*args
)
3771 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3772 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3778 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3780 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3781 wl_show(t
, args
, "Cookie White List", &c_wl
);
3787 cookie_cmd(struct tab
*t
, struct karg
*args
)
3789 if (args
->i
& XT_SHOW
)
3790 wl_show(t
, args
, "Cookie White List", &c_wl
);
3791 else if (args
->i
& XT_WL_TOGGLE
) {
3792 args
->i
|= XT_WL_RELOAD
;
3793 toggle_cwl(t
, args
);
3794 } else if (args
->i
& XT_SAVE
) {
3795 args
->i
|= XT_WL_RELOAD
;
3796 wl_save(t
, args
, 0);
3797 } else if (args
->i
& XT_DELETE
)
3798 show_oops(t
, "'cookie delete' currently unimplemented");
3804 js_cmd(struct tab
*t
, struct karg
*args
)
3806 if (args
->i
& XT_SHOW
)
3807 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3808 else if (args
->i
& XT_SAVE
) {
3809 args
->i
|= XT_WL_RELOAD
;
3810 wl_save(t
, args
, 1);
3811 } else if (args
->i
& XT_WL_TOGGLE
) {
3812 args
->i
|= XT_WL_RELOAD
;
3814 } else if (args
->i
& XT_DELETE
)
3815 show_oops(t
, "'js delete' currently unimplemented");
3821 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3823 js_toggle_cb(t
->js_toggle
, t
);
3829 add_favorite(struct tab
*t
, struct karg
*args
)
3831 char file
[PATH_MAX
];
3834 size_t urilen
, linelen
;
3835 const gchar
*uri
, *title
;
3840 /* don't allow adding of xtp pages to favorites */
3841 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3842 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3846 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3847 if ((f
= fopen(file
, "r+")) == NULL
) {
3848 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3852 title
= get_title(t
, FALSE
);
3855 if (title
== NULL
|| uri
== NULL
) {
3856 show_oops(t
, "can't add page to favorites");
3860 urilen
= strlen(uri
);
3863 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3864 if (feof(f
) || ferror(f
))
3867 if (linelen
== urilen
&& !strcmp(line
, uri
))
3874 fprintf(f
, "\n%s\n%s", title
, uri
);
3880 update_favorite_tabs(NULL
);
3886 navaction(struct tab
*t
, struct karg
*args
)
3888 WebKitWebHistoryItem
*item
;
3890 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3891 t
->tab_id
, args
->i
);
3893 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3896 if (args
->i
== XT_NAV_BACK
)
3897 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3899 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3901 return (XT_CB_PASSTHROUGH
);
3902 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3904 return (XT_CB_PASSTHROUGH
);
3910 item
= webkit_web_back_forward_list_get_back_item(t
->bfl
);
3912 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3914 case XT_NAV_FORWARD
:
3916 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3918 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3921 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3923 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3926 return (XT_CB_PASSTHROUGH
);
3930 move(struct tab
*t
, struct karg
*args
)
3932 GtkAdjustment
*adjust
;
3933 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3939 case XT_MOVE_BOTTOM
:
3941 case XT_MOVE_PAGEDOWN
:
3942 case XT_MOVE_PAGEUP
:
3943 case XT_MOVE_HALFDOWN
:
3944 case XT_MOVE_HALFUP
:
3945 case XT_MOVE_PERCENT
:
3946 adjust
= t
->adjust_v
;
3949 adjust
= t
->adjust_h
;
3953 pos
= gtk_adjustment_get_value(adjust
);
3954 ps
= gtk_adjustment_get_page_size(adjust
);
3955 upper
= gtk_adjustment_get_upper(adjust
);
3956 lower
= gtk_adjustment_get_lower(adjust
);
3957 si
= gtk_adjustment_get_step_increment(adjust
);
3958 pi
= gtk_adjustment_get_page_increment(adjust
);
3961 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3962 "max %f si %f pi %f\n",
3963 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3964 pos
, ps
, upper
, lower
, max
, si
, pi
);
3970 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3975 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3977 case XT_MOVE_BOTTOM
:
3978 case XT_MOVE_FARRIGHT
:
3979 gtk_adjustment_set_value(adjust
, max
);
3982 case XT_MOVE_FARLEFT
:
3983 gtk_adjustment_set_value(adjust
, lower
);
3985 case XT_MOVE_PAGEDOWN
:
3987 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3989 case XT_MOVE_PAGEUP
:
3991 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3993 case XT_MOVE_HALFDOWN
:
3995 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3997 case XT_MOVE_HALFUP
:
3999 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4001 case XT_MOVE_PERCENT
:
4002 percent
= atoi(args
->s
) / 100.0;
4003 pos
= max
* percent
;
4004 if (pos
< 0.0 || pos
> max
)
4006 gtk_adjustment_set_value(adjust
, pos
);
4009 return (XT_CB_PASSTHROUGH
);
4012 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4014 return (XT_CB_HANDLED
);
4018 url_set_visibility(void)
4022 TAILQ_FOREACH(t
, &tabs
, entry
)
4023 if (show_url
== 0) {
4024 gtk_widget_hide(t
->toolbar
);
4027 gtk_widget_show(t
->toolbar
);
4031 notebook_tab_set_visibility(void)
4033 if (show_tabs
== 0) {
4034 gtk_widget_hide(tab_bar
);
4035 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4037 if (tab_style
== XT_TABS_NORMAL
) {
4038 gtk_widget_hide(tab_bar
);
4039 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4040 } else if (tab_style
== XT_TABS_COMPACT
) {
4041 gtk_widget_show(tab_bar
);
4042 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4048 statusbar_set_visibility(void)
4052 TAILQ_FOREACH(t
, &tabs
, entry
)
4053 if (show_statusbar
== 0) {
4054 gtk_widget_hide(t
->statusbar_box
);
4057 gtk_widget_show(t
->statusbar_box
);
4061 url_set(struct tab
*t
, int enable_url_entry
)
4066 show_url
= enable_url_entry
;
4068 if (enable_url_entry
) {
4069 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4070 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4071 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4073 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4074 GTK_ENTRY_ICON_PRIMARY
);
4076 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4077 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4078 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4079 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4085 fullscreen(struct tab
*t
, struct karg
*args
)
4087 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4090 return (XT_CB_PASSTHROUGH
);
4092 if (show_url
== 0) {
4100 url_set_visibility();
4101 notebook_tab_set_visibility();
4103 return (XT_CB_HANDLED
);
4107 statustoggle(struct tab
*t
, struct karg
*args
)
4109 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4111 if (show_statusbar
== 1) {
4113 statusbar_set_visibility();
4114 } else if (show_statusbar
== 0) {
4116 statusbar_set_visibility();
4118 return (XT_CB_HANDLED
);
4122 urlaction(struct tab
*t
, struct karg
*args
)
4124 int rv
= XT_CB_HANDLED
;
4126 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4129 return (XT_CB_PASSTHROUGH
);
4133 if (show_url
== 0) {
4135 url_set_visibility();
4139 if (show_url
== 1) {
4141 url_set_visibility();
4149 tabaction(struct tab
*t
, struct karg
*args
)
4151 int rv
= XT_CB_HANDLED
;
4152 char *url
= args
->s
;
4156 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4159 return (XT_CB_PASSTHROUGH
);
4163 if (strlen(url
) > 0)
4164 create_new_tab(url
, NULL
, 1, args
->precount
);
4166 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4169 if (args
->precount
< 0)
4172 TAILQ_FOREACH(tt
, &tabs
, entry
)
4173 if (tt
->tab_id
== args
->precount
- 1) {
4178 case XT_TAB_DELQUIT
:
4179 if (gtk_notebook_get_n_pages(notebook
) > 1)
4185 if (strlen(url
) > 0)
4188 rv
= XT_CB_PASSTHROUGH
;
4194 if (show_tabs
== 0) {
4196 notebook_tab_set_visibility();
4200 if (show_tabs
== 1) {
4202 notebook_tab_set_visibility();
4205 case XT_TAB_NEXTSTYLE
:
4206 if (tab_style
== XT_TABS_NORMAL
) {
4207 tab_style
= XT_TABS_COMPACT
;
4208 recolor_compact_tabs();
4211 tab_style
= XT_TABS_NORMAL
;
4212 notebook_tab_set_visibility();
4214 case XT_TAB_UNDO_CLOSE
:
4215 if (undo_count
== 0) {
4216 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4221 u
= TAILQ_FIRST(&undos
);
4222 create_new_tab(u
->uri
, u
, 1, -1);
4224 TAILQ_REMOVE(&undos
, u
, entry
);
4226 /* u->history is freed in create_new_tab() */
4231 rv
= XT_CB_PASSTHROUGH
;
4245 resizetab(struct tab
*t
, struct karg
*args
)
4247 if (t
== NULL
|| args
== NULL
) {
4248 show_oops(NULL
, "resizetab invalid parameters");
4249 return (XT_CB_PASSTHROUGH
);
4252 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4253 t
->tab_id
, args
->i
);
4255 setzoom_webkit(t
, args
->i
);
4257 return (XT_CB_HANDLED
);
4261 movetab(struct tab
*t
, struct karg
*args
)
4265 if (t
== NULL
|| args
== NULL
) {
4266 show_oops(NULL
, "movetab invalid parameters");
4267 return (XT_CB_PASSTHROUGH
);
4270 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4271 t
->tab_id
, args
->i
);
4273 if (args
->i
>= XT_TAB_INVALID
)
4274 return (XT_CB_PASSTHROUGH
);
4276 if (TAILQ_EMPTY(&tabs
))
4277 return (XT_CB_PASSTHROUGH
);
4279 n
= gtk_notebook_get_n_pages(notebook
);
4280 dest
= gtk_notebook_get_current_page(notebook
);
4284 if (args
->precount
< 0)
4285 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4287 dest
= args
->precount
- 1;
4291 if (args
->precount
< 0)
4294 dest
-= args
->precount
% n
;
4307 return (XT_CB_PASSTHROUGH
);
4310 if (dest
< 0 || dest
>= n
)
4311 return (XT_CB_PASSTHROUGH
);
4312 if (t
->tab_id
== dest
) {
4313 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4314 return (XT_CB_HANDLED
);
4317 set_current_tab(dest
);
4319 return (XT_CB_HANDLED
);
4325 command(struct tab
*t
, struct karg
*args
)
4327 char *s
= NULL
, *ss
= NULL
;
4331 if (t
== NULL
|| args
== NULL
) {
4332 show_oops(NULL
, "command invalid parameters");
4333 return (XT_CB_PASSTHROUGH
);
4344 if (cmd_prefix
== 0)
4347 ss
= g_strdup_printf(":%d", cmd_prefix
);
4358 case XT_CMD_OPEN_CURRENT
:
4361 case XT_CMD_TABNEW_CURRENT
:
4362 if (!s
) /* FALL THROUGH? */
4364 if ((uri
= get_uri(t
)) != NULL
) {
4365 ss
= g_strdup_printf("%s%s", s
, uri
);
4370 show_oops(t
, "command: invalid opcode %d", args
->i
);
4371 return (XT_CB_PASSTHROUGH
);
4374 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4376 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4377 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4378 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4380 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4381 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4386 return (XT_CB_HANDLED
);
4390 * Return a new string with a download row (in html)
4391 * appended. Old string is freed.
4394 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4397 WebKitDownloadStatus stat
;
4398 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4400 char cur_sz
[FMT_SCALED_STRSIZE
];
4401 char tot_sz
[FMT_SCALED_STRSIZE
];
4404 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4406 /* All actions wil take this form:
4407 * xxxt://class/seskey
4409 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4410 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4412 stat
= webkit_download_get_status(dl
->download
);
4415 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4416 status_html
= g_strdup_printf("Finished");
4417 cmd_html
= g_strdup_printf(
4418 "<a href='%s%d/%d'>Remove</a>",
4419 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4421 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4422 /* gather size info */
4423 progress
= 100 * webkit_download_get_progress(dl
->download
);
4426 webkit_download_get_current_size(dl
->download
), cur_sz
);
4428 webkit_download_get_total_size(dl
->download
), tot_sz
);
4430 status_html
= g_strdup_printf(
4431 "<div style='width: 100%%' align='center'>"
4432 "<div class='progress-outer'>"
4433 "<div class='progress-inner' style='width: %.2f%%'>"
4434 "</div></div></div>"
4435 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4436 progress
, cur_sz
, tot_sz
, progress
);
4438 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4439 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4443 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4444 status_html
= g_strdup_printf("Cancelled");
4445 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4446 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4448 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4449 status_html
= g_strdup_printf("Error!");
4450 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4451 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4453 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4454 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4455 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4456 status_html
= g_strdup_printf("Starting");
4459 show_oops(t
, "%s: unknown download status", __func__
);
4462 new_html
= g_strdup_printf(
4463 "%s\n<tr><td>%s</td><td>%s</td>"
4464 "<td style='text-align:center'>%s</td></tr>\n",
4465 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4466 status_html
, cmd_html
);
4470 g_free(status_html
);
4481 * update all download tabs apart from one. Pass NULL if
4482 * you want to update all.
4485 update_download_tabs(struct tab
*apart_from
)
4488 if (!updating_dl_tabs
) {
4489 updating_dl_tabs
= 1; /* stop infinite recursion */
4490 TAILQ_FOREACH(t
, &tabs
, entry
)
4491 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4492 && (t
!= apart_from
))
4493 xtp_page_dl(t
, NULL
);
4494 updating_dl_tabs
= 0;
4499 * update all cookie tabs apart from one. Pass NULL if
4500 * you want to update all.
4503 update_cookie_tabs(struct tab
*apart_from
)
4506 if (!updating_cl_tabs
) {
4507 updating_cl_tabs
= 1; /* stop infinite recursion */
4508 TAILQ_FOREACH(t
, &tabs
, entry
)
4509 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4510 && (t
!= apart_from
))
4511 xtp_page_cl(t
, NULL
);
4512 updating_cl_tabs
= 0;
4517 * update all history tabs apart from one. Pass NULL if
4518 * you want to update all.
4521 update_history_tabs(struct tab
*apart_from
)
4525 if (!updating_hl_tabs
) {
4526 updating_hl_tabs
= 1; /* stop infinite recursion */
4527 TAILQ_FOREACH(t
, &tabs
, entry
)
4528 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4529 && (t
!= apart_from
))
4530 xtp_page_hl(t
, NULL
);
4531 updating_hl_tabs
= 0;
4535 /* cookie management XTP page */
4537 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4539 char *body
, *page
, *tmp
;
4540 int i
= 1; /* all ids start 1 */
4541 GSList
*sc
, *pc
, *pc_start
;
4543 char *type
, *table_headers
, *last_domain
;
4545 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4548 show_oops(NULL
, "%s invalid parameters", __func__
);
4552 /* Generate a new session key */
4553 if (!updating_cl_tabs
)
4554 generate_xtp_session_key(&cl_session_key
);
4557 table_headers
= g_strdup_printf("<table><tr>"
4560 "<th style='width:200px'>Value</th>"
4564 "<th>HTTP<br />only</th>"
4565 "<th style='width:40px'>Rm</th></tr>\n");
4567 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4568 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4572 last_domain
= strdup("");
4573 for (; sc
; sc
= sc
->next
) {
4576 if (strcmp(last_domain
, c
->domain
) != 0) {
4579 last_domain
= strdup(c
->domain
);
4583 body
= g_strdup_printf("%s</table>"
4585 body
, c
->domain
, table_headers
);
4589 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4590 c
->domain
, table_headers
);
4595 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4596 if (soup_cookie_equal(pc
->data
, c
)) {
4597 type
= "Session + Persistent";
4602 body
= g_strdup_printf(
4605 "<td style='word-wrap:normal'>%s</td>"
4607 " <textarea rows='4'>%s</textarea>"
4613 "<td style='text-align:center'>"
4614 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4621 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4636 soup_cookies_free(sc
);
4637 soup_cookies_free(pc
);
4639 /* small message if there are none */
4641 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4642 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4645 body
= g_strdup_printf("%s</table>", body
);
4648 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4650 g_free(table_headers
);
4651 g_free(last_domain
);
4653 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4654 update_cookie_tabs(t
);
4662 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4664 char *body
, *page
, *tmp
;
4666 int i
= 1; /* all ids start 1 */
4668 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4671 show_oops(NULL
, "%s invalid parameters", __func__
);
4675 /* Generate a new session key */
4676 if (!updating_hl_tabs
)
4677 generate_xtp_session_key(&hl_session_key
);
4680 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4681 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4683 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4685 body
= g_strdup_printf(
4687 "<td><a href='%s'>%s</a></td>"
4689 "<td style='text-align: center'>"
4690 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4691 body
, h
->uri
, h
->uri
, h
->title
,
4692 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4693 XT_XTP_HL_REMOVE
, i
);
4699 /* small message if there are none */
4702 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4703 "colspan='3'>No History</td></tr>\n", body
);
4708 body
= g_strdup_printf("%s</table>", body
);
4711 page
= get_html_page("History", body
, "", TRUE
);
4715 * update all history manager tabs as the xtp session
4716 * key has now changed. No need to update the current tab.
4717 * Already did that above.
4719 update_history_tabs(t
);
4721 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4728 * Generate a web page detailing the status of any downloads
4731 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4733 struct download
*dl
;
4734 char *body
, *page
, *tmp
;
4738 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4741 show_oops(NULL
, "%s invalid parameters", __func__
);
4746 * Generate a new session key for next page instance.
4747 * This only happens for the top level call to xtp_page_dl()
4748 * in which case updating_dl_tabs is 0.
4750 if (!updating_dl_tabs
)
4751 generate_xtp_session_key(&dl_session_key
);
4753 /* header - with refresh so as to update */
4754 if (refresh_interval
>= 1)
4755 ref
= g_strdup_printf(
4756 "<meta http-equiv='refresh' content='%u"
4757 ";url=%s%d/%s/%d' />\n",
4766 body
= g_strdup_printf("<div align='center'>"
4767 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4768 "</p><table><tr><th style='width: 60%%'>"
4769 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4770 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4772 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4773 body
= xtp_page_dl_row(t
, body
, dl
);
4777 /* message if no downloads in list */
4780 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4781 " style='text-align: center'>"
4782 "No downloads</td></tr>\n", body
);
4787 body
= g_strdup_printf("%s</table></div>", body
);
4790 page
= get_html_page("Downloads", body
, ref
, 1);
4795 * update all download manager tabs as the xtp session
4796 * key has now changed. No need to update the current tab.
4797 * Already did that above.
4799 update_download_tabs(t
);
4801 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4808 search(struct tab
*t
, struct karg
*args
)
4812 if (t
== NULL
|| args
== NULL
) {
4813 show_oops(NULL
, "search invalid parameters");
4816 if (t
->search_text
== NULL
) {
4817 if (global_search
== NULL
)
4818 return (XT_CB_PASSTHROUGH
);
4820 t
->search_text
= g_strdup(global_search
);
4821 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4822 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4826 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4827 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4830 case XT_SEARCH_NEXT
:
4831 d
= t
->search_forward
;
4833 case XT_SEARCH_PREV
:
4834 d
= !t
->search_forward
;
4837 return (XT_CB_PASSTHROUGH
);
4840 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4842 return (XT_CB_HANDLED
);
4845 struct settings_args
{
4851 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4854 struct settings_args
*sa
= cb_args
;
4859 if (s
->flags
& XT_SF_RUNTIME
)
4865 *sa
->body
= g_strdup_printf(
4867 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4868 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4880 set_show(struct tab
*t
, struct karg
*args
)
4882 char *body
, *page
, *tmp
;
4884 struct settings_args sa
;
4886 bzero(&sa
, sizeof sa
);
4890 body
= g_strdup_printf("<div align='center'><table><tr>"
4891 "<th align='left'>Setting</th>"
4892 "<th align='left'>Value</th></tr>\n");
4894 settings_walk(print_setting
, &sa
);
4897 /* small message if there are none */
4900 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4901 "colspan='2'>No settings</td></tr>\n", body
);
4906 body
= g_strdup_printf("%s</table></div>", body
);
4909 page
= get_html_page("Settings", body
, "", 0);
4913 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4917 return (XT_CB_PASSTHROUGH
);
4921 set(struct tab
*t
, struct karg
*args
)
4926 if (args
== NULL
|| args
->s
== NULL
)
4927 return (set_show(t
, args
));
4930 p
= g_strstrip(args
->s
);
4933 return (set_show(t
, args
));
4935 /* we got some sort of string */
4936 val
= g_strrstr(p
, "=");
4939 val
= g_strchomp(val
);
4942 for (i
= 0; i
< LENGTH(rs
); i
++) {
4943 if (strcmp(rs
[i
].name
, p
))
4946 if (rs
[i
].activate
) {
4947 if (rs
[i
].activate(val
))
4948 show_oops(t
, "%s invalid value %s",
4951 show_oops(t
, ":set %s = %s", p
, val
);
4954 show_oops(t
, "not a runtime option: %s", p
);
4958 show_oops(t
, "unknown option: %s", p
);
4962 for (i
= 0; i
< LENGTH(rs
); i
++) {
4963 if (strcmp(rs
[i
].name
, p
))
4966 /* XXX this could use some cleanup */
4967 switch (rs
[i
].type
) {
4970 show_oops(t
, "%s = %d",
4971 rs
[i
].name
, *rs
[i
].ival
);
4972 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4973 show_oops(t
, "%s = %s",
4975 rs
[i
].s
->get(&rs
[i
]));
4976 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4977 show_oops(t
, "%s = ...", rs
[i
].name
);
4979 show_oops(t
, "%s = ", rs
[i
].name
);
4983 show_oops(t
, "%s = %f",
4984 rs
[i
].name
, *rs
[i
].fval
);
4985 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4986 show_oops(t
, "%s = %s",
4988 rs
[i
].s
->get(&rs
[i
]));
4989 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4990 show_oops(t
, "%s = ...", rs
[i
].name
);
4992 show_oops(t
, "%s = ", rs
[i
].name
);
4995 if (rs
[i
].sval
&& *rs
[i
].sval
)
4996 show_oops(t
, "%s = %s",
4997 rs
[i
].name
, *rs
[i
].sval
);
4998 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4999 show_oops(t
, "%s = %s",
5001 rs
[i
].s
->get(&rs
[i
]));
5002 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5003 show_oops(t
, "%s = ...", rs
[i
].name
);
5005 show_oops(t
, "%s = ", rs
[i
].name
);
5008 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5014 show_oops(t
, "unknown option: %s", p
);
5017 return (XT_CB_PASSTHROUGH
);
5021 session_save(struct tab
*t
, char *filename
)
5026 if (strlen(filename
) == 0)
5029 if (filename
[0] == '.' || filename
[0] == '/')
5033 if (save_tabs(t
, &a
))
5035 strlcpy(named_session
, filename
, sizeof named_session
);
5043 session_open(struct tab
*t
, char *filename
)
5048 if (strlen(filename
) == 0)
5051 if (filename
[0] == '.' || filename
[0] == '/')
5055 a
.i
= XT_SES_CLOSETABS
;
5056 if (open_tabs(t
, &a
))
5059 strlcpy(named_session
, filename
, sizeof named_session
);
5067 session_delete(struct tab
*t
, char *filename
)
5069 char file
[PATH_MAX
];
5072 if (strlen(filename
) == 0)
5075 if (filename
[0] == '.' || filename
[0] == '/')
5078 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5082 if (!strcmp(filename
, named_session
))
5083 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5084 sizeof named_session
);
5092 session_cmd(struct tab
*t
, struct karg
*args
)
5094 char *filename
= args
->s
;
5099 if (args
->i
& XT_SHOW
)
5100 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5101 XT_SAVED_TABS_FILE
: named_session
);
5102 else if (args
->i
& XT_SAVE
) {
5103 if (session_save(t
, filename
)) {
5104 show_oops(t
, "Can't save session: %s",
5105 filename
? filename
: "INVALID");
5108 } else if (args
->i
& XT_OPEN
) {
5109 if (session_open(t
, filename
)) {
5110 show_oops(t
, "Can't open session: %s",
5111 filename
? filename
: "INVALID");
5114 } else if (args
->i
& XT_DELETE
) {
5115 if (session_delete(t
, filename
)) {
5116 show_oops(t
, "Can't delete session: %s",
5117 filename
? filename
: "INVALID");
5122 return (XT_CB_PASSTHROUGH
);
5126 * Make a hardcopy of the page
5129 print_page(struct tab
*t
, struct karg
*args
)
5131 WebKitWebFrame
*frame
;
5133 GtkPrintOperation
*op
;
5134 GtkPrintOperationAction action
;
5135 GtkPrintOperationResult print_res
;
5136 GError
*g_err
= NULL
;
5137 int marg_l
, marg_r
, marg_t
, marg_b
;
5139 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5141 ps
= gtk_page_setup_new();
5142 op
= gtk_print_operation_new();
5143 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5144 frame
= webkit_web_view_get_main_frame(t
->wv
);
5146 /* the default margins are too small, so we will bump them */
5147 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5148 XT_PRINT_EXTRA_MARGIN
;
5149 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5150 XT_PRINT_EXTRA_MARGIN
;
5151 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5152 XT_PRINT_EXTRA_MARGIN
;
5153 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5154 XT_PRINT_EXTRA_MARGIN
;
5157 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5158 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5159 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5160 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5162 gtk_print_operation_set_default_page_setup(op
, ps
);
5164 /* this appears to free 'op' and 'ps' */
5165 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5167 /* check it worked */
5168 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5169 show_oops(NULL
, "can't print: %s", g_err
->message
);
5170 g_error_free (g_err
);
5178 go_home(struct tab
*t
, struct karg
*args
)
5185 restart(struct tab
*t
, struct karg
*args
)
5189 a
.s
= XT_RESTART_TABS_FILE
;
5191 execvp(start_argv
[0], start_argv
);
5197 #define CTRL GDK_CONTROL_MASK
5198 #define MOD1 GDK_MOD1_MASK
5199 #define SHFT GDK_SHIFT_MASK
5201 /* inherent to GTK not all keys will be caught at all times */
5202 /* XXX sort key bindings */
5203 struct key_binding
{
5208 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5210 { "cookiejar", MOD1
, 0, GDK_j
},
5211 { "downloadmgr", MOD1
, 0, GDK_d
},
5212 { "history", MOD1
, 0, GDK_h
},
5213 { "print", CTRL
, 0, GDK_p
},
5214 { "search", 0, 0, GDK_slash
},
5215 { "searchb", 0, 0, GDK_question
},
5216 { "statustoggle", CTRL
, 0, GDK_n
},
5217 { "command", 0, 0, GDK_colon
},
5218 { "qa", CTRL
, 0, GDK_q
},
5219 { "restart", MOD1
, 0, GDK_q
},
5220 { "js toggle", CTRL
, 0, GDK_j
},
5221 { "cookie toggle", MOD1
, 0, GDK_c
},
5222 { "togglesrc", CTRL
, 0, GDK_s
},
5223 { "yankuri", 0, 0, GDK_y
},
5224 { "pasteuricur", 0, 0, GDK_p
},
5225 { "pasteurinew", 0, 0, GDK_P
},
5226 { "toplevel toggle", 0, 0, GDK_F4
},
5227 { "help", 0, 0, GDK_F1
},
5228 { "run_script", MOD1
, 0, GDK_r
},
5231 { "searchnext", 0, 0, GDK_n
},
5232 { "searchprevious", 0, 0, GDK_N
},
5235 { "focusaddress", 0, 0, GDK_F6
},
5236 { "focussearch", 0, 0, GDK_F7
},
5239 { "hinting", 0, 0, GDK_f
},
5241 /* custom stylesheet */
5242 { "userstyle", 0, 0, GDK_i
},
5245 { "goback", 0, 0, GDK_BackSpace
},
5246 { "goback", MOD1
, 0, GDK_Left
},
5247 { "goforward", SHFT
, 0, GDK_BackSpace
},
5248 { "goforward", MOD1
, 0, GDK_Right
},
5249 { "reload", 0, 0, GDK_F5
},
5250 { "reload", CTRL
, 0, GDK_r
},
5251 { "reload", CTRL
, 0, GDK_l
},
5252 { "favorites", MOD1
, 1, GDK_f
},
5254 /* vertical movement */
5255 { "scrolldown", 0, 0, GDK_j
},
5256 { "scrolldown", 0, 0, GDK_Down
},
5257 { "scrollup", 0, 0, GDK_Up
},
5258 { "scrollup", 0, 0, GDK_k
},
5259 { "scrollbottom", 0, 0, GDK_G
},
5260 { "scrollbottom", 0, 0, GDK_End
},
5261 { "scrolltop", 0, 0, GDK_Home
},
5262 { "scrollpagedown", 0, 0, GDK_space
},
5263 { "scrollpagedown", CTRL
, 0, GDK_f
},
5264 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5265 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5266 { "scrollpageup", 0, 0, GDK_Page_Up
},
5267 { "scrollpageup", CTRL
, 0, GDK_b
},
5268 { "scrollhalfup", CTRL
, 0, GDK_u
},
5269 /* horizontal movement */
5270 { "scrollright", 0, 0, GDK_l
},
5271 { "scrollright", 0, 0, GDK_Right
},
5272 { "scrollleft", 0, 0, GDK_Left
},
5273 { "scrollleft", 0, 0, GDK_h
},
5274 { "scrollfarright", 0, 0, GDK_dollar
},
5275 { "scrollfarleft", 0, 0, GDK_0
},
5278 { "tabnew", CTRL
, 0, GDK_t
},
5279 { "999tabnew", CTRL
, 0, GDK_T
},
5280 { "tabclose", CTRL
, 1, GDK_w
},
5281 { "tabundoclose", 0, 0, GDK_U
},
5282 { "tabnext 1", CTRL
, 0, GDK_1
},
5283 { "tabnext 2", CTRL
, 0, GDK_2
},
5284 { "tabnext 3", CTRL
, 0, GDK_3
},
5285 { "tabnext 4", CTRL
, 0, GDK_4
},
5286 { "tabnext 5", CTRL
, 0, GDK_5
},
5287 { "tabnext 6", CTRL
, 0, GDK_6
},
5288 { "tabnext 7", CTRL
, 0, GDK_7
},
5289 { "tabnext 8", CTRL
, 0, GDK_8
},
5290 { "tabnext 9", CTRL
, 0, GDK_9
},
5291 { "tabfirst", CTRL
, 0, GDK_less
},
5292 { "tablast", CTRL
, 0, GDK_greater
},
5293 { "tabprevious", CTRL
, 0, GDK_Left
},
5294 { "tabnext", CTRL
, 0, GDK_Right
},
5295 { "focusout", CTRL
, 0, GDK_minus
},
5296 { "focusin", CTRL
, 0, GDK_plus
},
5297 { "focusin", CTRL
, 0, GDK_equal
},
5298 { "focusreset", CTRL
, 0, GDK_0
},
5300 /* command aliases (handy when -S flag is used) */
5301 { "promptopen", 0, 0, GDK_F9
},
5302 { "promptopencurrent", 0, 0, GDK_F10
},
5303 { "prompttabnew", 0, 0, GDK_F11
},
5304 { "prompttabnewcurrent",0, 0, GDK_F12
},
5306 TAILQ_HEAD(keybinding_list
, key_binding
);
5309 walk_kb(struct settings
*s
,
5310 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5312 struct key_binding
*k
;
5315 if (s
== NULL
|| cb
== NULL
) {
5316 show_oops(NULL
, "walk_kb invalid parameters");
5320 TAILQ_FOREACH(k
, &kbl
, entry
) {
5326 if (gdk_keyval_name(k
->key
) == NULL
)
5329 strlcat(str
, k
->cmd
, sizeof str
);
5330 strlcat(str
, ",", sizeof str
);
5332 if (k
->mask
& GDK_SHIFT_MASK
)
5333 strlcat(str
, "S-", sizeof str
);
5334 if (k
->mask
& GDK_CONTROL_MASK
)
5335 strlcat(str
, "C-", sizeof str
);
5336 if (k
->mask
& GDK_MOD1_MASK
)
5337 strlcat(str
, "M1-", sizeof str
);
5338 if (k
->mask
& GDK_MOD2_MASK
)
5339 strlcat(str
, "M2-", sizeof str
);
5340 if (k
->mask
& GDK_MOD3_MASK
)
5341 strlcat(str
, "M3-", sizeof str
);
5342 if (k
->mask
& GDK_MOD4_MASK
)
5343 strlcat(str
, "M4-", sizeof str
);
5344 if (k
->mask
& GDK_MOD5_MASK
)
5345 strlcat(str
, "M5-", sizeof str
);
5347 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5348 cb(s
, str
, cb_args
);
5353 init_keybindings(void)
5356 struct key_binding
*k
;
5358 for (i
= 0; i
< LENGTH(keys
); i
++) {
5359 k
= g_malloc0(sizeof *k
);
5360 k
->cmd
= keys
[i
].cmd
;
5361 k
->mask
= keys
[i
].mask
;
5362 k
->use_in_entry
= keys
[i
].use_in_entry
;
5363 k
->key
= keys
[i
].key
;
5364 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5366 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5367 k
->cmd
? k
->cmd
: "unnamed key");
5372 keybinding_clearall(void)
5374 struct key_binding
*k
, *next
;
5376 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5377 next
= TAILQ_NEXT(k
, entry
);
5381 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5382 k
->cmd
? k
->cmd
: "unnamed key");
5383 TAILQ_REMOVE(&kbl
, k
, entry
);
5389 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5391 struct key_binding
*k
;
5392 guint keyval
, mask
= 0;
5395 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5397 /* Keys which are to be used in entry have been prefixed with an
5398 * exclamation mark. */
5402 /* find modifier keys */
5403 if (strstr(key
, "S-"))
5404 mask
|= GDK_SHIFT_MASK
;
5405 if (strstr(key
, "C-"))
5406 mask
|= GDK_CONTROL_MASK
;
5407 if (strstr(key
, "M1-"))
5408 mask
|= GDK_MOD1_MASK
;
5409 if (strstr(key
, "M2-"))
5410 mask
|= GDK_MOD2_MASK
;
5411 if (strstr(key
, "M3-"))
5412 mask
|= GDK_MOD3_MASK
;
5413 if (strstr(key
, "M4-"))
5414 mask
|= GDK_MOD4_MASK
;
5415 if (strstr(key
, "M5-"))
5416 mask
|= GDK_MOD5_MASK
;
5419 for (i
= strlen(key
) - 1; i
> 0; i
--)
5423 /* validate keyname */
5424 keyval
= gdk_keyval_from_name(key
);
5425 if (keyval
== GDK_VoidSymbol
) {
5426 warnx("invalid keybinding name %s", key
);
5429 /* must run this test too, gtk+ doesn't handle 10 for example */
5430 if (gdk_keyval_name(keyval
) == NULL
) {
5431 warnx("invalid keybinding name %s", key
);
5435 /* Remove eventual dupes. */
5436 TAILQ_FOREACH(k
, &kbl
, entry
)
5437 if (k
->key
== keyval
&& k
->mask
== mask
) {
5438 TAILQ_REMOVE(&kbl
, k
, entry
);
5444 k
= g_malloc0(sizeof *k
);
5445 k
->cmd
= g_strdup(cmd
);
5447 k
->use_in_entry
= use_in_entry
;
5450 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5455 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5456 k
->cmd
, gdk_keyval_name(keyval
));
5458 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5464 add_kb(struct settings
*s
, char *entry
)
5468 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5470 /* clearall is special */
5471 if (!strcmp(entry
, "clearall")) {
5472 keybinding_clearall();
5476 kb
= strstr(entry
, ",");
5482 return (keybinding_add(entry
, key
, key
[0] == '!'));
5488 int (*func
)(struct tab
*, struct karg
*);
5492 { "command", 0, command
, ':', 0 },
5493 { "search", 0, command
, '/', 0 },
5494 { "searchb", 0, command
, '?', 0 },
5495 { "togglesrc", 0, toggle_src
, 0, 0 },
5497 /* yanking and pasting */
5498 { "yankuri", 0, yank_uri
, 0, 0 },
5499 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5500 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5501 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5504 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5505 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5508 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5509 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5512 { "hinting", 0, hint
, 0, 0 },
5514 /* custom stylesheet */
5515 { "userstyle", 0, userstyle
, 0, 0 },
5518 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5519 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5520 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5522 /* vertical movement */
5523 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5524 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5525 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5526 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5527 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5528 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5529 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5530 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5531 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5532 /* horizontal movement */
5533 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5534 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5535 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5536 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5538 { "favorites", 0, xtp_page_fl
, 0, 0 },
5539 { "fav", 0, xtp_page_fl
, 0, 0 },
5540 { "favadd", 0, add_favorite
, 0, 0 },
5542 { "qall", 0, quit
, 0, 0 },
5543 { "quitall", 0, quit
, 0, 0 },
5544 { "w", 0, save_tabs
, 0, 0 },
5545 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5546 { "help", 0, help
, 0, 0 },
5547 { "about", 0, about
, 0, 0 },
5548 { "stats", 0, stats
, 0, 0 },
5549 { "version", 0, about
, 0, 0 },
5552 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5553 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5554 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5555 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5556 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5557 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5558 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5559 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5560 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5561 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5562 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5564 /* cookie command */
5565 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5566 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5567 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5568 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5569 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5570 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5571 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5572 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5573 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5574 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5575 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5577 /* toplevel (domain) command */
5578 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5579 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5582 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5585 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5586 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5587 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5589 { "ca", 0, ca_cmd
, 0, 0 },
5590 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5591 { "dl", 0, xtp_page_dl
, 0, 0 },
5592 { "h", 0, xtp_page_hl
, 0, 0 },
5593 { "history", 0, xtp_page_hl
, 0, 0 },
5594 { "home", 0, go_home
, 0, 0 },
5595 { "restart", 0, restart
, 0, 0 },
5596 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5597 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5598 { "statustoggle", 0, statustoggle
, 0, 0 },
5599 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5601 { "print", 0, print_page
, 0, 0 },
5604 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5605 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5606 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5607 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5608 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5609 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5610 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5611 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5612 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5613 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5614 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5615 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5616 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5617 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5618 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5619 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5620 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5621 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5622 { "buffers", 0, buffers
, 0, 0 },
5623 { "ls", 0, buffers
, 0, 0 },
5624 { "tabs", 0, buffers
, 0, 0 },
5626 /* command aliases (handy when -S flag is used) */
5627 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5628 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5629 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5630 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5633 { "set", 0, set
, 0, XT_USERARG
},
5635 { "fullscreen", 0, fullscreen
, 0, 0 },
5636 { "f", 0, fullscreen
, 0, 0 },
5639 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5640 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5641 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5642 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5643 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5650 } cmd_status
= {-1, 0};
5653 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5656 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5663 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5670 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5672 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5678 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5680 a
.i
= XT_NAV_FORWARD
;
5690 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5692 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5694 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5701 * cancel, remove, etc. downloads
5704 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5706 struct download find
, *d
= NULL
;
5708 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5710 /* some commands require a valid download id */
5711 if (cmd
!= XT_XTP_DL_LIST
) {
5712 /* lookup download in question */
5714 d
= RB_FIND(download_list
, &downloads
, &find
);
5717 show_oops(t
, "%s: no such download", __func__
);
5722 /* decide what to do */
5724 case XT_XTP_DL_CANCEL
:
5725 webkit_download_cancel(d
->download
);
5727 case XT_XTP_DL_REMOVE
:
5728 webkit_download_cancel(d
->download
); /* just incase */
5729 g_object_unref(d
->download
);
5730 RB_REMOVE(download_list
, &downloads
, d
);
5732 case XT_XTP_DL_LIST
:
5736 show_oops(t
, "%s: unknown command", __func__
);
5739 xtp_page_dl(t
, NULL
);
5743 * Actions on history, only does one thing for now, but
5744 * we provide the function for future actions
5747 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5749 struct history
*h
, *next
;
5753 case XT_XTP_HL_REMOVE
:
5754 /* walk backwards, as listed in reverse */
5755 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5756 next
= RB_PREV(history_list
, &hl
, h
);
5758 RB_REMOVE(history_list
, &hl
, h
);
5759 g_free((gpointer
) h
->title
);
5760 g_free((gpointer
) h
->uri
);
5767 case XT_XTP_HL_LIST
:
5768 /* Nothing - just xtp_page_hl() below */
5771 show_oops(t
, "%s: unknown command", __func__
);
5775 xtp_page_hl(t
, NULL
);
5778 /* remove a favorite */
5780 remove_favorite(struct tab
*t
, int index
)
5782 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5783 char *new_favs
, *tmp
;
5788 /* open favorites */
5789 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5791 if ((f
= fopen(file
, "r")) == NULL
) {
5792 show_oops(t
, "%s: can't open favorites: %s",
5793 __func__
, strerror(errno
));
5797 /* build a string which will become the new favroites file */
5798 new_favs
= g_strdup("");
5801 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5802 if (feof(f
) || ferror(f
))
5804 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5811 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5812 if (feof(f
) || ferror(f
)) {
5813 show_oops(t
, "%s: can't parse favorites %s",
5814 __func__
, strerror(errno
));
5819 /* as long as this isn't the one we are deleting add to file */
5822 new_favs
= g_strdup_printf("%s%s\n%s\n",
5823 new_favs
, title
, uri
);
5835 /* write back new favorites file */
5836 if ((f
= fopen(file
, "w")) == NULL
) {
5837 show_oops(t
, "%s: can't open favorites: %s",
5838 __func__
, strerror(errno
));
5842 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5855 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5858 case XT_XTP_FL_LIST
:
5859 /* nothing, just the below call to xtp_page_fl() */
5861 case XT_XTP_FL_REMOVE
:
5862 remove_favorite(t
, arg
);
5865 show_oops(t
, "%s: invalid favorites command", __func__
);
5869 xtp_page_fl(t
, NULL
);
5873 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5876 case XT_XTP_CL_LIST
:
5877 /* nothing, just xtp_page_cl() */
5879 case XT_XTP_CL_REMOVE
:
5883 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5887 xtp_page_cl(t
, NULL
);
5890 /* link an XTP class to it's session key and handler function */
5891 struct xtp_despatch
{
5894 void (*handle_func
)(struct tab
*, uint8_t, int);
5897 struct xtp_despatch xtp_despatches
[] = {
5898 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5899 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5900 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5901 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5902 { XT_XTP_INVALID
, NULL
, NULL
}
5906 * is the url xtp protocol? (xxxt://)
5907 * if so, parse and despatch correct bahvior
5910 parse_xtp_url(struct tab
*t
, const char *url
)
5912 char *dup
= NULL
, *p
, *last
;
5913 uint8_t n_tokens
= 0;
5914 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5915 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5920 * tokens array meaning:
5922 * tokens[1] = session key
5923 * tokens[2] = action
5924 * tokens[3] = optional argument
5927 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5929 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5932 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5934 /* split out the url */
5935 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5936 (p
= strtok_r(NULL
, "/", &last
))) {
5938 tokens
[n_tokens
++] = p
;
5941 /* should be atleast three fields 'class/seskey/command/arg' */
5945 dsp
= xtp_despatches
;
5946 req_class
= atoi(tokens
[0]);
5947 while (dsp
->xtp_class
) {
5948 if (dsp
->xtp_class
== req_class
) {
5955 /* did we find one atall? */
5956 if (dsp_match
== NULL
) {
5957 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5961 /* check session key and call despatch function */
5962 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5963 ret
= TRUE
; /* all is well, this was a valid xtp request */
5964 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5977 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5979 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5981 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5984 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5989 show_oops(t
, "activate_uri_entry_cb no uri");
5993 uri
+= strspn(uri
, "\t ");
5995 /* if xxxt:// treat specially */
5996 if (parse_xtp_url(t
, uri
))
5999 /* otherwise continue to load page normally */
6000 load_uri(t
, (gchar
*)uri
);
6005 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6007 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6008 char *newuri
= NULL
;
6011 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6014 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6018 if (search_string
== NULL
) {
6019 show_oops(t
, "no search_string");
6023 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6025 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6026 newuri
= g_strdup_printf(search_string
, enc_search
);
6030 webkit_web_view_load_uri(t
->wv
, newuri
);
6038 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6040 struct domain
*d
= NULL
;
6043 if (uri
== NULL
|| t
== NULL
)
6046 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6051 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6052 es
? "enable" : "disable", uri
);
6054 g_object_set(G_OBJECT(t
->settings
),
6055 "enable-html5-local-storage", es
, (char *)NULL
);
6056 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6060 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6062 struct domain
*d
= NULL
;
6065 if (uri
== NULL
|| t
== NULL
)
6068 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6073 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6074 es
? "enable" : "disable", uri
);
6076 g_object_set(G_OBJECT(t
->settings
),
6077 "enable-scripts", es
, (char *)NULL
);
6078 g_object_set(G_OBJECT(t
->settings
),
6079 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6080 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6082 button_set_stockid(t
->js_toggle
,
6083 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6087 color_address_bar(gpointer p
)
6090 struct tab
*tt
, *t
= p
;
6091 gchar
*col_str
= XT_COLOR_YELLOW
;
6093 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6095 /* make sure t still exists */
6098 TAILQ_FOREACH(tt
, &tabs
, entry
)
6104 switch (load_compare_cert(t
, NULL
)) {
6106 col_str
= XT_COLOR_BLUE
;
6109 col_str
= XT_COLOR_GREEN
;
6111 case CERT_UNTRUSTED
:
6112 col_str
= XT_COLOR_YELLOW
;
6115 col_str
= XT_COLOR_RED
;
6119 gdk_color_parse(col_str
, &color
);
6120 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6122 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6123 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6125 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6129 return (FALSE
/* kill thread */);
6133 show_ca_status(struct tab
*t
, const char *uri
)
6136 gchar
*col_str
= XT_COLOR_WHITE
;
6138 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6139 ssl_strict_certs
, ssl_ca_file
, uri
);
6146 if (ssl_ca_file
== NULL
) {
6147 if (g_str_has_prefix(uri
, "http://"))
6149 if (g_str_has_prefix(uri
, "https://")) {
6150 col_str
= XT_COLOR_RED
;
6155 if (g_str_has_prefix(uri
, "http://") ||
6156 !g_str_has_prefix(uri
, "https://"))
6159 /* thread the coloring of the address bar */
6160 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
,
6161 color_address_bar
, t
, NULL
);
6166 gdk_color_parse(col_str
, &color
);
6167 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6169 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6170 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6172 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6177 free_favicon(struct tab
*t
)
6179 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6180 __func__
, t
->icon_download
, t
->icon_request
);
6182 if (t
->icon_request
)
6183 g_object_unref(t
->icon_request
);
6184 if (t
->icon_dest_uri
)
6185 g_free(t
->icon_dest_uri
);
6187 t
->icon_request
= NULL
;
6188 t
->icon_dest_uri
= NULL
;
6192 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6194 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6195 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6197 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6198 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6200 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6201 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6205 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6207 GdkPixbuf
*pb_scaled
;
6209 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6210 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6211 GDK_INTERP_BILINEAR
);
6215 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6216 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6218 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6219 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6221 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6222 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6224 if (pb_scaled
!= pb
)
6225 g_object_unref(pb_scaled
);
6229 xt_icon_from_file(struct tab
*t
, char *file
)
6233 if (g_str_has_prefix(file
, "file://"))
6234 file
+= strlen("file://");
6236 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6238 xt_icon_from_pixbuf(t
, pb
);
6241 xt_icon_from_name(t
, "text-html");
6245 is_valid_icon(char *file
)
6248 const char *mime_type
;
6252 gf
= g_file_new_for_path(file
);
6253 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6255 mime_type
= g_file_info_get_content_type(fi
);
6256 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6257 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6258 g_strcmp0(mime_type
, "image/png") == 0 ||
6259 g_strcmp0(mime_type
, "image/gif") == 0 ||
6260 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6268 set_favicon_from_file(struct tab
*t
, char *file
)
6272 if (t
== NULL
|| file
== NULL
)
6275 if (g_str_has_prefix(file
, "file://"))
6276 file
+= strlen("file://");
6277 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6279 if (!stat(file
, &sb
)) {
6280 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6281 /* corrupt icon so trash it */
6282 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6285 /* no need to set icon to default here */
6289 xt_icon_from_file(t
, file
);
6293 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6296 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6297 struct tab
*tt
= NULL
, *t
= NULL
;
6300 * find the webview instead of passing in the tab as it could have been
6301 * deleted from underneath us.
6303 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6312 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6313 __func__
, t
->tab_id
, status
);
6316 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6318 t
->icon_download
= NULL
;
6321 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6324 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6327 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6329 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6330 __func__
, t
->tab_id
);
6331 t
->icon_download
= NULL
;
6334 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6337 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6338 __func__
, t
->icon_dest_uri
);
6339 set_favicon_from_file(t
, t
->icon_dest_uri
);
6340 /* these will be freed post callback */
6341 t
->icon_request
= NULL
;
6342 t
->icon_download
= NULL
;
6350 abort_favicon_download(struct tab
*t
)
6352 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6354 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6355 if (t
->icon_download
) {
6356 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6357 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6358 webkit_download_cancel(t
->icon_download
);
6359 t
->icon_download
= NULL
;
6364 xt_icon_from_name(t
, "text-html");
6368 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6370 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6372 if (uri
== NULL
|| t
== NULL
)
6375 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6376 /* take icon from WebKitIconDatabase */
6379 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6381 xt_icon_from_pixbuf(t
, pb
);
6384 xt_icon_from_name(t
, "text-html");
6385 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6386 /* download icon to cache dir */
6387 gchar
*name_hash
, file
[PATH_MAX
];
6390 if (t
->icon_request
) {
6391 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6395 /* check to see if we got the icon in cache */
6396 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6397 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6400 if (!stat(file
, &sb
)) {
6401 if (sb
.st_size
> 0) {
6402 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6404 set_favicon_from_file(t
, file
);
6408 /* corrupt icon so trash it */
6409 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6414 /* create download for icon */
6415 t
->icon_request
= webkit_network_request_new(uri
);
6416 if (t
->icon_request
== NULL
) {
6417 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6422 t
->icon_download
= webkit_download_new(t
->icon_request
);
6423 if (t
->icon_download
== NULL
)
6426 /* we have to free icon_dest_uri later */
6427 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6428 webkit_download_set_destination_uri(t
->icon_download
,
6431 if (webkit_download_get_status(t
->icon_download
) ==
6432 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6433 g_object_unref(t
->icon_request
);
6434 g_free(t
->icon_dest_uri
);
6435 t
->icon_request
= NULL
;
6436 t
->icon_dest_uri
= NULL
;
6440 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6441 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6443 webkit_download_start(t
->icon_download
);
6448 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6450 const gchar
*uri
= NULL
, *title
= NULL
;
6451 struct history
*h
, find
;
6455 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6456 webkit_web_view_get_load_status(wview
),
6457 get_uri(t
) ? get_uri(t
) : "NOTHING");
6460 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6464 switch (webkit_web_view_get_load_status(wview
)) {
6465 case WEBKIT_LOAD_PROVISIONAL
:
6467 abort_favicon_download(t
);
6468 #if GTK_CHECK_VERSION(2, 20, 0)
6469 gtk_widget_show(t
->spinner
);
6470 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6472 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6474 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6476 /* assume we are a new address */
6477 gdk_color_parse("white", &color
);
6478 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6479 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6481 /* take focus if we are visible */
6487 case WEBKIT_LOAD_COMMITTED
:
6492 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6498 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6500 /* check if js white listing is enabled */
6501 if (enable_cookie_whitelist
)
6502 check_and_set_cookie(uri
, t
);
6503 if (enable_js_whitelist
)
6504 check_and_set_js(uri
, t
);
6510 /* we know enough to autosave the session */
6511 if (session_autosave
) {
6516 show_ca_status(t
, uri
);
6519 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6523 case WEBKIT_LOAD_FINISHED
:
6529 if (!strncmp(uri
, "http://", strlen("http://")) ||
6530 !strncmp(uri
, "https://", strlen("https://")) ||
6531 !strncmp(uri
, "file://", strlen("file://"))) {
6533 h
= RB_FIND(history_list
, &hl
, &find
);
6535 title
= get_title(t
, FALSE
);
6536 h
= g_malloc(sizeof *h
);
6537 h
->uri
= g_strdup(uri
);
6538 h
->title
= g_strdup(title
);
6539 RB_INSERT(history_list
, &hl
, h
);
6540 completion_add_uri(h
->uri
);
6541 update_history_tabs(NULL
);
6545 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6546 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6547 case WEBKIT_LOAD_FAILED
:
6550 #if GTK_CHECK_VERSION(2, 20, 0)
6551 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6552 gtk_widget_hide(t
->spinner
);
6555 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6560 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6562 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6563 webkit_web_view_can_go_back(wview
));
6565 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6566 webkit_web_view_can_go_forward(wview
));
6571 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6572 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6575 * XXX this function is wrong
6576 * it overwrites perfectly good urls with garbage on load errors
6577 * those happen often when popups fail to resolve dns
6581 t
->tmp_uri
= g_strdup(uri
);
6582 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6583 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6584 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6585 set_status(t
, uri
, XT_STATUS_NOTHING
);
6592 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6594 const gchar
*title
= NULL
, *win_title
= NULL
;
6596 title
= get_title(t
, FALSE
);
6597 win_title
= get_title(t
, TRUE
);
6598 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6599 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6600 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6601 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6605 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6607 run_script(t
, JS_HINTING
);
6611 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6613 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6614 progress
== 100 ? 0 : (double)progress
/ 100);
6615 if (show_url
== 0) {
6616 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6617 progress
== 100 ? 0 : (double)progress
/ 100);
6620 update_statusbar_position(NULL
, NULL
);
6624 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6625 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6626 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6629 WebKitWebNavigationReason reason
;
6630 struct domain
*d
= NULL
;
6633 show_oops(NULL
, "webview_npd_cb invalid parameters");
6637 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6639 webkit_network_request_get_uri(request
));
6641 uri
= (char *)webkit_network_request_get_uri(request
);
6643 /* if this is an xtp url, we don't load anything else */
6644 if (parse_xtp_url(t
, uri
))
6647 if (t
->ctrl_click
) {
6649 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6650 webkit_web_policy_decision_ignore(pd
);
6651 return (TRUE
); /* we made the decission */
6655 * This is a little hairy but it comes down to this:
6656 * when we run in whitelist mode we have to assist the browser in
6657 * opening the URL that it would have opened in a new tab.
6659 reason
= webkit_web_navigation_action_get_reason(na
);
6660 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6661 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6662 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6663 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6665 webkit_web_policy_decision_use(pd
);
6666 return (TRUE
); /* we made the decision */
6673 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6676 struct domain
*d
= NULL
;
6678 WebKitWebView
*webview
= NULL
;
6680 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6681 webkit_web_view_get_uri(wv
));
6684 /* open in current tab */
6686 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6687 uri
= webkit_web_view_get_uri(wv
);
6688 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6691 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6693 } else if (enable_scripts
== 1) {
6694 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6702 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6705 struct domain
*d
= NULL
;
6707 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6709 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6710 uri
= webkit_web_view_get_uri(wv
);
6711 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6715 } else if (enable_scripts
== 1)
6722 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6724 /* we can not eat the event without throwing gtk off so defer it */
6726 /* catch middle click */
6727 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6732 /* catch ctrl click */
6733 if (e
->type
== GDK_BUTTON_RELEASE
&&
6734 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6739 return (XT_CB_PASSTHROUGH
);
6743 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6745 struct mime_type
*m
;
6747 m
= find_mime_type(mime_type
);
6755 show_oops(t
, "can't fork mime handler");
6765 execlp(m
->mt_action
, m
->mt_action
,
6766 webkit_network_request_get_uri(request
), (void *)NULL
);
6775 get_mime_type(char *file
)
6777 const char *mime_type
;
6781 if (g_str_has_prefix(file
, "file://"))
6782 file
+= strlen("file://");
6784 gf
= g_file_new_for_path(file
);
6785 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6787 mime_type
= g_file_info_get_content_type(fi
);
6795 run_download_mimehandler(char *mime_type
, char *file
)
6797 struct mime_type
*m
;
6799 m
= find_mime_type(mime_type
);
6805 show_oops(NULL
, "can't fork download mime handler");
6815 if (g_str_has_prefix(file
, "file://"))
6816 file
+= strlen("file://");
6817 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6826 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6829 WebKitDownloadStatus status
;
6830 const gchar
*file
= NULL
, *mime
= NULL
;
6832 if (download
== NULL
)
6834 status
= webkit_download_get_status(download
);
6835 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6838 file
= webkit_download_get_destination_uri(download
);
6841 mime
= get_mime_type((char *)file
);
6845 run_download_mimehandler((char *)mime
, (char *)file
);
6849 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6850 WebKitNetworkRequest
*request
, char *mime_type
,
6851 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6854 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6858 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6859 t
->tab_id
, mime_type
);
6861 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6862 webkit_web_policy_decision_ignore(decision
);
6867 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6868 webkit_web_policy_decision_download(decision
);
6876 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6880 const gchar
*suggested_name
;
6881 gchar
*filename
= NULL
;
6883 struct download
*download_entry
;
6886 if (wk_download
== NULL
|| t
== NULL
) {
6887 show_oops(NULL
, "%s invalid parameters", __func__
);
6891 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6892 if (suggested_name
== NULL
)
6893 return (FALSE
); /* abort download */
6904 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6906 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6907 filename
: suggested_name
);
6909 } while (!stat(uri
+ strlen("file://"), &sb
));
6911 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6912 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6914 webkit_download_set_destination_uri(wk_download
, uri
);
6916 if (webkit_download_get_status(wk_download
) ==
6917 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6918 show_oops(t
, "%s: download failed to start", __func__
);
6920 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6922 /* connect "download first" mime handler */
6923 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6924 G_CALLBACK(download_status_changed_cb
), NULL
);
6926 download_entry
= g_malloc(sizeof(struct download
));
6927 download_entry
->download
= wk_download
;
6928 download_entry
->tab
= t
;
6929 download_entry
->id
= next_download_id
++;
6930 RB_INSERT(download_list
, &downloads
, download_entry
);
6931 /* get from history */
6932 g_object_ref(wk_download
);
6933 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6934 show_oops(t
, "Download of '%s' started...",
6935 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6944 /* sync other download manager tabs */
6945 update_download_tabs(NULL
);
6948 * NOTE: never redirect/render the current tab before this
6949 * function returns. This will cause the download to never start.
6951 return (ret
); /* start download */
6955 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6957 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6960 show_oops(NULL
, "webview_hover_cb");
6965 set_status(t
, uri
, XT_STATUS_LINK
);
6968 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6973 mark(struct tab
*t
, struct karg
*arg
)
6979 if ((index
= marktoindex(mark
)) == -1)
6982 if (arg
->i
== XT_MARK_SET
)
6983 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6984 else if (arg
->i
== XT_MARK_GOTO
) {
6985 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6986 show_oops(t
, "mark '%c' does not exist", mark
);
6989 /* XXX t->mark[index] can be bigger than the maximum if ajax or
6990 something changes the document size */
6991 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
6998 marks_clear(struct tab
*t
)
7002 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7003 t
->mark
[i
] = XT_INVALID_MARK
;
7009 char file
[PATH_MAX
];
7010 char *line
= NULL
, *p
, mark
;
7015 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7016 if ((f
= fopen(file
, "r+")) == NULL
) {
7017 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7021 for (i
= 1; ; i
++) {
7022 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7023 if (feof(f
) || ferror(f
))
7025 if (strlen(line
) == 0 || line
[0] == '#') {
7031 p
= strtok(line
, " \t");
7033 if (p
== NULL
|| strlen(p
) != 1 ||
7034 (index
= marktoindex(*p
)) == -1) {
7035 warnx("corrupt quickmarks file, line %d", i
);
7040 p
= strtok(NULL
, " \t");
7041 if (qmarks
[index
] != NULL
)
7042 g_free(qmarks
[index
]);
7043 qmarks
[index
] = g_strdup(p
);
7054 char file
[PATH_MAX
];
7058 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7059 if ((f
= fopen(file
, "r+")) == NULL
) {
7060 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7064 for (i
= 0; i
< XT_NOMARKS
; i
++)
7065 if (qmarks
[i
] != NULL
)
7066 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7074 qmark(struct tab
*t
, struct karg
*arg
)
7079 mark
= arg
->s
[strlen(arg
->s
)-1];
7080 index
= marktoindex(mark
);
7086 if (qmarks
[index
] != NULL
)
7087 g_free(qmarks
[index
]);
7089 qmarks_load(); /* sync if multiple instances */
7090 qmarks
[index
] = g_strdup(get_uri(t
));
7094 if (qmarks
[index
] != NULL
)
7095 load_uri(t
, qmarks
[index
]);
7097 show_oops(t
, "quickmark \"%c\" does not exist",
7103 if (qmarks
[index
] != NULL
)
7104 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7106 show_oops(t
, "quickmark \"%c\" does not exist",
7117 go_up(struct tab
*t
, struct karg
*args
)
7123 levels
= atoi(args
->s
);
7127 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7128 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7130 tmp
+= strlen(XT_PROTO_DELIM
);
7132 /* if an uri starts with a slash, leave it alone (for file:///) */
7139 p
= strrchr(tmp
, '/');
7153 gototab(struct tab
*t
, struct karg
*args
)
7156 struct karg arg
= {0, NULL
, -1};
7158 tab
= atoi(args
->s
);
7160 arg
.i
= XT_TAB_NEXT
;
7169 zoom_amount(struct tab
*t
, struct karg
*arg
)
7171 struct karg narg
= {0, NULL
, -1};
7173 narg
.i
= atoi(arg
->s
);
7174 resizetab(t
, &narg
);
7180 flip_colon(struct tab
*t
, struct karg
*arg
)
7182 struct karg narg
= {0, NULL
, -1};
7185 if (t
== NULL
|| arg
== NULL
)
7188 p
= strstr(arg
->s
, ":");
7200 /* buffer commands receive the regex that triggered them in arg.s */
7201 char bcmd
[XT_BUFCMD_SZ
];
7205 #define XT_PRE_NO (0)
7206 #define XT_PRE_YES (1)
7207 #define XT_PRE_MAYBE (2)
7209 int (*func
)(struct tab
*, struct karg
*);
7213 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7214 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7215 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7216 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7217 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7218 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7219 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7220 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7221 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7222 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7223 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7224 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7225 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7226 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7227 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7228 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7229 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7230 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7234 buffercmd_init(void)
7238 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7239 regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7244 buffercmd_abort(struct tab
*t
)
7248 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7249 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7252 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7253 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7257 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7259 struct karg arg
= {0, NULL
, -1};
7262 arg
.s
= g_strdup(bcmd
);
7264 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7265 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7275 buffercmd_addkey(struct tab
*t
, guint keyval
)
7278 char s
[XT_BUFCMD_SZ
];
7280 if (keyval
== GDK_Escape
) {
7282 return (XT_CB_HANDLED
);
7285 /* key with modifier or non-ascii character */
7286 if (!isascii(keyval
))
7287 return (XT_CB_PASSTHROUGH
);
7289 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7290 "to buffer \"%s\"\n", keyval
, bcmd
);
7292 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7293 if (bcmd
[i
] == '\0') {
7298 /* buffer full, ignore input */
7299 if (i
>= LENGTH(bcmd
) -1) {
7300 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7302 return (XT_CB_HANDLED
);
7305 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7307 /* find exact match */
7308 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7309 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7310 (size_t) 0, NULL
, 0) == 0) {
7311 buffercmd_execute(t
, &buffercmds
[i
]);
7315 /* find non exact matches to see if we need to abort ot not */
7316 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7317 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7320 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7321 if (isdigit(bcmd
[0])) {
7322 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7326 if (sscanf(bcmd
, "%s", s
) == 0)
7329 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7330 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7333 if (sscanf(bcmd
, "%s", s
) == 0)
7336 if (c
== -1 && buffercmds
[i
].precount
)
7338 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7341 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7342 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7345 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7350 return (XT_CB_HANDLED
);
7354 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7356 struct key_binding
*k
;
7358 /* handle keybindings if buffercmd is empty.
7359 if not empty, allow commands like C-n */
7360 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7361 TAILQ_FOREACH(k
, &kbl
, entry
)
7362 if (e
->keyval
== k
->key
7363 && (entry
? k
->use_in_entry
: 1)) {
7365 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7366 return (cmd_execute(t
, k
->cmd
));
7367 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7368 return (cmd_execute(t
, k
->cmd
));
7372 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7373 return buffercmd_addkey(t
, e
->keyval
);
7375 return (XT_CB_PASSTHROUGH
);
7379 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7381 char s
[2], buf
[128];
7382 const char *errstr
= NULL
;
7384 /* don't use w directly; use t->whatever instead */
7387 show_oops(NULL
, "wv_keypress_after_cb");
7388 return (XT_CB_PASSTHROUGH
);
7391 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7392 e
->keyval
, e
->state
, t
);
7396 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7398 return (XT_CB_HANDLED
);
7402 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7404 /* we have a string */
7406 /* we have a number */
7407 snprintf(buf
, sizeof buf
,
7408 "vimprobable_fire(%s)", t
->hint_num
);
7415 /* XXX unfuck this */
7416 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7417 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7418 /* last input was numerical */
7420 l
= strlen(t
->hint_num
);
7427 t
->hint_num
[l
] = '\0';
7431 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7432 /* last input was alphanumerical */
7434 l
= strlen(t
->hint_buf
);
7441 t
->hint_buf
[l
] = '\0';
7451 /* numerical input */
7452 if (CLEAN(e
->state
) == 0 &&
7453 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7454 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7455 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7456 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7457 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7461 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7462 "invalid link number\n");
7465 snprintf(buf
, sizeof buf
,
7466 "vimprobable_update_hints(%s)",
7468 t
->hint_mode
= XT_HINT_NUMERICAL
;
7472 /* empty the counter buffer */
7473 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7474 return (XT_CB_HANDLED
);
7477 /* alphanumerical input */
7478 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7479 e
->keyval
<= GDK_z
) ||
7480 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7481 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7482 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7483 e
->keyval
<= GDK_9
) ||
7484 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7485 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7486 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7487 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7488 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7489 " %s\n", t
->hint_buf
);
7491 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7494 snprintf(buf
, sizeof buf
,
7495 "vimprobable_show_hints('%s')", t
->hint_buf
);
7496 t
->hint_mode
= XT_HINT_ALPHANUM
;
7499 /* empty the counter buffer */
7500 bzero(t
->hint_num
, sizeof t
->hint_num
);
7501 return (XT_CB_HANDLED
);
7504 return (XT_CB_HANDLED
);
7507 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7508 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7509 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7512 return (handle_keypress(t
, e
, 0));
7516 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7520 /* Hide buffers, if they are visible, with escape. */
7521 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7522 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7523 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7525 return (XT_CB_HANDLED
);
7528 return (XT_CB_PASSTHROUGH
);
7532 search_continue(struct tab
*t
)
7534 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7535 gboolean rv
= FALSE
;
7539 if (strlen(c
) == 1) {
7540 webkit_web_view_unmark_text_matches(t
->wv
);
7545 t
->search_forward
= TRUE
;
7546 else if (c
[0] == '?')
7547 t
->search_forward
= FALSE
;
7557 search_cb(struct tab
*t
)
7559 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7562 if (search_continue(t
) == FALSE
)
7566 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7568 /* not found, mark red */
7569 gdk_color_parse(XT_COLOR_RED
, &color
);
7570 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7571 /* unmark and remove selection */
7572 webkit_web_view_unmark_text_matches(t
->wv
);
7573 /* my kingdom for a way to unselect text in webview */
7575 /* found, highlight all */
7576 webkit_web_view_unmark_text_matches(t
->wv
);
7577 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7578 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7579 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7580 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7588 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7590 const gchar
*c
= gtk_entry_get_text(w
);
7593 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7594 return (XT_CB_PASSTHROUGH
);
7597 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7598 e
->keyval
, e
->state
, t
);
7600 if (search_continue(t
) == FALSE
)
7603 /* if search length is > 4 then no longer play timeout games */
7604 if (strlen(c
) > 4) {
7606 g_source_remove(t
->search_id
);
7613 /* reestablish a new timer if the user types fast */
7615 g_source_remove(t
->search_id
);
7616 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7619 return (XT_CB_PASSTHROUGH
);
7623 match_uri(const gchar
*uri
, const gchar
*key
) {
7626 gboolean match
= FALSE
;
7630 if (!strncmp(key
, uri
, len
))
7633 voffset
= strstr(uri
, "/") + 2;
7634 if (!strncmp(key
, voffset
, len
))
7636 else if (g_str_has_prefix(voffset
, "www.")) {
7637 voffset
= voffset
+ strlen("www.");
7638 if (!strncmp(key
, voffset
, len
))
7647 cmd_getlist(int id
, char *key
)
7652 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7653 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7654 if (match_uri(h
->uri
, key
)) {
7655 cmd_status
.list
[c
] = (char *)h
->uri
;
7664 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7666 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7667 if (cmds
[i
].level
< dep
)
7669 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7671 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7679 cmd_getnext(int dir
)
7681 cmd_status
.index
+= dir
;
7683 if (cmd_status
.index
< 0)
7684 cmd_status
.index
= cmd_status
.len
- 1;
7685 else if (cmd_status
.index
>= cmd_status
.len
)
7686 cmd_status
.index
= 0;
7688 return cmd_status
.list
[cmd_status
.index
];
7692 cmd_tokenize(char *s
, char *tokens
[])
7696 size_t len
= strlen(s
);
7699 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7700 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7701 tok
= strtok_r(NULL
, " ", &last
), i
++)
7711 cmd_complete(struct tab
*t
, char *str
, int dir
)
7713 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7714 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7716 char *tok
, *match
, *s
= g_strdup(str
);
7718 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7721 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7724 for (i
= 0; isdigit(s
[i
]); i
++)
7727 for (; isspace(s
[i
]); i
++)
7732 levels
= cmd_tokenize(s
, tokens
);
7734 for (i
= 0; i
< levels
- 1; i
++) {
7737 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7738 if (cmds
[j
].level
< dep
)
7740 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7744 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7751 if (matchcount
== 1) {
7752 strlcat(res
, tok
, sizeof res
);
7753 strlcat(res
, " ", sizeof res
);
7763 if (cmd_status
.index
== -1)
7764 cmd_getlist(parent
, tokens
[i
]);
7766 if (cmd_status
.len
> 0) {
7767 match
= cmd_getnext(dir
);
7768 strlcat(res
, match
, sizeof res
);
7769 gtk_entry_set_text(w
, res
);
7770 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7777 cmd_execute(struct tab
*t
, char *str
)
7779 struct cmd
*cmd
= NULL
;
7780 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7782 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7783 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7784 struct karg arg
= {0, NULL
, -1};
7789 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7795 while (isspace(s
[0]))
7798 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7799 prefix
= atoi(prefixstr
);
7803 for (tok
= strtok_r(s
, " ", &last
); tok
;
7804 tok
= strtok_r(NULL
, " ", &last
)) {
7806 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7807 if (cmds
[j
].level
< dep
)
7809 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7811 if (cmds
[j
].level
== dep
&&
7812 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7816 if (len
== strlen(cmds
[j
].cmd
)) {
7822 if (matchcount
== 1) {
7827 show_oops(t
, "Invalid command: %s", str
);
7835 arg
.precount
= prefix
;
7836 else if (cmd_prefix
> 0)
7837 arg
.precount
= cmd_prefix
;
7839 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
7840 show_oops(t
, "No prefix allowed: %s", str
);
7844 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7845 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7846 arg
.precount
= atoi(arg
.s
);
7847 if (arg
.precount
<= 0) {
7848 if (arg
.s
[0] == '0')
7849 show_oops(t
, "Zero count");
7851 show_oops(t
, "Trailing characters");
7856 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
7857 __func__
, arg
.precount
, arg
.s
);
7873 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7876 show_oops(NULL
, "entry_key_cb invalid parameters");
7877 return (XT_CB_PASSTHROUGH
);
7880 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7881 e
->keyval
, e
->state
, t
);
7885 if (e
->keyval
== GDK_Escape
) {
7886 /* don't use focus_webview(t) because we want to type :cmds */
7887 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7890 return (handle_keypress(t
, e
, 1));
7894 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7896 int rv
= XT_CB_HANDLED
;
7897 const gchar
*c
= gtk_entry_get_text(w
);
7900 show_oops(NULL
, "cmd_keypress_cb parameters");
7901 return (XT_CB_PASSTHROUGH
);
7904 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7905 e
->keyval
, e
->state
, t
);
7909 e
->keyval
= GDK_Escape
;
7910 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7911 e
->keyval
= GDK_Escape
;
7913 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7914 e
->keyval
!= GDK_ISO_Left_Tab
)
7915 cmd_status
.index
= -1;
7917 switch (e
->keyval
) {
7920 cmd_complete(t
, (char *)&c
[1], 1);
7922 case GDK_ISO_Left_Tab
:
7924 cmd_complete(t
, (char *)&c
[1], -1);
7928 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7936 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7937 webkit_web_view_unmark_text_matches(t
->wv
);
7941 rv
= XT_CB_PASSTHROUGH
;
7947 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7950 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7951 return (XT_CB_PASSTHROUGH
);
7953 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7958 if (show_url
== 0 || t
->focus_wv
)
7961 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7963 return (XT_CB_PASSTHROUGH
);
7967 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7970 const gchar
*c
= gtk_entry_get_text(entry
);
7973 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7977 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7984 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7990 if (c
[0] == '/' || c
[0] == '?') {
7991 /* see if there is a timer pending */
7993 g_source_remove(t
->search_id
);
7998 if (t
->search_text
) {
7999 g_free(t
->search_text
);
8000 t
->search_text
= NULL
;
8003 t
->search_text
= g_strdup(s
);
8005 g_free(global_search
);
8006 global_search
= g_strdup(s
);
8007 t
->search_forward
= c
[0] == '/';
8019 backward_cb(GtkWidget
*w
, struct tab
*t
)
8024 show_oops(NULL
, "backward_cb invalid parameters");
8028 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8035 forward_cb(GtkWidget
*w
, struct tab
*t
)
8040 show_oops(NULL
, "forward_cb invalid parameters");
8044 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8046 a
.i
= XT_NAV_FORWARD
;
8051 home_cb(GtkWidget
*w
, struct tab
*t
)
8054 show_oops(NULL
, "home_cb invalid parameters");
8058 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8064 stop_cb(GtkWidget
*w
, struct tab
*t
)
8066 WebKitWebFrame
*frame
;
8069 show_oops(NULL
, "stop_cb invalid parameters");
8073 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8075 frame
= webkit_web_view_get_main_frame(t
->wv
);
8076 if (frame
== NULL
) {
8077 show_oops(t
, "stop_cb: no frame");
8081 webkit_web_frame_stop_loading(frame
);
8082 abort_favicon_download(t
);
8086 setup_webkit(struct tab
*t
)
8088 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8089 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8090 FALSE
, (char *)NULL
);
8092 warnx("webkit does not have \"enable-dns-prefetching\" property");
8093 g_object_set(G_OBJECT(t
->settings
),
8094 "user-agent", t
->user_agent
, (char *)NULL
);
8095 g_object_set(G_OBJECT(t
->settings
),
8096 "enable-scripts", enable_scripts
, (char *)NULL
);
8097 g_object_set(G_OBJECT(t
->settings
),
8098 "enable-plugins", enable_plugins
, (char *)NULL
);
8099 g_object_set(G_OBJECT(t
->settings
),
8100 "javascript-can-open-windows-automatically", enable_scripts
,
8102 g_object_set(G_OBJECT(t
->settings
),
8103 "enable-html5-database", FALSE
, (char *)NULL
);
8104 g_object_set(G_OBJECT(t
->settings
),
8105 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8106 g_object_set(G_OBJECT(t
->settings
),
8107 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8108 g_object_set(G_OBJECT(t
->settings
),
8109 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8110 g_object_set(G_OBJECT(t
->wv
),
8111 "full-content-zoom", TRUE
, (char *)NULL
);
8113 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8117 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8119 struct tab
*ti
, *t
= NULL
;
8120 gdouble view_size
, value
, max
;
8123 TAILQ_FOREACH(ti
, &tabs
, entry
)
8124 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8132 if (adjustment
== NULL
)
8133 adjustment
= gtk_scrolled_window_get_vadjustment(
8134 GTK_SCROLLED_WINDOW(t
->browser_win
));
8136 view_size
= gtk_adjustment_get_page_size(adjustment
);
8137 value
= gtk_adjustment_get_value(adjustment
);
8138 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8141 position
= g_strdup("All");
8142 else if (value
== max
)
8143 position
= g_strdup("Bot");
8144 else if (value
== 0)
8145 position
= g_strdup("Top");
8147 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8149 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8156 create_browser(struct tab
*t
)
8160 GtkAdjustment
*adjustment
;
8163 show_oops(NULL
, "create_browser invalid parameters");
8167 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8168 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8169 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8170 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8172 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8173 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8174 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8176 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8177 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8180 t
->settings
= webkit_web_settings_new();
8182 if (user_agent
== NULL
) {
8183 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8185 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8188 t
->user_agent
= g_strdup(user_agent
);
8190 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8193 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8194 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8195 G_CALLBACK(update_statusbar_position
), NULL
);
8207 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8208 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8209 gtk_widget_set_name(w
, "xxxterm");
8210 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8211 g_signal_connect(G_OBJECT(w
), "delete_event",
8212 G_CALLBACK (gtk_main_quit
), NULL
);
8218 create_kiosk_toolbar(struct tab
*t
)
8220 GtkWidget
*toolbar
= NULL
, *b
;
8222 b
= gtk_hbox_new(FALSE
, 0);
8224 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8226 /* backward button */
8227 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8228 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8229 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8230 G_CALLBACK(backward_cb
), t
);
8231 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8233 /* forward button */
8234 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8235 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8236 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8237 G_CALLBACK(forward_cb
), t
);
8238 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8241 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8242 gtk_widget_set_sensitive(t
->gohome
, true);
8243 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8244 G_CALLBACK(home_cb
), t
);
8245 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8247 /* create widgets but don't use them */
8248 t
->uri_entry
= gtk_entry_new();
8249 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8250 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8251 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8257 create_toolbar(struct tab
*t
)
8259 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8261 b
= gtk_hbox_new(FALSE
, 0);
8263 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8265 /* backward button */
8266 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8267 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8268 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8269 G_CALLBACK(backward_cb
), t
);
8270 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8272 /* forward button */
8273 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8274 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8275 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8276 G_CALLBACK(forward_cb
), t
);
8277 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8281 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8282 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8283 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8284 G_CALLBACK(stop_cb
), t
);
8285 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8289 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8290 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8291 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8292 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8293 G_CALLBACK(js_toggle_cb
), t
);
8294 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8296 t
->uri_entry
= gtk_entry_new();
8297 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8298 G_CALLBACK(activate_uri_entry_cb
), t
);
8299 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8300 G_CALLBACK(entry_key_cb
), t
);
8302 eb1
= gtk_hbox_new(FALSE
, 0);
8303 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8304 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8305 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8308 if (search_string
) {
8310 t
->search_entry
= gtk_entry_new();
8311 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8312 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8313 G_CALLBACK(activate_search_entry_cb
), t
);
8314 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8315 G_CALLBACK(entry_key_cb
), t
);
8316 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8317 eb2
= gtk_hbox_new(FALSE
, 0);
8318 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8319 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8321 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8328 create_buffers(struct tab
*t
)
8330 GtkCellRenderer
*renderer
;
8333 view
= gtk_tree_view_new();
8335 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8337 renderer
= gtk_cell_renderer_text_new();
8338 gtk_tree_view_insert_column_with_attributes
8339 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
8341 renderer
= gtk_cell_renderer_text_new();
8342 gtk_tree_view_insert_column_with_attributes
8343 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8346 gtk_tree_view_set_model
8347 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8353 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8354 GtkTreeViewColumn
*col
, struct tab
*t
)
8359 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8361 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8364 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8365 set_current_tab(id
- 1);
8371 /* after tab reordering/creation/removal */
8378 TAILQ_FOREACH(t
, &tabs
, entry
) {
8379 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8380 if (t
->tab_id
> maxid
)
8383 gtk_widget_show(t
->tab_elems
.sep
);
8386 TAILQ_FOREACH(t
, &tabs
, entry
) {
8387 if (t
->tab_id
== maxid
) {
8388 gtk_widget_hide(t
->tab_elems
.sep
);
8394 /* after active tab change */
8396 recolor_compact_tabs(void)
8402 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8403 TAILQ_FOREACH(t
, &tabs
, entry
)
8404 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8407 curid
= gtk_notebook_get_current_page(notebook
);
8408 TAILQ_FOREACH(t
, &tabs
, entry
)
8409 if (t
->tab_id
== curid
) {
8410 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8411 gtk_widget_modify_fg(t
->tab_elems
.label
,
8412 GTK_STATE_NORMAL
, &color
);
8418 set_current_tab(int page_num
)
8420 buffercmd_abort(get_current_tab());
8421 gtk_notebook_set_current_page(notebook
, page_num
);
8422 recolor_compact_tabs();
8426 undo_close_tab_save(struct tab
*t
)
8430 struct undo
*u1
, *u2
;
8432 WebKitWebHistoryItem
*item
;
8434 if ((uri
= get_uri(t
)) == NULL
)
8437 u1
= g_malloc0(sizeof(struct undo
));
8438 u1
->uri
= g_strdup(uri
);
8440 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8442 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8443 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8446 /* forward history */
8447 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8451 u1
->history
= g_list_prepend(u1
->history
,
8452 webkit_web_history_item_copy(item
));
8453 items
= g_list_next(items
);
8458 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8459 u1
->history
= g_list_prepend(u1
->history
,
8460 webkit_web_history_item_copy(item
));
8464 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8468 u1
->history
= g_list_prepend(u1
->history
,
8469 webkit_web_history_item_copy(item
));
8470 items
= g_list_next(items
);
8473 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8475 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8476 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8477 TAILQ_REMOVE(&undos
, u2
, entry
);
8479 g_list_free(u2
->history
);
8488 delete_tab(struct tab
*t
)
8492 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8498 TAILQ_REMOVE(&tabs
, t
, entry
);
8500 /* Halt all webkit activity. */
8501 abort_favicon_download(t
);
8502 webkit_web_view_stop_loading(t
->wv
);
8504 /* Save the tab, so we can undo the close. */
8505 undo_close_tab_save(t
);
8507 if (browser_mode
== XT_BM_KIOSK
) {
8508 gtk_widget_destroy(t
->uri_entry
);
8509 gtk_widget_destroy(t
->stop
);
8510 gtk_widget_destroy(t
->js_toggle
);
8513 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8514 gtk_widget_destroy(t
->vbox
);
8518 g_source_remove(t
->search_id
);
8520 g_free(t
->user_agent
);
8521 g_free(t
->stylesheet
);
8525 if (TAILQ_EMPTY(&tabs
)) {
8526 if (browser_mode
== XT_BM_KIOSK
)
8527 create_new_tab(home
, NULL
, 1, -1);
8529 create_new_tab(NULL
, NULL
, 1, -1);
8532 /* recreate session */
8533 if (session_autosave
) {
8539 recolor_compact_tabs();
8543 update_statusbar_zoom(struct tab
*t
)
8546 char s
[16] = { '\0' };
8548 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8549 if ((zoom
<= 0.99 || zoom
>= 1.01))
8550 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8551 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8555 setzoom_webkit(struct tab
*t
, int adjust
)
8557 #define XT_ZOOMPERCENT 0.04
8562 show_oops(NULL
, "setzoom_webkit invalid parameters");
8566 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8567 if (adjust
== XT_ZOOM_IN
)
8568 zoom
+= XT_ZOOMPERCENT
;
8569 else if (adjust
== XT_ZOOM_OUT
)
8570 zoom
-= XT_ZOOMPERCENT
;
8571 else if (adjust
> 0)
8572 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8574 show_oops(t
, "setzoom_webkit invalid zoom value");
8578 if (zoom
< XT_ZOOMPERCENT
)
8579 zoom
= XT_ZOOMPERCENT
;
8580 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8581 update_statusbar_zoom(t
);
8585 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8587 struct tab
*t
= (struct tab
*) data
;
8589 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8591 switch (event
->button
) {
8593 set_current_tab(t
->tab_id
);
8604 append_tab(struct tab
*t
)
8609 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8610 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8614 create_sbe(int width
)
8618 sbe
= gtk_entry_new();
8619 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8620 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8621 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8622 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8623 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8624 gtk_widget_set_size_request(sbe
, width
, -1);
8630 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8635 WebKitWebHistoryItem
*item
;
8639 int sbe_p
= 0, sbe_b
= 0,
8642 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8644 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8645 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8649 t
= g_malloc0(sizeof *t
);
8651 if (title
== NULL
) {
8652 title
= "(untitled)";
8656 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8658 /* label + button for tab */
8659 b
= gtk_hbox_new(FALSE
, 0);
8662 #if GTK_CHECK_VERSION(2, 20, 0)
8663 t
->spinner
= gtk_spinner_new();
8665 t
->label
= gtk_label_new(title
);
8666 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8667 gtk_widget_set_size_request(t
->label
, 100, 0);
8668 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8669 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8670 gtk_widget_set_size_request(b
, 130, 0);
8672 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8673 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8674 #if GTK_CHECK_VERSION(2, 20, 0)
8675 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8679 if (browser_mode
== XT_BM_KIOSK
) {
8680 t
->toolbar
= create_kiosk_toolbar(t
);
8681 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8684 t
->toolbar
= create_toolbar(t
);
8686 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8694 t
->browser_win
= create_browser(t
);
8695 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8697 /* oops message for user feedback */
8698 t
->oops
= gtk_entry_new();
8699 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8700 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8701 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8702 gdk_color_parse(XT_COLOR_RED
, &color
);
8703 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8704 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8705 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8708 t
->cmd
= gtk_entry_new();
8709 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8710 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8711 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8712 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8715 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8717 t
->sbe
.statusbar
= gtk_entry_new();
8718 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8719 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8720 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8721 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8723 /* create these widgets only if specified in statusbar_elems */
8725 t
->sbe
.position
= create_sbe(40);
8726 t
->sbe
.zoom
= create_sbe(40);
8727 t
->sbe
.buffercmd
= create_sbe(60);
8729 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8731 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8734 /* gtk widgets cannot be added to a box twice. sbe_* variables
8735 make sure of this */
8736 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8740 GtkWidget
*sep
= gtk_vseparator_new();
8742 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8743 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8744 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8745 FALSE
, FALSE
, FALSE
);
8750 warnx("flag \"%c\" specified more than "
8751 "once in statusbar_elems\n", *p
);
8755 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8756 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8760 warnx("flag \"%c\" specified more than "
8761 "once in statusbar_elems\n", *p
);
8765 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8766 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8770 warnx("flag \"%c\" specified more than "
8771 "once in statusbar_elems\n", *p
);
8775 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8776 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8779 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8784 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8787 t
->buffers
= create_buffers(t
);
8788 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8790 /* xtp meaning is normal by default */
8791 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8793 /* set empty favicon */
8794 xt_icon_from_name(t
, "text-html");
8796 /* and show it all */
8797 gtk_widget_show_all(b
);
8798 gtk_widget_show_all(t
->vbox
);
8800 /* compact tab bar */
8801 t
->tab_elems
.label
= gtk_label_new(title
);
8802 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8803 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8804 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8805 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8807 t
->tab_elems
.eventbox
= gtk_event_box_new();
8808 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8809 t
->tab_elems
.sep
= gtk_vseparator_new();
8811 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8812 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8813 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8814 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8815 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8816 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8818 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8820 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8822 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8825 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8827 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8829 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8832 id
= position
>= 0 ? position
:
8833 gtk_notebook_get_current_page(notebook
) + 1;
8834 if (id
> gtk_notebook_get_n_pages(notebook
))
8837 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8838 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8839 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8840 t
->tab_elems
.eventbox
, id
);
8845 #if GTK_CHECK_VERSION(2, 20, 0)
8846 /* turn spinner off if we are a new tab without uri */
8848 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8849 gtk_widget_hide(t
->spinner
);
8852 /* make notebook tabs reorderable */
8853 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8855 /* compact tabs clickable */
8856 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8857 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8859 g_object_connect(G_OBJECT(t
->cmd
),
8860 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8861 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8862 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8863 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8866 /* reuse wv_button_cb to hide oops */
8867 g_object_connect(G_OBJECT(t
->oops
),
8868 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8871 g_signal_connect(t
->buffers
,
8872 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8873 g_object_connect(G_OBJECT(t
->buffers
),
8874 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8876 g_object_connect(G_OBJECT(t
->wv
),
8877 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8878 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8879 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8880 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8881 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8882 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8883 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8884 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8885 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8886 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8887 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8888 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8889 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8890 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8891 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8893 g_signal_connect(t
->wv
,
8894 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8896 * XXX this puts invalid url in uri_entry and that is undesirable
8899 g_signal_connect(t
->wv
,
8900 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8902 g_signal_connect(t
->wv
,
8903 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8905 /* hijack the unused keys as if we were the browser */
8906 g_object_connect(G_OBJECT(t
->toolbar
),
8907 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8910 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8911 G_CALLBACK(tab_close_cb
), t
);
8917 url_set_visibility();
8918 statusbar_set_visibility();
8921 set_current_tab(t
->tab_id
);
8922 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8927 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8931 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8936 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8937 /* restore the tab's history */
8938 if (u
&& u
->history
) {
8942 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8943 items
= g_list_next(items
);
8946 item
= g_list_nth_data(u
->history
, u
->back
);
8948 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8951 g_list_free(u
->history
);
8953 webkit_web_back_forward_list_clear(t
->bfl
);
8955 recolor_compact_tabs();
8956 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
8961 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8967 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8969 if (gtk_notebook_get_current_page(notebook
) == -1)
8972 TAILQ_FOREACH(t
, &tabs
, entry
) {
8973 if (t
->tab_id
== pn
) {
8974 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
8977 uri
= get_title(t
, TRUE
);
8978 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8984 /* can't use focus_webview here */
8985 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8992 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8995 struct tab
*t
= NULL
, *tt
;
8999 TAILQ_FOREACH(tt
, &tabs
, entry
)
9000 if (tt
->tab_id
== pn
) {
9005 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9007 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9012 menuitem_response(struct tab
*t
)
9014 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9018 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9020 GtkWidget
*menu
, *menu_items
;
9021 GdkEventButton
*bevent
;
9025 if (event
->type
== GDK_BUTTON_PRESS
) {
9026 bevent
= (GdkEventButton
*) event
;
9027 menu
= gtk_menu_new();
9029 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9030 if ((uri
= get_uri(ti
)) == NULL
)
9031 /* XXX make sure there is something to print */
9032 /* XXX add gui pages in here to look purdy */
9034 menu_items
= gtk_menu_item_new_with_label(uri
);
9035 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9036 gtk_widget_show(menu_items
);
9038 g_signal_connect_swapped((menu_items
),
9039 "activate", G_CALLBACK(menuitem_response
),
9043 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9044 bevent
->button
, bevent
->time
);
9046 /* unref object so it'll free itself when popped down */
9047 #if !GTK_CHECK_VERSION(3, 0, 0)
9048 /* XXX does not need unref with gtk+3? */
9049 g_object_ref_sink(menu
);
9050 g_object_unref(menu
);
9053 return (TRUE
/* eat event */);
9056 return (FALSE
/* propagate */);
9060 icon_size_map(int icon_size
)
9062 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9063 icon_size
> GTK_ICON_SIZE_DIALOG
)
9064 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9070 create_button(char *name
, char *stockid
, int size
)
9072 GtkWidget
*button
, *image
;
9076 rcstring
= g_strdup_printf(
9077 "style \"%s-style\"\n"
9079 " GtkWidget::focus-padding = 0\n"
9080 " GtkWidget::focus-line-width = 0\n"
9084 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9085 gtk_rc_parse_string(rcstring
);
9087 button
= gtk_button_new();
9088 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9089 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9091 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9092 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9093 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9094 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9095 gtk_widget_set_name(button
, name
);
9096 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9102 button_set_stockid(GtkWidget
*button
, char *stockid
)
9106 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9107 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9108 gtk_button_set_image(GTK_BUTTON(button
), image
);
9112 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9115 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9118 if (xterm_workaround
== 0)
9122 * xterm doesn't play nice with clipboards because it clears the
9123 * primary when clicked. We rely on primary being set to properly
9124 * handle middle mouse button clicks (paste). So when someone clears
9125 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9126 * other application behavior (as in DON'T clear primary).
9129 p
= gtk_clipboard_wait_for_text(primary
);
9131 if (gdk_property_get(gdk_get_default_root_window(),
9133 gdk_atom_intern("STRING", FALSE
),
9135 1024 * 1024 /* picked out of my butt */,
9141 /* yes sir, we need to NUL the string */
9143 gtk_clipboard_set_text(primary
, p
, -1);
9157 char file
[PATH_MAX
];
9160 vbox
= gtk_vbox_new(FALSE
, 0);
9161 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9162 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9163 #if !GTK_CHECK_VERSION(3, 0, 0)
9164 /* XXX seems to be needed with gtk+2 */
9165 gtk_notebook_set_tab_hborder(notebook
, 0);
9166 gtk_notebook_set_tab_vborder(notebook
, 0);
9168 gtk_notebook_set_scrollable(notebook
, TRUE
);
9169 gtk_notebook_set_show_border(notebook
, FALSE
);
9170 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9172 abtn
= gtk_button_new();
9173 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9174 gtk_widget_set_size_request(arrow
, -1, -1);
9175 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9176 gtk_widget_set_size_request(abtn
, -1, 20);
9178 #if GTK_CHECK_VERSION(2, 20, 0)
9179 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9181 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9183 /* compact tab bar */
9184 tab_bar
= gtk_hbox_new(TRUE
, 0);
9186 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9187 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9188 gtk_widget_set_size_request(vbox
, -1, -1);
9190 g_object_connect(G_OBJECT(notebook
),
9191 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9193 g_object_connect(G_OBJECT(notebook
),
9194 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9195 NULL
, (char *)NULL
);
9196 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9197 G_CALLBACK(arrow_cb
), NULL
);
9199 main_window
= create_window();
9200 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9203 for (i
= 0; i
< LENGTH(icons
); i
++) {
9204 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9205 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9206 l
= g_list_append(l
, pb
);
9208 gtk_window_set_default_icon_list(l
);
9210 /* clipboard work around */
9211 if (xterm_workaround
)
9213 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9214 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9216 gtk_widget_show_all(abtn
);
9217 gtk_widget_show_all(main_window
);
9218 notebook_tab_set_visibility();
9222 set_hook(void **hook
, char *name
)
9225 errx(1, "set_hook");
9227 if (*hook
== NULL
) {
9228 *hook
= dlsym(RTLD_NEXT
, name
);
9230 errx(1, "can't hook %s", name
);
9234 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9236 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9238 g_return_val_if_fail(cookie1
, FALSE
);
9239 g_return_val_if_fail(cookie2
, FALSE
);
9241 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9242 !strcmp (cookie1
->value
, cookie2
->value
) &&
9243 !strcmp (cookie1
->path
, cookie2
->path
) &&
9244 !strcmp (cookie1
->domain
, cookie2
->domain
));
9248 transfer_cookies(void)
9251 SoupCookie
*sc
, *pc
;
9253 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9255 for (;cf
; cf
= cf
->next
) {
9257 sc
= soup_cookie_copy(pc
);
9258 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9261 soup_cookies_free(cf
);
9265 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9270 print_cookie("soup_cookie_jar_delete_cookie", c
);
9272 if (cookies_enabled
== 0)
9275 if (jar
== NULL
|| c
== NULL
)
9278 /* find and remove from persistent jar */
9279 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9281 for (;cf
; cf
= cf
->next
) {
9283 if (soup_cookie_equal(ci
, c
)) {
9284 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9289 soup_cookies_free(cf
);
9291 /* delete from session jar */
9292 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9296 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9298 struct domain
*d
= NULL
;
9302 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9303 jar
, p_cookiejar
, s_cookiejar
);
9305 if (cookies_enabled
== 0)
9308 /* see if we are up and running */
9309 if (p_cookiejar
== NULL
) {
9310 _soup_cookie_jar_add_cookie(jar
, cookie
);
9313 /* disallow p_cookiejar adds, shouldn't happen */
9314 if (jar
== p_cookiejar
)
9318 if (jar
== NULL
|| cookie
== NULL
)
9321 if (enable_cookie_whitelist
&&
9322 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9324 DNPRINTF(XT_D_COOKIE
,
9325 "soup_cookie_jar_add_cookie: reject %s\n",
9327 if (save_rejected_cookies
) {
9328 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9329 show_oops(NULL
, "can't open reject cookie file");
9332 fseek(r_cookie_f
, 0, SEEK_END
);
9333 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9334 cookie
->http_only
? "#HttpOnly_" : "",
9336 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9338 cookie
->secure
? "TRUE" : "FALSE",
9340 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9347 if (!allow_volatile_cookies
)
9351 if (cookie
->expires
== NULL
&& session_timeout
) {
9352 soup_cookie_set_expires(cookie
,
9353 soup_date_new_from_now(session_timeout
));
9354 print_cookie("modified add cookie", cookie
);
9357 /* see if we are white listed for persistence */
9358 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9359 /* add to persistent jar */
9360 c
= soup_cookie_copy(cookie
);
9361 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9362 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9365 /* add to session jar */
9366 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9367 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9373 char file
[PATH_MAX
];
9375 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9376 "soup_cookie_jar_add_cookie");
9377 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9378 "soup_cookie_jar_delete_cookie");
9380 if (cookies_enabled
== 0)
9384 * the following code is intricate due to overriding several libsoup
9386 * do not alter order of these operations.
9389 /* rejected cookies */
9390 if (save_rejected_cookies
)
9391 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9394 /* persistent cookies */
9395 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9396 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9398 /* session cookies */
9399 s_cookiejar
= soup_cookie_jar_new();
9400 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9401 cookie_policy
, (void *)NULL
);
9404 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9408 setup_proxy(char *uri
)
9413 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9414 soup_uri_free(proxy_uri
);
9418 if (http_proxy
!= uri
) {
9425 http_proxy
= g_strdup(uri
);
9426 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9427 suri
= soup_uri_new(http_proxy
);
9428 if (!(suri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(suri
)))
9429 g_object_set(session
, "proxy-uri", proxy_uri
,
9432 soup_uri_free(suri
);
9437 set_http_proxy(char *proxy
)
9444 /* see if we need to clear it instead */
9445 if (strlen(proxy
) == 0) {
9450 uri
= soup_uri_new(proxy
);
9451 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9462 send_cmd_to_socket(char *cmd
)
9465 struct sockaddr_un sa
;
9467 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9468 warnx("%s: socket", __func__
);
9472 sa
.sun_family
= AF_UNIX
;
9473 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9474 work_dir
, XT_SOCKET_FILE
);
9477 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9478 warnx("%s: connect", __func__
);
9482 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9483 warnx("%s: send", __func__
);
9494 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9497 char str
[XT_MAX_URL_LENGTH
];
9498 socklen_t t
= sizeof(struct sockaddr_un
);
9499 struct sockaddr_un sa
;
9504 gint fd
= g_io_channel_unix_get_fd(source
);
9506 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9511 if (getpeereid(s
, &uid
, &gid
) == -1) {
9515 if (uid
!= getuid() || gid
!= getgid()) {
9516 warnx("unauthorized user");
9522 warnx("not a valid user");
9526 n
= recv(s
, str
, sizeof(str
), 0);
9530 tt
= TAILQ_LAST(&tabs
, tab_list
);
9531 cmd_execute(tt
, str
);
9539 struct sockaddr_un sa
;
9541 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9542 warn("is_running: socket");
9546 sa
.sun_family
= AF_UNIX
;
9547 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9548 work_dir
, XT_SOCKET_FILE
);
9551 /* connect to see if there is a listener */
9552 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9553 rv
= 0; /* not running */
9555 rv
= 1; /* already running */
9566 struct sockaddr_un sa
;
9568 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9569 warn("build_socket: socket");
9573 sa
.sun_family
= AF_UNIX
;
9574 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9575 work_dir
, XT_SOCKET_FILE
);
9578 /* connect to see if there is a listener */
9579 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9580 /* no listener so we will */
9581 unlink(sa
.sun_path
);
9583 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9584 warn("build_socket: bind");
9588 if (listen(s
, 1) == -1) {
9589 warn("build_socket: listen");
9602 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9603 GtkTreeIter
*iter
, struct tab
*t
)
9607 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9615 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9616 GtkTreeIter
*iter
, struct tab
*t
)
9620 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9621 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9622 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9629 completion_add_uri(const gchar
*uri
)
9633 /* add uri to list_store */
9634 gtk_list_store_append(completion_model
, &iter
);
9635 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9639 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9640 GtkTreeIter
*iter
, gpointer user_data
)
9643 gboolean match
= FALSE
;
9645 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9651 match
= match_uri(value
, key
);
9658 completion_add(struct tab
*t
)
9660 /* enable completion for tab */
9661 t
->completion
= gtk_entry_completion_new();
9662 gtk_entry_completion_set_text_column(t
->completion
, 0);
9663 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9664 gtk_entry_completion_set_model(t
->completion
,
9665 GTK_TREE_MODEL(completion_model
));
9666 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9668 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9669 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9670 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9671 G_CALLBACK(completion_select_cb
), t
);
9672 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9673 G_CALLBACK(completion_hover_cb
), t
);
9681 if (stat(dir
, &sb
)) {
9682 if (mkdir(dir
, S_IRWXU
) == -1)
9683 err(1, "mkdir %s", dir
);
9685 err(1, "stat %s", dir
);
9687 if (S_ISDIR(sb
.st_mode
) == 0)
9688 errx(1, "%s not a dir", dir
);
9689 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9690 warnx("fixing invalid permissions on %s", dir
);
9691 if (chmod(dir
, S_IRWXU
) == -1)
9692 err(1, "chmod %s", dir
);
9700 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9706 main(int argc
, char *argv
[])
9709 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9710 char conf
[PATH_MAX
] = { '\0' };
9711 char file
[PATH_MAX
];
9712 char *env_proxy
= NULL
;
9716 struct sigaction sact
;
9717 GIOChannel
*channel
;
9722 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9726 RB_INIT(&downloads
);
9730 TAILQ_INIT(&aliases
);
9735 /* fiddle with ulimits */
9736 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9739 /* just use them all */
9740 rlp
.rlim_cur
= rlp
.rlim_max
;
9741 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9743 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9745 else if (rlp
.rlim_cur
<= 256)
9746 startpage_add("%s requires at least 256 file "
9747 "descriptors, currently it has up to %d available",
9748 __progname
, rlp
.rlim_cur
);
9751 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9760 errx(0 , "Version: %s", version
);
9763 strlcpy(conf
, optarg
, sizeof(conf
));
9766 strlcpy(named_session
, optarg
, sizeof(named_session
));
9787 gnutls_global_init();
9789 /* generate session keys for xtp pages */
9790 generate_xtp_session_key(&dl_session_key
);
9791 generate_xtp_session_key(&hl_session_key
);
9792 generate_xtp_session_key(&cl_session_key
);
9793 generate_xtp_session_key(&fl_session_key
);
9796 if (!g_thread_supported()) {
9797 g_thread_init(NULL
);
9799 gdk_threads_enter();
9801 gtk_init(&argc
, &argv
);
9804 bzero(&sact
, sizeof(sact
));
9805 sigemptyset(&sact
.sa_mask
);
9806 sact
.sa_handler
= sigchild
;
9807 sact
.sa_flags
= SA_NOCLDSTOP
;
9808 sigaction(SIGCHLD
, &sact
, NULL
);
9810 /* set download dir */
9811 pwd
= getpwuid(getuid());
9813 errx(1, "invalid user %d", getuid());
9814 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9816 /* compile buffer command regexes */
9819 /* set default string settings */
9820 home
= g_strdup("https://www.cyphertite.com");
9821 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9822 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9823 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9824 cmd_font_name
= g_strdup("monospace normal 9");
9825 oops_font_name
= g_strdup("monospace normal 9");
9826 statusbar_font_name
= g_strdup("monospace normal 9");
9827 tabbar_font_name
= g_strdup("monospace normal 9");
9828 statusbar_elems
= g_strdup("BP");
9830 /* read config file */
9831 if (strlen(conf
) == 0)
9832 snprintf(conf
, sizeof conf
, "%s/.%s",
9833 pwd
->pw_dir
, XT_CONF_FILE
);
9834 config_parse(conf
, 0);
9837 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9838 oops_font
= pango_font_description_from_string(oops_font_name
);
9839 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9840 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9842 /* working directory */
9843 if (strlen(work_dir
) == 0)
9844 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9845 pwd
->pw_dir
, XT_DIR
);
9848 /* icon cache dir */
9849 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9853 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9857 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9858 work_dir
, XT_SESSIONS_DIR
);
9859 xxx_dir(sessions_dir
);
9861 /* runtime settings that can override config file */
9862 if (runtime_settings
[0] != '\0')
9863 config_parse(runtime_settings
, 1);
9866 if (!strcmp(download_dir
, pwd
->pw_dir
))
9867 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9868 xxx_dir(download_dir
);
9870 /* favorites file */
9871 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9872 if (stat(file
, &sb
)) {
9873 warnx("favorites file doesn't exist, creating it");
9874 if ((f
= fopen(file
, "w")) == NULL
)
9875 err(1, "favorites");
9879 /* quickmarks file */
9880 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9881 if (stat(file
, &sb
)) {
9882 warnx("quickmarks file doesn't exist, creating it");
9883 if ((f
= fopen(file
, "w")) == NULL
)
9884 err(1, "quickmarks");
9889 session
= webkit_get_default_session();
9894 if (stat(ssl_ca_file
, &sb
)) {
9895 warnx("no CA file: %s", ssl_ca_file
);
9896 g_free(ssl_ca_file
);
9899 g_object_set(session
,
9900 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9901 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9906 env_proxy
= getenv("http_proxy");
9908 setup_proxy(env_proxy
);
9910 setup_proxy(http_proxy
);
9913 send_cmd_to_socket(argv
[0]);
9917 /* set some connection parameters */
9918 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9919 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9922 /* see if there is already an xxxterm running */
9923 if (single_instance
&& is_running()) {
9925 warnx("already running");
9930 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9931 send_cmd_to_socket(cmd
);
9941 /* uri completion */
9942 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9945 buffers_store
= gtk_list_store_new
9946 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9952 notebook_tab_set_visibility();
9954 if (save_global_history
)
9955 restore_global_history();
9957 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9958 restore_saved_tabs();
9960 a
.s
= named_session
;
9961 a
.i
= XT_SES_DONOTHING
;
9962 open_tabs(NULL
, &a
);
9965 /* see if we have an exception */
9966 if (!TAILQ_EMPTY(&spl
)) {
9967 create_new_tab("about:startpage", NULL
, focus
, -1);
9972 create_new_tab(argv
[0], NULL
, focus
, -1);
9979 if (TAILQ_EMPTY(&tabs
))
9980 create_new_tab(home
, NULL
, 1, -1);
9983 if ((s
= build_socket()) != -1) {
9984 channel
= g_io_channel_unix_new(s
);
9985 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
9990 if (!g_thread_supported()) {
9991 gdk_threads_leave();
9994 gnutls_global_deinit();