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
;
1619 SoupURI
*uri
= NULL
;
1621 url_out
= match_alias(url_in
);
1622 if (url_out
!= NULL
)
1626 uri
= soup_uri_new(url_in
);
1627 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
1628 /* invalid URI so search instead */
1629 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1630 url_out
= g_strdup_printf(search_string
, enc_search
);
1636 /* XXX not sure about this heuristic */
1637 if (stat(url_in
, &sb
) == 0)
1638 url_out
= g_strdup_printf("file://%s", url_in
);
1640 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1642 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1650 load_uri(struct tab
*t
, gchar
*uri
)
1653 gchar
*newuri
= NULL
;
1659 /* Strip leading spaces. */
1660 while (*uri
&& isspace(*uri
))
1663 if (strlen(uri
) == 0) {
1668 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1670 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1671 for (i
= 0; i
< LENGTH(about_list
); i
++)
1672 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1673 bzero(&args
, sizeof args
);
1674 about_list
[i
].func(t
, &args
);
1675 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1679 show_oops(t
, "invalid about page");
1683 if (valid_url_type(uri
)) {
1684 newuri
= guess_url_type(uri
);
1688 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1690 webkit_web_view_load_uri(t
->wv
, uri
);
1697 get_uri(struct tab
*t
)
1699 const gchar
*uri
= NULL
;
1701 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1703 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1704 uri
= webkit_web_view_get_uri(t
->wv
);
1706 /* use tmp_uri to make sure it is g_freed */
1709 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1710 about_list
[t
->xtp_meaning
].name
);
1717 get_title(struct tab
*t
, bool window
)
1719 const gchar
*set
= NULL
, *title
= NULL
;
1720 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1722 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1723 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1726 title
= webkit_web_view_get_title(t
->wv
);
1727 if ((set
= title
? title
: get_uri(t
)))
1731 set
= window
? XT_NAME
: "(untitled)";
1737 add_alias(struct settings
*s
, char *line
)
1740 struct alias
*a
= NULL
;
1742 if (s
== NULL
|| line
== NULL
) {
1743 show_oops(NULL
, "add_alias invalid parameters");
1748 a
= g_malloc(sizeof(*a
));
1750 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1751 show_oops(NULL
, "add_alias: incomplete alias definition");
1754 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1755 show_oops(NULL
, "add_alias: invalid alias definition");
1759 a
->a_name
= g_strdup(alias
);
1760 a
->a_uri
= g_strdup(l
);
1762 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1764 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1774 add_mime_type(struct settings
*s
, char *line
)
1778 struct mime_type
*m
= NULL
;
1779 int downloadfirst
= 0;
1781 /* XXX this could be smarter */
1783 if (line
== NULL
|| strlen(line
) == 0) {
1784 show_oops(NULL
, "add_mime_type invalid parameters");
1793 m
= g_malloc(sizeof(*m
));
1795 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1796 show_oops(NULL
, "add_mime_type: invalid mime_type");
1799 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1800 mime_type
[strlen(mime_type
) - 1] = '\0';
1805 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1806 show_oops(NULL
, "add_mime_type: invalid mime_type");
1810 m
->mt_type
= g_strdup(mime_type
);
1811 m
->mt_action
= g_strdup(l
);
1812 m
->mt_download
= downloadfirst
;
1814 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1815 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1817 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1827 find_mime_type(char *mime_type
)
1829 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1831 TAILQ_FOREACH(m
, &mtl
, entry
) {
1832 if (m
->mt_default
&&
1833 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1836 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1849 walk_mime_type(struct settings
*s
,
1850 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1852 struct mime_type
*m
;
1855 if (s
== NULL
|| cb
== NULL
) {
1856 show_oops(NULL
, "walk_mime_type invalid parameters");
1860 TAILQ_FOREACH(m
, &mtl
, entry
) {
1861 str
= g_strdup_printf("%s%s --> %s",
1863 m
->mt_default
? "*" : "",
1865 cb(s
, str
, cb_args
);
1871 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1876 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1879 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1881 /* treat *.moo.com the same as .moo.com */
1882 if (str
[0] == '*' && str
[1] == '.')
1884 else if (str
[0] == '.')
1889 d
= g_malloc(sizeof *d
);
1891 d
->d
= g_strdup_printf(".%s", str
);
1893 d
->d
= g_strdup(str
);
1896 if (RB_INSERT(domain_list
, wl
, d
))
1899 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1910 add_cookie_wl(struct settings
*s
, char *entry
)
1912 wl_add(entry
, &c_wl
, 1);
1917 walk_cookie_wl(struct settings
*s
,
1918 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1922 if (s
== NULL
|| cb
== NULL
) {
1923 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1927 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1928 cb(s
, d
->d
, cb_args
);
1932 walk_js_wl(struct settings
*s
,
1933 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1937 if (s
== NULL
|| cb
== NULL
) {
1938 show_oops(NULL
, "walk_js_wl invalid parameters");
1942 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1943 cb(s
, d
->d
, cb_args
);
1947 add_js_wl(struct settings
*s
, char *entry
)
1949 wl_add(entry
, &js_wl
, 1 /* persistent */);
1954 wl_find(const gchar
*search
, struct domain_list
*wl
)
1957 struct domain
*d
= NULL
, dfind
;
1960 if (search
== NULL
|| wl
== NULL
)
1962 if (strlen(search
) < 2)
1965 if (search
[0] != '.')
1966 s
= g_strdup_printf(".%s", search
);
1968 s
= g_strdup(search
);
1970 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1973 d
= RB_FIND(domain_list
, wl
, &dfind
);
1987 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1993 if (s
== NULL
|| wl
== NULL
)
1996 if (!strncmp(s
, "http://", strlen("http://")))
1997 s
= &s
[strlen("http://")];
1998 else if (!strncmp(s
, "https://", strlen("https://")))
1999 s
= &s
[strlen("https://")];
2004 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2005 /* chop string at first slash */
2006 if (s
[i
] == '/' || s
[i
] == '\0') {
2009 r
= wl_find(ss
, wl
);
2018 settings_add(char *var
, char *val
)
2025 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2026 if (strcmp(var
, rs
[i
].name
))
2030 if (rs
[i
].s
->set(&rs
[i
], val
))
2031 errx(1, "invalid value for %s: %s", var
, val
);
2035 switch (rs
[i
].type
) {
2044 errx(1, "invalid sval for %s",
2058 errx(1, "invalid type for %s", var
);
2067 config_parse(char *filename
, int runtime
)
2070 char *line
, *cp
, *var
, *val
;
2071 size_t len
, lineno
= 0;
2073 char file
[PATH_MAX
];
2076 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2078 if (filename
== NULL
)
2081 if (runtime
&& runtime_settings
[0] != '\0') {
2082 snprintf(file
, sizeof file
, "%s/%s",
2083 work_dir
, runtime_settings
);
2084 if (stat(file
, &sb
)) {
2085 warnx("runtime file doesn't exist, creating it");
2086 if ((f
= fopen(file
, "w")) == NULL
)
2088 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2092 strlcpy(file
, filename
, sizeof file
);
2094 if ((config
= fopen(file
, "r")) == NULL
) {
2095 warn("config_parse: cannot open %s", filename
);
2100 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2101 if (feof(config
) || ferror(config
))
2105 cp
+= (long)strspn(cp
, WS
);
2106 if (cp
[0] == '\0') {
2112 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2113 startpage_add("invalid configuration file entry: %s",
2116 cp
+= (long)strspn(cp
, WS
);
2118 if ((val
= strsep(&cp
, "\0")) == NULL
)
2121 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2122 handled
= settings_add(var
, val
);
2124 startpage_add("invalid configuration file entry: %s=%s",
2134 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2140 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2144 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2147 JSStringGetUTF8CString(jsref
, s
, l
);
2148 JSStringRelease(jsref
);
2154 disable_hints(struct tab
*t
)
2156 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2157 bzero(t
->hint_num
, sizeof t
->hint_num
);
2158 run_script(t
, "vimprobable_clear()");
2160 t
->hint_mode
= XT_HINT_NONE
;
2164 enable_hints(struct tab
*t
)
2166 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2167 run_script(t
, "vimprobable_show_hints()");
2169 t
->hint_mode
= XT_HINT_NONE
;
2172 #define XT_JS_OPEN ("open;")
2173 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2174 #define XT_JS_FIRE ("fire;")
2175 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2176 #define XT_JS_FOUND ("found;")
2177 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2180 run_script(struct tab
*t
, char *s
)
2182 JSGlobalContextRef ctx
;
2183 WebKitWebFrame
*frame
;
2185 JSValueRef val
, exception
;
2188 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2189 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2191 frame
= webkit_web_view_get_main_frame(t
->wv
);
2192 ctx
= webkit_web_frame_get_global_context(frame
);
2194 str
= JSStringCreateWithUTF8CString(s
);
2195 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2196 NULL
, 0, &exception
);
2197 JSStringRelease(str
);
2199 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2201 es
= js_ref_to_string(ctx
, exception
);
2202 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2206 es
= js_ref_to_string(ctx
, val
);
2207 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2209 /* handle return value right here */
2210 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2213 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2216 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2217 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2218 &es
[XT_JS_FIRE_LEN
]);
2223 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2224 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2235 hint(struct tab
*t
, struct karg
*args
)
2238 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2240 if (t
->hints_on
== 0)
2249 apply_style(struct tab
*t
)
2251 g_object_set(G_OBJECT(t
->settings
),
2252 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2256 userstyle(struct tab
*t
, struct karg
*args
)
2258 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2262 g_object_set(G_OBJECT(t
->settings
),
2263 "user-stylesheet-uri", NULL
, (char *)NULL
);
2272 * Doesn't work fully, due to the following bug:
2273 * https://bugs.webkit.org/show_bug.cgi?id=51747
2276 restore_global_history(void)
2278 char file
[PATH_MAX
];
2283 const char delim
[3] = {'\\', '\\', '\0'};
2285 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2287 if ((f
= fopen(file
, "r")) == NULL
) {
2288 warnx("%s: fopen", __func__
);
2293 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2294 if (feof(f
) || ferror(f
))
2297 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2298 if (feof(f
) || ferror(f
)) {
2300 warnx("%s: broken history file\n", __func__
);
2304 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2305 webkit_web_history_item_new_with_data(uri
, title
);
2306 h
= g_malloc(sizeof(struct history
));
2307 h
->uri
= g_strdup(uri
);
2308 h
->title
= g_strdup(title
);
2309 RB_INSERT(history_list
, &hl
, h
);
2310 completion_add_uri(h
->uri
);
2312 warnx("%s: failed to restore history\n", __func__
);
2328 save_global_history_to_disk(struct tab
*t
)
2330 char file
[PATH_MAX
];
2334 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2336 if ((f
= fopen(file
, "w")) == NULL
) {
2337 show_oops(t
, "%s: global history file: %s",
2338 __func__
, strerror(errno
));
2342 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2343 if (h
->uri
&& h
->title
)
2344 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2353 quit(struct tab
*t
, struct karg
*args
)
2355 if (save_global_history
)
2356 save_global_history_to_disk(t
);
2364 open_tabs(struct tab
*t
, struct karg
*a
)
2366 char file
[PATH_MAX
];
2370 struct tab
*ti
, *tt
;
2375 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2376 if ((f
= fopen(file
, "r")) == NULL
)
2379 ti
= TAILQ_LAST(&tabs
, tab_list
);
2382 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2383 if (feof(f
) || ferror(f
))
2386 /* retrieve session name */
2387 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2388 strlcpy(named_session
,
2389 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2390 sizeof named_session
);
2394 if (uri
&& strlen(uri
))
2395 create_new_tab(uri
, NULL
, 1, -1);
2401 /* close open tabs */
2402 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2404 tt
= TAILQ_FIRST(&tabs
);
2424 restore_saved_tabs(void)
2426 char file
[PATH_MAX
];
2427 int unlink_file
= 0;
2432 snprintf(file
, sizeof file
, "%s/%s",
2433 sessions_dir
, XT_RESTART_TABS_FILE
);
2434 if (stat(file
, &sb
) == -1)
2435 a
.s
= XT_SAVED_TABS_FILE
;
2438 a
.s
= XT_RESTART_TABS_FILE
;
2441 a
.i
= XT_SES_DONOTHING
;
2442 rv
= open_tabs(NULL
, &a
);
2451 save_tabs(struct tab
*t
, struct karg
*a
)
2453 char file
[PATH_MAX
];
2455 int num_tabs
= 0, i
;
2456 struct tab
**stabs
= NULL
;
2461 snprintf(file
, sizeof file
, "%s/%s",
2462 sessions_dir
, named_session
);
2464 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2466 if ((f
= fopen(file
, "w")) == NULL
) {
2467 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2471 /* save session name */
2472 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2474 /* Save tabs, in the order they are arranged in the notebook. */
2475 num_tabs
= sort_tabs_by_page_num(&stabs
);
2477 for (i
= 0; i
< num_tabs
; i
++)
2479 if (get_uri(stabs
[i
]) != NULL
)
2480 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2481 else if (gtk_entry_get_text(GTK_ENTRY(
2482 stabs
[i
]->uri_entry
)))
2483 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2484 stabs
[i
]->uri_entry
)));
2489 /* try and make sure this gets to disk NOW. XXX Backup first? */
2490 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2491 show_oops(t
, "May not have managed to save session: %s",
2501 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2513 run_page_script(struct tab
*t
, struct karg
*args
)
2516 char *tmp
, script
[PATH_MAX
];
2518 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2519 if (tmp
[0] == '\0') {
2520 show_oops(t
, "no script specified");
2524 if ((uri
= get_uri(t
)) == NULL
) {
2525 show_oops(t
, "tab is empty, not running script");
2530 snprintf(script
, sizeof script
, "%s/%s",
2531 pwd
->pw_dir
, &tmp
[1]);
2533 strlcpy(script
, tmp
, sizeof script
);
2537 show_oops(t
, "can't fork to run script");
2547 execlp(script
, script
, uri
, (void *)NULL
);
2557 yank_uri(struct tab
*t
, struct karg
*args
)
2560 GtkClipboard
*clipboard
;
2562 if ((uri
= get_uri(t
)) == NULL
)
2565 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2566 gtk_clipboard_set_text(clipboard
, uri
, -1);
2572 paste_uri(struct tab
*t
, struct karg
*args
)
2574 GtkClipboard
*clipboard
;
2575 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2577 gchar
*p
= NULL
, *uri
;
2579 /* try primary clipboard first */
2580 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2581 p
= gtk_clipboard_wait_for_text(clipboard
);
2583 /* if it failed get whatever text is in cut_buffer0 */
2584 if (p
== NULL
&& xterm_workaround
)
2585 if (gdk_property_get(gdk_get_default_root_window(),
2587 gdk_atom_intern("STRING", FALSE
),
2589 1024 * 1024 /* picked out of my butt */,
2595 /* yes sir, we need to NUL the string */
2601 while (*uri
&& isspace(*uri
))
2603 if (strlen(uri
) == 0) {
2604 show_oops(t
, "empty paste buffer");
2607 if (guess_search
== 0 && valid_url_type(uri
)) {
2608 /* we can be clever and paste this in search box */
2609 show_oops(t
, "not a valid URL");
2613 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2615 else if (args
->i
== XT_PASTE_NEW_TAB
)
2616 create_new_tab(uri
, NULL
, 1, -1);
2627 find_domain(const gchar
*s
, int toplevel
)
2635 uri
= soup_uri_new(s
);
2637 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2641 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2642 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2643 while(--p
>= uri
->host
&& *p
!= '.');
2650 if (uri
->port
== 80)
2651 ret
= g_strdup_printf(".%s", p
);
2653 ret
= g_strdup_printf(".%s:%d", p
, uri
->port
);
2661 toggle_cwl(struct tab
*t
, struct karg
*args
)
2672 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2674 if (uri
== NULL
|| dom
== NULL
||
2675 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2676 show_oops(t
, "Can't toggle domain in cookie white list");
2679 d
= wl_find(dom
, &c_wl
);
2686 if (args
->i
& XT_WL_TOGGLE
)
2688 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2690 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2694 /* enable cookies for domain */
2695 wl_add(dom
, &c_wl
, 0);
2697 /* disable cookies for domain */
2698 RB_REMOVE(domain_list
, &c_wl
, d
);
2700 if (args
->i
& XT_WL_RELOAD
)
2701 webkit_web_view_reload(t
->wv
);
2709 toggle_js(struct tab
*t
, struct karg
*args
)
2719 g_object_get(G_OBJECT(t
->settings
),
2720 "enable-scripts", &es
, (char *)NULL
);
2721 if (args
->i
& XT_WL_TOGGLE
)
2723 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2725 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2731 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2733 if (uri
== NULL
|| dom
== NULL
||
2734 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2735 show_oops(t
, "Can't toggle domain in JavaScript white list");
2740 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2741 wl_add(dom
, &js_wl
, 0 /* session */);
2743 d
= wl_find(dom
, &js_wl
);
2745 RB_REMOVE(domain_list
, &js_wl
, d
);
2746 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2748 g_object_set(G_OBJECT(t
->settings
),
2749 "enable-scripts", es
, (char *)NULL
);
2750 g_object_set(G_OBJECT(t
->settings
),
2751 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2752 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2754 if (args
->i
& XT_WL_RELOAD
)
2755 webkit_web_view_reload(t
->wv
);
2763 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2767 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2770 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2775 toggle_src(struct tab
*t
, struct karg
*args
)
2782 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2783 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2784 webkit_web_view_reload(t
->wv
);
2790 focus_webview(struct tab
*t
)
2795 /* only grab focus if we are visible */
2796 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2797 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2801 focus(struct tab
*t
, struct karg
*args
)
2803 if (t
== NULL
|| args
== NULL
)
2809 if (args
->i
== XT_FOCUS_URI
)
2810 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2811 else if (args
->i
== XT_FOCUS_SEARCH
)
2812 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2818 stats(struct tab
*t
, struct karg
*args
)
2820 char *page
, *body
, *s
, line
[64 * 1024];
2821 uint64_t line_count
= 0;
2825 show_oops(NULL
, "stats invalid parameters");
2828 if (save_rejected_cookies
) {
2829 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2831 s
= fgets(line
, sizeof line
, r_cookie_f
);
2832 if (s
== NULL
|| feof(r_cookie_f
) ||
2838 snprintf(line
, sizeof line
,
2839 "<br/>Cookies blocked(*) total: %llu", line_count
);
2841 show_oops(t
, "Can't open blocked cookies file: %s",
2845 body
= g_strdup_printf(
2846 "Cookies blocked(*) this session: %llu"
2848 "<p><small><b>*</b> results vary based on settings</small></p>",
2852 page
= get_html_page("Statistics", body
, "", 0);
2855 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2862 marco(struct tab
*t
, struct karg
*args
)
2864 char *page
, line
[64 * 1024];
2868 show_oops(NULL
, "marco invalid parameters");
2871 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2873 page
= get_html_page("Marco Sez...", line
, "", 0);
2875 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2882 blank(struct tab
*t
, struct karg
*args
)
2885 show_oops(NULL
, "blank invalid parameters");
2887 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2893 about(struct tab
*t
, struct karg
*args
)
2898 show_oops(NULL
, "about invalid parameters");
2900 body
= g_strdup_printf("<b>Version: %s</b><p>"
2903 "<li>Marco Peereboom <marco@peereboom.us></li>"
2904 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2905 "<li>Edd Barrett <vext01@gmail.com> </li>"
2906 "<li>Todd T. Fries <todd@fries.net> </li>"
2907 "<li>Raphael Graf <r@undefined.ch> </li>"
2909 "Copyrights and licenses can be found on the XXXTerm "
2910 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2914 page
= get_html_page("About", body
, "", 0);
2917 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2924 help(struct tab
*t
, struct karg
*args
)
2926 char *page
, *head
, *body
;
2929 show_oops(NULL
, "help invalid parameters");
2931 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2932 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2934 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
2935 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2936 "cgi-bin/man-cgi?xxxterm</a>";
2938 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
2940 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2947 startpage(struct tab
*t
, struct karg
*args
)
2949 char *page
, *body
, *b
;
2953 show_oops(NULL
, "startpage invalid parameters");
2955 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
2957 TAILQ_FOREACH(s
, &spl
, entry
) {
2959 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
2963 page
= get_html_page("Startup Exception", body
, "", 0);
2966 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
2973 startpage_add(const char *fmt
, ...)
2983 if (vasprintf(&msg
, fmt
, ap
) == -1)
2984 errx(1, "startpage_add failed");
2987 s
= g_malloc0(sizeof *s
);
2990 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
2994 * update all favorite tabs apart from one. Pass NULL if
2995 * you want to update all.
2998 update_favorite_tabs(struct tab
*apart_from
)
3001 if (!updating_fl_tabs
) {
3002 updating_fl_tabs
= 1; /* stop infinite recursion */
3003 TAILQ_FOREACH(t
, &tabs
, entry
)
3004 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3005 && (t
!= apart_from
))
3006 xtp_page_fl(t
, NULL
);
3007 updating_fl_tabs
= 0;
3011 /* show a list of favorites (bookmarks) */
3013 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3015 char file
[PATH_MAX
];
3017 char *uri
= NULL
, *title
= NULL
;
3018 size_t len
, lineno
= 0;
3020 char *body
, *tmp
, *page
= NULL
;
3021 const char delim
[3] = {'\\', '\\', '\0'};
3023 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3026 warn("%s: bad param", __func__
);
3028 /* new session key */
3029 if (!updating_fl_tabs
)
3030 generate_xtp_session_key(&fl_session_key
);
3032 /* open favorites */
3033 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3034 if ((f
= fopen(file
, "r")) == NULL
) {
3035 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3040 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3041 "<th style='width: 40px'>#</th><th>Link</th>"
3042 "<th style='width: 40px'>Rm</th></tr>\n");
3045 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3046 if (feof(f
) || ferror(f
))
3048 if (strlen(title
) == 0 || title
[0] == '#') {
3054 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3055 if (feof(f
) || ferror(f
)) {
3056 show_oops(t
, "favorites file corrupt");
3062 body
= g_strdup_printf("%s<tr>"
3064 "<td><a href='%s'>%s</a></td>"
3065 "<td style='text-align: center'>"
3066 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3068 body
, i
, uri
, title
,
3069 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3081 /* if none, say so */
3084 body
= g_strdup_printf("%s<tr>"
3085 "<td colspan='3' style='text-align: center'>"
3086 "No favorites - To add one use the 'favadd' command."
3087 "</td></tr>", body
);
3092 body
= g_strdup_printf("%s</table>", body
);
3102 page
= get_html_page("Favorites", body
, "", 1);
3103 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3107 update_favorite_tabs(t
);
3116 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3117 size_t cert_count
, char *title
)
3119 gnutls_datum_t cinfo
;
3123 body
= g_strdup("");
3125 for (i
= 0; i
< cert_count
; i
++) {
3126 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3131 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3132 body
, i
, cinfo
.data
);
3133 gnutls_free(cinfo
.data
);
3137 tmp
= get_html_page(title
, body
, "", 0);
3140 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3145 ca_cmd(struct tab
*t
, struct karg
*args
)
3148 int rv
= 1, certs
= 0, certs_read
;
3151 gnutls_x509_crt_t
*c
= NULL
;
3152 char *certs_buf
= NULL
, *s
;
3154 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3155 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3159 if (fstat(fileno(f
), &sb
) == -1) {
3160 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3164 certs_buf
= g_malloc(sb
.st_size
+ 1);
3165 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3166 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3169 certs_buf
[sb
.st_size
] = '\0';
3172 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3174 s
+= strlen("BEGIN CERTIFICATE");
3177 bzero(&dt
, sizeof dt
);
3178 dt
.data
= (unsigned char *)certs_buf
;
3179 dt
.size
= sb
.st_size
;
3180 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3181 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3182 GNUTLS_X509_FMT_PEM
, 0);
3183 if (certs_read
<= 0) {
3184 show_oops(t
, "No cert(s) available");
3187 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3200 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3204 struct addrinfo hints
, *res
= NULL
, *ai
;
3205 int rv
= -1, s
= -1, on
, error
;
3208 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3209 show_oops(t
, "invalid URI");
3213 su
= soup_uri_new(uri
);
3215 show_oops(t
, "invalid soup URI");
3218 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3219 show_oops(t
, "invalid HTTPS URI");
3223 snprintf(port
, sizeof port
, "%d", su
->port
);
3224 bzero(&hints
, sizeof(struct addrinfo
));
3225 hints
.ai_flags
= AI_CANONNAME
;
3226 hints
.ai_family
= AF_UNSPEC
;
3227 hints
.ai_socktype
= SOCK_STREAM
;
3229 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3230 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3234 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3235 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3238 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3240 show_oops(t
, "socket failed: %s", strerror(errno
));
3243 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3244 sizeof(on
)) == -1) {
3245 show_oops(t
, "setsockopt failed: %s", strerror(errno
));
3248 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == -1) {
3249 show_oops(t
, "connect failed: %s", strerror(errno
));
3257 strlcpy(domain
, su
->host
, domain_sz
);
3264 if (rv
== -1 && s
!= -1)
3271 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3274 gnutls_deinit(gsession
);
3276 gnutls_certificate_free_credentials(xcred
);
3282 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3283 gnutls_certificate_credentials_t
*xc
)
3285 gnutls_certificate_credentials_t xcred
;
3286 gnutls_session_t gsession
;
3289 if (gs
== NULL
|| xc
== NULL
)
3295 gnutls_certificate_allocate_credentials(&xcred
);
3296 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3297 GNUTLS_X509_FMT_PEM
);
3299 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3300 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3301 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3302 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3303 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3304 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3306 gnutls_error_is_fatal(rv
),
3307 gnutls_strerror_name(rv
));
3308 stop_tls(gsession
, xcred
);
3312 gnutls_credentials_type_t cred
;
3313 cred
= gnutls_auth_get_type(gsession
);
3314 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3315 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3316 stop_tls(gsession
, xcred
);
3328 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3332 const gnutls_datum_t
*cl
;
3333 gnutls_x509_crt_t
*all_certs
;
3336 if (certs
== NULL
|| cert_count
== NULL
)
3338 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3340 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3344 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3345 for (i
= 0; i
< len
; i
++) {
3346 gnutls_x509_crt_init(&all_certs
[i
]);
3347 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3348 GNUTLS_X509_FMT_PEM
< 0)) {
3362 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3366 for (i
= 0; i
< cert_count
; i
++)
3367 gnutls_x509_crt_deinit(certs
[i
]);
3372 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3374 GdkColor c_text
, c_base
;
3376 gdk_color_parse(text
, &c_text
);
3377 gdk_color_parse(base
, &c_base
);
3379 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3380 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3381 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3382 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3384 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3385 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3386 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3387 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3391 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3392 size_t cert_count
, char *domain
)
3395 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3400 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3403 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3404 if ((f
= fopen(file
, "w")) == NULL
) {
3405 show_oops(t
, "Can't create cert file %s %s",
3406 file
, strerror(errno
));
3410 for (i
= 0; i
< cert_count
; i
++) {
3411 cert_buf_sz
= sizeof cert_buf
;
3412 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3413 cert_buf
, &cert_buf_sz
)) {
3414 show_oops(t
, "gnutls_x509_crt_export failed");
3417 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3418 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3423 /* not the best spot but oh well */
3424 gdk_color_parse("lightblue", &color
);
3425 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3426 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3439 load_compare_cert(struct tab
*t
, struct karg
*args
)
3442 char domain
[8182], file
[PATH_MAX
];
3443 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3444 int s
= -1, i
, error
;
3446 size_t cert_buf_sz
, cert_count
;
3447 enum cert_trust rv
= CERT_UNTRUSTED
;
3449 gnutls_session_t gsession
;
3450 gnutls_x509_crt_t
*certs
;
3451 gnutls_certificate_credentials_t xcred
;
3453 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3458 if ((uri
= get_uri(t
)) == NULL
)
3460 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3462 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3464 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3467 if (start_tls(t
, s
, &gsession
, &xcred
))
3469 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3471 /* verify certs in case cert file doesn't exist */
3472 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3474 show_oops(t
, "Invalid certificates");
3479 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3480 show_oops(t
, "Can't get connection certificates");
3484 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3485 if ((f
= fopen(file
, "r")) == NULL
) {
3491 for (i
= 0; i
< cert_count
; i
++) {
3492 cert_buf_sz
= sizeof cert_buf
;
3493 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3494 cert_buf
, &cert_buf_sz
)) {
3497 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3498 rv
= CERT_BAD
; /* critical */
3501 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3502 rv
= CERT_BAD
; /* critical */
3511 free_connection_certs(certs
, cert_count
);
3513 /* we close the socket first for speed */
3517 /* only complain if we didn't save it locally */
3518 if (error
&& rv
!= CERT_LOCAL
) {
3519 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3520 if (error
& GNUTLS_CERT_INVALID
)
3521 strlcat(serr
, "invalid, ", sizeof serr
);
3522 if (error
& GNUTLS_CERT_REVOKED
)
3523 strlcat(serr
, "revoked, ", sizeof serr
);
3524 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3525 strlcat(serr
, "signer not found, ", sizeof serr
);
3526 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3527 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3528 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3529 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3530 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3531 strlcat(serr
, "not activated, ", sizeof serr
);
3532 if (error
& GNUTLS_CERT_EXPIRED
)
3533 strlcat(serr
, "expired, ", sizeof serr
);
3534 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3535 if (serr
[i
] == ',') {
3542 stop_tls(gsession
, xcred
);
3548 cert_cmd(struct tab
*t
, struct karg
*args
)
3554 gnutls_session_t gsession
;
3555 gnutls_x509_crt_t
*certs
;
3556 gnutls_certificate_credentials_t xcred
;
3561 if (ssl_ca_file
== NULL
) {
3562 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3566 if ((uri
= get_uri(t
)) == NULL
) {
3567 show_oops(t
, "Invalid URI");
3571 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3572 show_oops(t
, "Invalid certificate URI: %s", uri
);
3577 if (start_tls(t
, s
, &gsession
, &xcred
))
3581 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3582 show_oops(t
, "get_connection_certs failed");
3586 if (args
->i
& XT_SHOW
)
3587 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3588 else if (args
->i
& XT_SAVE
)
3589 save_certs(t
, certs
, cert_count
, domain
);
3591 free_connection_certs(certs
, cert_count
);
3593 /* we close the socket first for speed */
3596 stop_tls(gsession
, xcred
);
3602 remove_cookie(int index
)
3608 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3610 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3612 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3616 print_cookie("remove cookie", c
);
3617 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3622 soup_cookies_free(cf
);
3628 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3633 body
= g_strdup("");
3636 if (args
->i
& XT_WL_PERSISTENT
) {
3638 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3640 RB_FOREACH(d
, domain_list
, wl
) {
3644 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3650 if (args
->i
& XT_WL_SESSION
) {
3652 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3654 RB_FOREACH(d
, domain_list
, wl
) {
3658 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3663 tmp
= get_html_page(title
, body
, "", 0);
3666 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3668 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3674 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3676 char file
[PATH_MAX
];
3678 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3686 if (t
== NULL
|| args
== NULL
)
3689 if (runtime_settings
[0] == '\0')
3692 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3693 if ((f
= fopen(file
, "r+")) == NULL
)
3697 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3698 if (uri
== NULL
|| dom
== NULL
||
3699 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3700 show_oops(t
, "Can't add domain to %s white list",
3701 js
? "JavaScript" : "cookie");
3705 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3708 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3711 if (!strcmp(line
, lt
))
3717 fprintf(f
, "%s\n", lt
);
3722 d
= wl_find(dom
, &js_wl
);
3724 settings_add("js_wl", dom
);
3725 d
= wl_find(dom
, &js_wl
);
3729 d
= wl_find(dom
, &c_wl
);
3731 settings_add("cookie_wl", dom
);
3732 d
= wl_find(dom
, &c_wl
);
3736 /* find and add to persistent jar */
3737 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3738 for (;cf
; cf
= cf
->next
) {
3740 if (!strcmp(dom
, ci
->domain
) ||
3741 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3742 c
= soup_cookie_copy(ci
);
3743 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3746 soup_cookies_free(cf
);
3764 js_show_wl(struct tab
*t
, struct karg
*args
)
3766 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3767 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3773 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3775 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3776 wl_show(t
, args
, "Cookie White List", &c_wl
);
3782 cookie_cmd(struct tab
*t
, struct karg
*args
)
3784 if (args
->i
& XT_SHOW
)
3785 wl_show(t
, args
, "Cookie White List", &c_wl
);
3786 else if (args
->i
& XT_WL_TOGGLE
) {
3787 args
->i
|= XT_WL_RELOAD
;
3788 toggle_cwl(t
, args
);
3789 } else if (args
->i
& XT_SAVE
) {
3790 args
->i
|= XT_WL_RELOAD
;
3791 wl_save(t
, args
, 0);
3792 } else if (args
->i
& XT_DELETE
)
3793 show_oops(t
, "'cookie delete' currently unimplemented");
3799 js_cmd(struct tab
*t
, struct karg
*args
)
3801 if (args
->i
& XT_SHOW
)
3802 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3803 else if (args
->i
& XT_SAVE
) {
3804 args
->i
|= XT_WL_RELOAD
;
3805 wl_save(t
, args
, 1);
3806 } else if (args
->i
& XT_WL_TOGGLE
) {
3807 args
->i
|= XT_WL_RELOAD
;
3809 } else if (args
->i
& XT_DELETE
)
3810 show_oops(t
, "'js delete' currently unimplemented");
3816 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3818 js_toggle_cb(t
->js_toggle
, t
);
3824 add_favorite(struct tab
*t
, struct karg
*args
)
3826 char file
[PATH_MAX
];
3829 size_t urilen
, linelen
;
3830 const gchar
*uri
, *title
;
3835 /* don't allow adding of xtp pages to favorites */
3836 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3837 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3841 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3842 if ((f
= fopen(file
, "r+")) == NULL
) {
3843 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3847 title
= get_title(t
, FALSE
);
3850 if (title
== NULL
|| uri
== NULL
) {
3851 show_oops(t
, "can't add page to favorites");
3855 urilen
= strlen(uri
);
3858 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3859 if (feof(f
) || ferror(f
))
3862 if (linelen
== urilen
&& !strcmp(line
, uri
))
3869 fprintf(f
, "\n%s\n%s", title
, uri
);
3875 update_favorite_tabs(NULL
);
3881 navaction(struct tab
*t
, struct karg
*args
)
3883 WebKitWebHistoryItem
*item
;
3885 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3886 t
->tab_id
, args
->i
);
3888 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3891 if (args
->i
== XT_NAV_BACK
)
3892 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3894 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3896 return (XT_CB_PASSTHROUGH
);
3897 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3899 return (XT_CB_PASSTHROUGH
);
3905 item
= webkit_web_back_forward_list_get_back_item(t
->bfl
);
3907 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3909 case XT_NAV_FORWARD
:
3911 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3913 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3916 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3918 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3921 return (XT_CB_PASSTHROUGH
);
3925 move(struct tab
*t
, struct karg
*args
)
3927 GtkAdjustment
*adjust
;
3928 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3934 case XT_MOVE_BOTTOM
:
3936 case XT_MOVE_PAGEDOWN
:
3937 case XT_MOVE_PAGEUP
:
3938 case XT_MOVE_HALFDOWN
:
3939 case XT_MOVE_HALFUP
:
3940 case XT_MOVE_PERCENT
:
3941 adjust
= t
->adjust_v
;
3944 adjust
= t
->adjust_h
;
3948 pos
= gtk_adjustment_get_value(adjust
);
3949 ps
= gtk_adjustment_get_page_size(adjust
);
3950 upper
= gtk_adjustment_get_upper(adjust
);
3951 lower
= gtk_adjustment_get_lower(adjust
);
3952 si
= gtk_adjustment_get_step_increment(adjust
);
3953 pi
= gtk_adjustment_get_page_increment(adjust
);
3956 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3957 "max %f si %f pi %f\n",
3958 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3959 pos
, ps
, upper
, lower
, max
, si
, pi
);
3965 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3970 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3972 case XT_MOVE_BOTTOM
:
3973 case XT_MOVE_FARRIGHT
:
3974 gtk_adjustment_set_value(adjust
, max
);
3977 case XT_MOVE_FARLEFT
:
3978 gtk_adjustment_set_value(adjust
, lower
);
3980 case XT_MOVE_PAGEDOWN
:
3982 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3984 case XT_MOVE_PAGEUP
:
3986 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3988 case XT_MOVE_HALFDOWN
:
3990 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3992 case XT_MOVE_HALFUP
:
3994 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3996 case XT_MOVE_PERCENT
:
3997 percent
= atoi(args
->s
) / 100.0;
3998 pos
= max
* percent
;
3999 if (pos
< 0.0 || pos
> max
)
4001 gtk_adjustment_set_value(adjust
, pos
);
4004 return (XT_CB_PASSTHROUGH
);
4007 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4009 return (XT_CB_HANDLED
);
4013 url_set_visibility(void)
4017 TAILQ_FOREACH(t
, &tabs
, entry
)
4018 if (show_url
== 0) {
4019 gtk_widget_hide(t
->toolbar
);
4022 gtk_widget_show(t
->toolbar
);
4026 notebook_tab_set_visibility(void)
4028 if (show_tabs
== 0) {
4029 gtk_widget_hide(tab_bar
);
4030 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4032 if (tab_style
== XT_TABS_NORMAL
) {
4033 gtk_widget_hide(tab_bar
);
4034 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4035 } else if (tab_style
== XT_TABS_COMPACT
) {
4036 gtk_widget_show(tab_bar
);
4037 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4043 statusbar_set_visibility(void)
4047 TAILQ_FOREACH(t
, &tabs
, entry
)
4048 if (show_statusbar
== 0) {
4049 gtk_widget_hide(t
->statusbar_box
);
4052 gtk_widget_show(t
->statusbar_box
);
4056 url_set(struct tab
*t
, int enable_url_entry
)
4061 show_url
= enable_url_entry
;
4063 if (enable_url_entry
) {
4064 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4065 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4066 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4068 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4069 GTK_ENTRY_ICON_PRIMARY
);
4071 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4072 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4073 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4074 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4080 fullscreen(struct tab
*t
, struct karg
*args
)
4082 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4085 return (XT_CB_PASSTHROUGH
);
4087 if (show_url
== 0) {
4095 url_set_visibility();
4096 notebook_tab_set_visibility();
4098 return (XT_CB_HANDLED
);
4102 statustoggle(struct tab
*t
, struct karg
*args
)
4104 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4106 if (show_statusbar
== 1) {
4108 statusbar_set_visibility();
4109 } else if (show_statusbar
== 0) {
4111 statusbar_set_visibility();
4113 return (XT_CB_HANDLED
);
4117 urlaction(struct tab
*t
, struct karg
*args
)
4119 int rv
= XT_CB_HANDLED
;
4121 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4124 return (XT_CB_PASSTHROUGH
);
4128 if (show_url
== 0) {
4130 url_set_visibility();
4134 if (show_url
== 1) {
4136 url_set_visibility();
4144 tabaction(struct tab
*t
, struct karg
*args
)
4146 int rv
= XT_CB_HANDLED
;
4147 char *url
= args
->s
;
4151 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4154 return (XT_CB_PASSTHROUGH
);
4158 if (strlen(url
) > 0)
4159 create_new_tab(url
, NULL
, 1, args
->precount
);
4161 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4164 if (args
->precount
< 0)
4167 TAILQ_FOREACH(tt
, &tabs
, entry
)
4168 if (tt
->tab_id
== args
->precount
- 1) {
4173 case XT_TAB_DELQUIT
:
4174 if (gtk_notebook_get_n_pages(notebook
) > 1)
4180 if (strlen(url
) > 0)
4183 rv
= XT_CB_PASSTHROUGH
;
4189 if (show_tabs
== 0) {
4191 notebook_tab_set_visibility();
4195 if (show_tabs
== 1) {
4197 notebook_tab_set_visibility();
4200 case XT_TAB_NEXTSTYLE
:
4201 if (tab_style
== XT_TABS_NORMAL
) {
4202 tab_style
= XT_TABS_COMPACT
;
4203 recolor_compact_tabs();
4206 tab_style
= XT_TABS_NORMAL
;
4207 notebook_tab_set_visibility();
4209 case XT_TAB_UNDO_CLOSE
:
4210 if (undo_count
== 0) {
4211 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4216 u
= TAILQ_FIRST(&undos
);
4217 create_new_tab(u
->uri
, u
, 1, -1);
4219 TAILQ_REMOVE(&undos
, u
, entry
);
4221 /* u->history is freed in create_new_tab() */
4226 rv
= XT_CB_PASSTHROUGH
;
4240 resizetab(struct tab
*t
, struct karg
*args
)
4242 if (t
== NULL
|| args
== NULL
) {
4243 show_oops(NULL
, "resizetab invalid parameters");
4244 return (XT_CB_PASSTHROUGH
);
4247 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4248 t
->tab_id
, args
->i
);
4250 setzoom_webkit(t
, args
->i
);
4252 return (XT_CB_HANDLED
);
4256 movetab(struct tab
*t
, struct karg
*args
)
4260 if (t
== NULL
|| args
== NULL
) {
4261 show_oops(NULL
, "movetab invalid parameters");
4262 return (XT_CB_PASSTHROUGH
);
4265 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4266 t
->tab_id
, args
->i
);
4268 if (args
->i
>= XT_TAB_INVALID
)
4269 return (XT_CB_PASSTHROUGH
);
4271 if (TAILQ_EMPTY(&tabs
))
4272 return (XT_CB_PASSTHROUGH
);
4274 n
= gtk_notebook_get_n_pages(notebook
);
4275 dest
= gtk_notebook_get_current_page(notebook
);
4279 if (args
->precount
< 0)
4280 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4282 dest
= args
->precount
- 1;
4286 if (args
->precount
< 0)
4289 dest
-= args
->precount
% n
;
4302 return (XT_CB_PASSTHROUGH
);
4305 if (dest
< 0 || dest
>= n
)
4306 return (XT_CB_PASSTHROUGH
);
4307 if (t
->tab_id
== dest
) {
4308 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4309 return (XT_CB_HANDLED
);
4312 set_current_tab(dest
);
4314 return (XT_CB_HANDLED
);
4320 command(struct tab
*t
, struct karg
*args
)
4322 char *s
= NULL
, *ss
= NULL
;
4326 if (t
== NULL
|| args
== NULL
) {
4327 show_oops(NULL
, "command invalid parameters");
4328 return (XT_CB_PASSTHROUGH
);
4339 if (cmd_prefix
== 0)
4342 ss
= g_strdup_printf(":%d", cmd_prefix
);
4353 case XT_CMD_OPEN_CURRENT
:
4356 case XT_CMD_TABNEW_CURRENT
:
4357 if (!s
) /* FALL THROUGH? */
4359 if ((uri
= get_uri(t
)) != NULL
) {
4360 ss
= g_strdup_printf("%s%s", s
, uri
);
4365 show_oops(t
, "command: invalid opcode %d", args
->i
);
4366 return (XT_CB_PASSTHROUGH
);
4369 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4371 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4372 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4373 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4375 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4376 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4381 return (XT_CB_HANDLED
);
4385 * Return a new string with a download row (in html)
4386 * appended. Old string is freed.
4389 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4392 WebKitDownloadStatus stat
;
4393 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4395 char cur_sz
[FMT_SCALED_STRSIZE
];
4396 char tot_sz
[FMT_SCALED_STRSIZE
];
4399 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4401 /* All actions wil take this form:
4402 * xxxt://class/seskey
4404 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4405 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4407 stat
= webkit_download_get_status(dl
->download
);
4410 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4411 status_html
= g_strdup_printf("Finished");
4412 cmd_html
= g_strdup_printf(
4413 "<a href='%s%d/%d'>Remove</a>",
4414 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4416 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4417 /* gather size info */
4418 progress
= 100 * webkit_download_get_progress(dl
->download
);
4421 webkit_download_get_current_size(dl
->download
), cur_sz
);
4423 webkit_download_get_total_size(dl
->download
), tot_sz
);
4425 status_html
= g_strdup_printf(
4426 "<div style='width: 100%%' align='center'>"
4427 "<div class='progress-outer'>"
4428 "<div class='progress-inner' style='width: %.2f%%'>"
4429 "</div></div></div>"
4430 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4431 progress
, cur_sz
, tot_sz
, progress
);
4433 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4434 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4438 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4439 status_html
= g_strdup_printf("Cancelled");
4440 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4441 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4443 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4444 status_html
= g_strdup_printf("Error!");
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_CREATED
:
4449 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4450 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4451 status_html
= g_strdup_printf("Starting");
4454 show_oops(t
, "%s: unknown download status", __func__
);
4457 new_html
= g_strdup_printf(
4458 "%s\n<tr><td>%s</td><td>%s</td>"
4459 "<td style='text-align:center'>%s</td></tr>\n",
4460 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4461 status_html
, cmd_html
);
4465 g_free(status_html
);
4476 * update all download tabs apart from one. Pass NULL if
4477 * you want to update all.
4480 update_download_tabs(struct tab
*apart_from
)
4483 if (!updating_dl_tabs
) {
4484 updating_dl_tabs
= 1; /* stop infinite recursion */
4485 TAILQ_FOREACH(t
, &tabs
, entry
)
4486 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4487 && (t
!= apart_from
))
4488 xtp_page_dl(t
, NULL
);
4489 updating_dl_tabs
= 0;
4494 * update all cookie tabs apart from one. Pass NULL if
4495 * you want to update all.
4498 update_cookie_tabs(struct tab
*apart_from
)
4501 if (!updating_cl_tabs
) {
4502 updating_cl_tabs
= 1; /* stop infinite recursion */
4503 TAILQ_FOREACH(t
, &tabs
, entry
)
4504 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4505 && (t
!= apart_from
))
4506 xtp_page_cl(t
, NULL
);
4507 updating_cl_tabs
= 0;
4512 * update all history tabs apart from one. Pass NULL if
4513 * you want to update all.
4516 update_history_tabs(struct tab
*apart_from
)
4520 if (!updating_hl_tabs
) {
4521 updating_hl_tabs
= 1; /* stop infinite recursion */
4522 TAILQ_FOREACH(t
, &tabs
, entry
)
4523 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4524 && (t
!= apart_from
))
4525 xtp_page_hl(t
, NULL
);
4526 updating_hl_tabs
= 0;
4530 /* cookie management XTP page */
4532 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4534 char *body
, *page
, *tmp
;
4535 int i
= 1; /* all ids start 1 */
4536 GSList
*sc
, *pc
, *pc_start
;
4538 char *type
, *table_headers
, *last_domain
;
4540 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4543 show_oops(NULL
, "%s invalid parameters", __func__
);
4547 /* Generate a new session key */
4548 if (!updating_cl_tabs
)
4549 generate_xtp_session_key(&cl_session_key
);
4552 table_headers
= g_strdup_printf("<table><tr>"
4555 "<th style='width:200px'>Value</th>"
4559 "<th>HTTP<br />only</th>"
4560 "<th style='width:40px'>Rm</th></tr>\n");
4562 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4563 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4567 last_domain
= strdup("");
4568 for (; sc
; sc
= sc
->next
) {
4571 if (strcmp(last_domain
, c
->domain
) != 0) {
4574 last_domain
= strdup(c
->domain
);
4578 body
= g_strdup_printf("%s</table>"
4580 body
, c
->domain
, table_headers
);
4584 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4585 c
->domain
, table_headers
);
4590 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4591 if (soup_cookie_equal(pc
->data
, c
)) {
4592 type
= "Session + Persistent";
4597 body
= g_strdup_printf(
4600 "<td style='word-wrap:normal'>%s</td>"
4602 " <textarea rows='4'>%s</textarea>"
4608 "<td style='text-align:center'>"
4609 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4616 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4631 soup_cookies_free(sc
);
4632 soup_cookies_free(pc
);
4634 /* small message if there are none */
4636 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4637 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4640 body
= g_strdup_printf("%s</table>", body
);
4643 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4645 g_free(table_headers
);
4646 g_free(last_domain
);
4648 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4649 update_cookie_tabs(t
);
4657 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4659 char *body
, *page
, *tmp
;
4661 int i
= 1; /* all ids start 1 */
4663 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4666 show_oops(NULL
, "%s invalid parameters", __func__
);
4670 /* Generate a new session key */
4671 if (!updating_hl_tabs
)
4672 generate_xtp_session_key(&hl_session_key
);
4675 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4676 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4678 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4680 body
= g_strdup_printf(
4682 "<td><a href='%s'>%s</a></td>"
4684 "<td style='text-align: center'>"
4685 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4686 body
, h
->uri
, h
->uri
, h
->title
,
4687 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4688 XT_XTP_HL_REMOVE
, i
);
4694 /* small message if there are none */
4697 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4698 "colspan='3'>No History</td></tr>\n", body
);
4703 body
= g_strdup_printf("%s</table>", body
);
4706 page
= get_html_page("History", body
, "", TRUE
);
4710 * update all history manager tabs as the xtp session
4711 * key has now changed. No need to update the current tab.
4712 * Already did that above.
4714 update_history_tabs(t
);
4716 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4723 * Generate a web page detailing the status of any downloads
4726 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4728 struct download
*dl
;
4729 char *body
, *page
, *tmp
;
4733 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4736 show_oops(NULL
, "%s invalid parameters", __func__
);
4741 * Generate a new session key for next page instance.
4742 * This only happens for the top level call to xtp_page_dl()
4743 * in which case updating_dl_tabs is 0.
4745 if (!updating_dl_tabs
)
4746 generate_xtp_session_key(&dl_session_key
);
4748 /* header - with refresh so as to update */
4749 if (refresh_interval
>= 1)
4750 ref
= g_strdup_printf(
4751 "<meta http-equiv='refresh' content='%u"
4752 ";url=%s%d/%s/%d' />\n",
4761 body
= g_strdup_printf("<div align='center'>"
4762 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4763 "</p><table><tr><th style='width: 60%%'>"
4764 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4765 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4767 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4768 body
= xtp_page_dl_row(t
, body
, dl
);
4772 /* message if no downloads in list */
4775 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4776 " style='text-align: center'>"
4777 "No downloads</td></tr>\n", body
);
4782 body
= g_strdup_printf("%s</table></div>", body
);
4785 page
= get_html_page("Downloads", body
, ref
, 1);
4790 * update all download manager tabs as the xtp session
4791 * key has now changed. No need to update the current tab.
4792 * Already did that above.
4794 update_download_tabs(t
);
4796 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4803 search(struct tab
*t
, struct karg
*args
)
4807 if (t
== NULL
|| args
== NULL
) {
4808 show_oops(NULL
, "search invalid parameters");
4811 if (t
->search_text
== NULL
) {
4812 if (global_search
== NULL
)
4813 return (XT_CB_PASSTHROUGH
);
4815 t
->search_text
= g_strdup(global_search
);
4816 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4817 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4821 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4822 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4825 case XT_SEARCH_NEXT
:
4826 d
= t
->search_forward
;
4828 case XT_SEARCH_PREV
:
4829 d
= !t
->search_forward
;
4832 return (XT_CB_PASSTHROUGH
);
4835 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4837 return (XT_CB_HANDLED
);
4840 struct settings_args
{
4846 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4849 struct settings_args
*sa
= cb_args
;
4854 if (s
->flags
& XT_SF_RUNTIME
)
4860 *sa
->body
= g_strdup_printf(
4862 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4863 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4875 set_show(struct tab
*t
, struct karg
*args
)
4877 char *body
, *page
, *tmp
;
4879 struct settings_args sa
;
4881 bzero(&sa
, sizeof sa
);
4885 body
= g_strdup_printf("<div align='center'><table><tr>"
4886 "<th align='left'>Setting</th>"
4887 "<th align='left'>Value</th></tr>\n");
4889 settings_walk(print_setting
, &sa
);
4892 /* small message if there are none */
4895 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4896 "colspan='2'>No settings</td></tr>\n", body
);
4901 body
= g_strdup_printf("%s</table></div>", body
);
4904 page
= get_html_page("Settings", body
, "", 0);
4908 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4912 return (XT_CB_PASSTHROUGH
);
4916 set(struct tab
*t
, struct karg
*args
)
4921 if (args
== NULL
|| args
->s
== NULL
)
4922 return (set_show(t
, args
));
4925 p
= g_strstrip(args
->s
);
4928 return (set_show(t
, args
));
4930 /* we got some sort of string */
4931 val
= g_strrstr(p
, "=");
4934 val
= g_strchomp(val
);
4937 for (i
= 0; i
< LENGTH(rs
); i
++) {
4938 if (strcmp(rs
[i
].name
, p
))
4941 if (rs
[i
].activate
) {
4942 if (rs
[i
].activate(val
))
4943 show_oops(t
, "%s invalid value %s",
4946 show_oops(t
, ":set %s = %s", p
, val
);
4949 show_oops(t
, "not a runtime option: %s", p
);
4953 show_oops(t
, "unknown option: %s", p
);
4957 for (i
= 0; i
< LENGTH(rs
); i
++) {
4958 if (strcmp(rs
[i
].name
, p
))
4961 /* XXX this could use some cleanup */
4962 switch (rs
[i
].type
) {
4965 show_oops(t
, "%s = %d",
4966 rs
[i
].name
, *rs
[i
].ival
);
4967 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4968 show_oops(t
, "%s = %s",
4970 rs
[i
].s
->get(&rs
[i
]));
4971 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4972 show_oops(t
, "%s = ...", rs
[i
].name
);
4974 show_oops(t
, "%s = ", rs
[i
].name
);
4978 show_oops(t
, "%s = %f",
4979 rs
[i
].name
, *rs
[i
].fval
);
4980 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4981 show_oops(t
, "%s = %s",
4983 rs
[i
].s
->get(&rs
[i
]));
4984 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4985 show_oops(t
, "%s = ...", rs
[i
].name
);
4987 show_oops(t
, "%s = ", rs
[i
].name
);
4990 if (rs
[i
].sval
&& *rs
[i
].sval
)
4991 show_oops(t
, "%s = %s",
4992 rs
[i
].name
, *rs
[i
].sval
);
4993 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4994 show_oops(t
, "%s = %s",
4996 rs
[i
].s
->get(&rs
[i
]));
4997 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4998 show_oops(t
, "%s = ...", rs
[i
].name
);
5000 show_oops(t
, "%s = ", rs
[i
].name
);
5003 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5009 show_oops(t
, "unknown option: %s", p
);
5012 return (XT_CB_PASSTHROUGH
);
5016 session_save(struct tab
*t
, char *filename
)
5021 if (strlen(filename
) == 0)
5024 if (filename
[0] == '.' || filename
[0] == '/')
5028 if (save_tabs(t
, &a
))
5030 strlcpy(named_session
, filename
, sizeof named_session
);
5038 session_open(struct tab
*t
, char *filename
)
5043 if (strlen(filename
) == 0)
5046 if (filename
[0] == '.' || filename
[0] == '/')
5050 a
.i
= XT_SES_CLOSETABS
;
5051 if (open_tabs(t
, &a
))
5054 strlcpy(named_session
, filename
, sizeof named_session
);
5062 session_delete(struct tab
*t
, char *filename
)
5064 char file
[PATH_MAX
];
5067 if (strlen(filename
) == 0)
5070 if (filename
[0] == '.' || filename
[0] == '/')
5073 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5077 if (!strcmp(filename
, named_session
))
5078 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5079 sizeof named_session
);
5087 session_cmd(struct tab
*t
, struct karg
*args
)
5089 char *filename
= args
->s
;
5094 if (args
->i
& XT_SHOW
)
5095 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5096 XT_SAVED_TABS_FILE
: named_session
);
5097 else if (args
->i
& XT_SAVE
) {
5098 if (session_save(t
, filename
)) {
5099 show_oops(t
, "Can't save session: %s",
5100 filename
? filename
: "INVALID");
5103 } else if (args
->i
& XT_OPEN
) {
5104 if (session_open(t
, filename
)) {
5105 show_oops(t
, "Can't open session: %s",
5106 filename
? filename
: "INVALID");
5109 } else if (args
->i
& XT_DELETE
) {
5110 if (session_delete(t
, filename
)) {
5111 show_oops(t
, "Can't delete session: %s",
5112 filename
? filename
: "INVALID");
5117 return (XT_CB_PASSTHROUGH
);
5121 * Make a hardcopy of the page
5124 print_page(struct tab
*t
, struct karg
*args
)
5126 WebKitWebFrame
*frame
;
5128 GtkPrintOperation
*op
;
5129 GtkPrintOperationAction action
;
5130 GtkPrintOperationResult print_res
;
5131 GError
*g_err
= NULL
;
5132 int marg_l
, marg_r
, marg_t
, marg_b
;
5134 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5136 ps
= gtk_page_setup_new();
5137 op
= gtk_print_operation_new();
5138 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5139 frame
= webkit_web_view_get_main_frame(t
->wv
);
5141 /* the default margins are too small, so we will bump them */
5142 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5143 XT_PRINT_EXTRA_MARGIN
;
5144 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5145 XT_PRINT_EXTRA_MARGIN
;
5146 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5147 XT_PRINT_EXTRA_MARGIN
;
5148 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5149 XT_PRINT_EXTRA_MARGIN
;
5152 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5153 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5154 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5155 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5157 gtk_print_operation_set_default_page_setup(op
, ps
);
5159 /* this appears to free 'op' and 'ps' */
5160 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5162 /* check it worked */
5163 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5164 show_oops(NULL
, "can't print: %s", g_err
->message
);
5165 g_error_free (g_err
);
5173 go_home(struct tab
*t
, struct karg
*args
)
5180 restart(struct tab
*t
, struct karg
*args
)
5184 a
.s
= XT_RESTART_TABS_FILE
;
5186 execvp(start_argv
[0], start_argv
);
5192 #define CTRL GDK_CONTROL_MASK
5193 #define MOD1 GDK_MOD1_MASK
5194 #define SHFT GDK_SHIFT_MASK
5196 /* inherent to GTK not all keys will be caught at all times */
5197 /* XXX sort key bindings */
5198 struct key_binding
{
5203 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5205 { "cookiejar", MOD1
, 0, GDK_j
},
5206 { "downloadmgr", MOD1
, 0, GDK_d
},
5207 { "history", MOD1
, 0, GDK_h
},
5208 { "print", CTRL
, 0, GDK_p
},
5209 { "search", 0, 0, GDK_slash
},
5210 { "searchb", 0, 0, GDK_question
},
5211 { "statustoggle", CTRL
, 0, GDK_n
},
5212 { "command", 0, 0, GDK_colon
},
5213 { "qa", CTRL
, 0, GDK_q
},
5214 { "restart", MOD1
, 0, GDK_q
},
5215 { "js toggle", CTRL
, 0, GDK_j
},
5216 { "cookie toggle", MOD1
, 0, GDK_c
},
5217 { "togglesrc", CTRL
, 0, GDK_s
},
5218 { "yankuri", 0, 0, GDK_y
},
5219 { "pasteuricur", 0, 0, GDK_p
},
5220 { "pasteurinew", 0, 0, GDK_P
},
5221 { "toplevel toggle", 0, 0, GDK_F4
},
5222 { "help", 0, 0, GDK_F1
},
5223 { "run_script", MOD1
, 0, GDK_r
},
5226 { "searchnext", 0, 0, GDK_n
},
5227 { "searchprevious", 0, 0, GDK_N
},
5230 { "focusaddress", 0, 0, GDK_F6
},
5231 { "focussearch", 0, 0, GDK_F7
},
5234 { "hinting", 0, 0, GDK_f
},
5236 /* custom stylesheet */
5237 { "userstyle", 0, 0, GDK_i
},
5240 { "goback", 0, 0, GDK_BackSpace
},
5241 { "goback", MOD1
, 0, GDK_Left
},
5242 { "goforward", SHFT
, 0, GDK_BackSpace
},
5243 { "goforward", MOD1
, 0, GDK_Right
},
5244 { "reload", 0, 0, GDK_F5
},
5245 { "reload", CTRL
, 0, GDK_r
},
5246 { "reload", CTRL
, 0, GDK_l
},
5247 { "favorites", MOD1
, 1, GDK_f
},
5249 /* vertical movement */
5250 { "scrolldown", 0, 0, GDK_j
},
5251 { "scrolldown", 0, 0, GDK_Down
},
5252 { "scrollup", 0, 0, GDK_Up
},
5253 { "scrollup", 0, 0, GDK_k
},
5254 { "scrollbottom", 0, 0, GDK_G
},
5255 { "scrollbottom", 0, 0, GDK_End
},
5256 { "scrolltop", 0, 0, GDK_Home
},
5257 { "scrollpagedown", 0, 0, GDK_space
},
5258 { "scrollpagedown", CTRL
, 0, GDK_f
},
5259 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5260 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5261 { "scrollpageup", 0, 0, GDK_Page_Up
},
5262 { "scrollpageup", CTRL
, 0, GDK_b
},
5263 { "scrollhalfup", CTRL
, 0, GDK_u
},
5264 /* horizontal movement */
5265 { "scrollright", 0, 0, GDK_l
},
5266 { "scrollright", 0, 0, GDK_Right
},
5267 { "scrollleft", 0, 0, GDK_Left
},
5268 { "scrollleft", 0, 0, GDK_h
},
5269 { "scrollfarright", 0, 0, GDK_dollar
},
5270 { "scrollfarleft", 0, 0, GDK_0
},
5273 { "tabnew", CTRL
, 0, GDK_t
},
5274 { "999tabnew", CTRL
, 0, GDK_T
},
5275 { "tabclose", CTRL
, 1, GDK_w
},
5276 { "tabundoclose", 0, 0, GDK_U
},
5277 { "tabnext 1", CTRL
, 0, GDK_1
},
5278 { "tabnext 2", CTRL
, 0, GDK_2
},
5279 { "tabnext 3", CTRL
, 0, GDK_3
},
5280 { "tabnext 4", CTRL
, 0, GDK_4
},
5281 { "tabnext 5", CTRL
, 0, GDK_5
},
5282 { "tabnext 6", CTRL
, 0, GDK_6
},
5283 { "tabnext 7", CTRL
, 0, GDK_7
},
5284 { "tabnext 8", CTRL
, 0, GDK_8
},
5285 { "tabnext 9", CTRL
, 0, GDK_9
},
5286 { "tabfirst", CTRL
, 0, GDK_less
},
5287 { "tablast", CTRL
, 0, GDK_greater
},
5288 { "tabprevious", CTRL
, 0, GDK_Left
},
5289 { "tabnext", CTRL
, 0, GDK_Right
},
5290 { "focusout", CTRL
, 0, GDK_minus
},
5291 { "focusin", CTRL
, 0, GDK_plus
},
5292 { "focusin", CTRL
, 0, GDK_equal
},
5293 { "focusreset", CTRL
, 0, GDK_0
},
5295 /* command aliases (handy when -S flag is used) */
5296 { "promptopen", 0, 0, GDK_F9
},
5297 { "promptopencurrent", 0, 0, GDK_F10
},
5298 { "prompttabnew", 0, 0, GDK_F11
},
5299 { "prompttabnewcurrent",0, 0, GDK_F12
},
5301 TAILQ_HEAD(keybinding_list
, key_binding
);
5304 walk_kb(struct settings
*s
,
5305 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5307 struct key_binding
*k
;
5310 if (s
== NULL
|| cb
== NULL
) {
5311 show_oops(NULL
, "walk_kb invalid parameters");
5315 TAILQ_FOREACH(k
, &kbl
, entry
) {
5321 if (gdk_keyval_name(k
->key
) == NULL
)
5324 strlcat(str
, k
->cmd
, sizeof str
);
5325 strlcat(str
, ",", sizeof str
);
5327 if (k
->mask
& GDK_SHIFT_MASK
)
5328 strlcat(str
, "S-", sizeof str
);
5329 if (k
->mask
& GDK_CONTROL_MASK
)
5330 strlcat(str
, "C-", sizeof str
);
5331 if (k
->mask
& GDK_MOD1_MASK
)
5332 strlcat(str
, "M1-", sizeof str
);
5333 if (k
->mask
& GDK_MOD2_MASK
)
5334 strlcat(str
, "M2-", sizeof str
);
5335 if (k
->mask
& GDK_MOD3_MASK
)
5336 strlcat(str
, "M3-", sizeof str
);
5337 if (k
->mask
& GDK_MOD4_MASK
)
5338 strlcat(str
, "M4-", sizeof str
);
5339 if (k
->mask
& GDK_MOD5_MASK
)
5340 strlcat(str
, "M5-", sizeof str
);
5342 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5343 cb(s
, str
, cb_args
);
5348 init_keybindings(void)
5351 struct key_binding
*k
;
5353 for (i
= 0; i
< LENGTH(keys
); i
++) {
5354 k
= g_malloc0(sizeof *k
);
5355 k
->cmd
= keys
[i
].cmd
;
5356 k
->mask
= keys
[i
].mask
;
5357 k
->use_in_entry
= keys
[i
].use_in_entry
;
5358 k
->key
= keys
[i
].key
;
5359 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5361 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5362 k
->cmd
? k
->cmd
: "unnamed key");
5367 keybinding_clearall(void)
5369 struct key_binding
*k
, *next
;
5371 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5372 next
= TAILQ_NEXT(k
, entry
);
5376 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5377 k
->cmd
? k
->cmd
: "unnamed key");
5378 TAILQ_REMOVE(&kbl
, k
, entry
);
5384 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5386 struct key_binding
*k
;
5387 guint keyval
, mask
= 0;
5390 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5392 /* Keys which are to be used in entry have been prefixed with an
5393 * exclamation mark. */
5397 /* find modifier keys */
5398 if (strstr(key
, "S-"))
5399 mask
|= GDK_SHIFT_MASK
;
5400 if (strstr(key
, "C-"))
5401 mask
|= GDK_CONTROL_MASK
;
5402 if (strstr(key
, "M1-"))
5403 mask
|= GDK_MOD1_MASK
;
5404 if (strstr(key
, "M2-"))
5405 mask
|= GDK_MOD2_MASK
;
5406 if (strstr(key
, "M3-"))
5407 mask
|= GDK_MOD3_MASK
;
5408 if (strstr(key
, "M4-"))
5409 mask
|= GDK_MOD4_MASK
;
5410 if (strstr(key
, "M5-"))
5411 mask
|= GDK_MOD5_MASK
;
5414 for (i
= strlen(key
) - 1; i
> 0; i
--)
5418 /* validate keyname */
5419 keyval
= gdk_keyval_from_name(key
);
5420 if (keyval
== GDK_VoidSymbol
) {
5421 warnx("invalid keybinding name %s", key
);
5424 /* must run this test too, gtk+ doesn't handle 10 for example */
5425 if (gdk_keyval_name(keyval
) == NULL
) {
5426 warnx("invalid keybinding name %s", key
);
5430 /* Remove eventual dupes. */
5431 TAILQ_FOREACH(k
, &kbl
, entry
)
5432 if (k
->key
== keyval
&& k
->mask
== mask
) {
5433 TAILQ_REMOVE(&kbl
, k
, entry
);
5439 k
= g_malloc0(sizeof *k
);
5440 k
->cmd
= g_strdup(cmd
);
5442 k
->use_in_entry
= use_in_entry
;
5445 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5450 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5451 k
->cmd
, gdk_keyval_name(keyval
));
5453 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5459 add_kb(struct settings
*s
, char *entry
)
5463 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5465 /* clearall is special */
5466 if (!strcmp(entry
, "clearall")) {
5467 keybinding_clearall();
5471 kb
= strstr(entry
, ",");
5477 return (keybinding_add(entry
, key
, key
[0] == '!'));
5483 int (*func
)(struct tab
*, struct karg
*);
5487 { "command", 0, command
, ':', 0 },
5488 { "search", 0, command
, '/', 0 },
5489 { "searchb", 0, command
, '?', 0 },
5490 { "togglesrc", 0, toggle_src
, 0, 0 },
5492 /* yanking and pasting */
5493 { "yankuri", 0, yank_uri
, 0, 0 },
5494 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5495 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5496 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5499 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5500 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5503 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5504 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5507 { "hinting", 0, hint
, 0, 0 },
5509 /* custom stylesheet */
5510 { "userstyle", 0, userstyle
, 0, 0 },
5513 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5514 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5515 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5517 /* vertical movement */
5518 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5519 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5520 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5521 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5522 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5523 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5524 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5525 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5526 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5527 /* horizontal movement */
5528 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5529 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5530 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5531 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5533 { "favorites", 0, xtp_page_fl
, 0, 0 },
5534 { "fav", 0, xtp_page_fl
, 0, 0 },
5535 { "favadd", 0, add_favorite
, 0, 0 },
5537 { "qall", 0, quit
, 0, 0 },
5538 { "quitall", 0, quit
, 0, 0 },
5539 { "w", 0, save_tabs
, 0, 0 },
5540 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5541 { "help", 0, help
, 0, 0 },
5542 { "about", 0, about
, 0, 0 },
5543 { "stats", 0, stats
, 0, 0 },
5544 { "version", 0, about
, 0, 0 },
5547 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5548 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5549 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5550 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5551 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5552 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5553 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5554 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5555 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5556 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5557 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5559 /* cookie command */
5560 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5561 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5562 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5563 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5564 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5565 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5566 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5567 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5568 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5569 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5570 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5572 /* toplevel (domain) command */
5573 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5574 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5577 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5580 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5581 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5582 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5584 { "ca", 0, ca_cmd
, 0, 0 },
5585 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5586 { "dl", 0, xtp_page_dl
, 0, 0 },
5587 { "h", 0, xtp_page_hl
, 0, 0 },
5588 { "history", 0, xtp_page_hl
, 0, 0 },
5589 { "home", 0, go_home
, 0, 0 },
5590 { "restart", 0, restart
, 0, 0 },
5591 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5592 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5593 { "statustoggle", 0, statustoggle
, 0, 0 },
5594 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5596 { "print", 0, print_page
, 0, 0 },
5599 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5600 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5601 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5602 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5603 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5604 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5605 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5606 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5607 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5608 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5609 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5610 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5611 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5612 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5613 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5614 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5615 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5616 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5617 { "buffers", 0, buffers
, 0, 0 },
5618 { "ls", 0, buffers
, 0, 0 },
5619 { "tabs", 0, buffers
, 0, 0 },
5621 /* command aliases (handy when -S flag is used) */
5622 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5623 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5624 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5625 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5628 { "set", 0, set
, 0, XT_USERARG
},
5630 { "fullscreen", 0, fullscreen
, 0, 0 },
5631 { "f", 0, fullscreen
, 0, 0 },
5634 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5635 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5636 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5637 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5638 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5645 } cmd_status
= {-1, 0};
5648 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5651 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5658 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5665 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5667 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5673 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5675 a
.i
= XT_NAV_FORWARD
;
5685 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5687 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5689 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5696 * cancel, remove, etc. downloads
5699 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5701 struct download find
, *d
= NULL
;
5703 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5705 /* some commands require a valid download id */
5706 if (cmd
!= XT_XTP_DL_LIST
) {
5707 /* lookup download in question */
5709 d
= RB_FIND(download_list
, &downloads
, &find
);
5712 show_oops(t
, "%s: no such download", __func__
);
5717 /* decide what to do */
5719 case XT_XTP_DL_CANCEL
:
5720 webkit_download_cancel(d
->download
);
5722 case XT_XTP_DL_REMOVE
:
5723 webkit_download_cancel(d
->download
); /* just incase */
5724 g_object_unref(d
->download
);
5725 RB_REMOVE(download_list
, &downloads
, d
);
5727 case XT_XTP_DL_LIST
:
5731 show_oops(t
, "%s: unknown command", __func__
);
5734 xtp_page_dl(t
, NULL
);
5738 * Actions on history, only does one thing for now, but
5739 * we provide the function for future actions
5742 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5744 struct history
*h
, *next
;
5748 case XT_XTP_HL_REMOVE
:
5749 /* walk backwards, as listed in reverse */
5750 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5751 next
= RB_PREV(history_list
, &hl
, h
);
5753 RB_REMOVE(history_list
, &hl
, h
);
5754 g_free((gpointer
) h
->title
);
5755 g_free((gpointer
) h
->uri
);
5762 case XT_XTP_HL_LIST
:
5763 /* Nothing - just xtp_page_hl() below */
5766 show_oops(t
, "%s: unknown command", __func__
);
5770 xtp_page_hl(t
, NULL
);
5773 /* remove a favorite */
5775 remove_favorite(struct tab
*t
, int index
)
5777 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5778 char *new_favs
, *tmp
;
5783 /* open favorites */
5784 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5786 if ((f
= fopen(file
, "r")) == NULL
) {
5787 show_oops(t
, "%s: can't open favorites: %s",
5788 __func__
, strerror(errno
));
5792 /* build a string which will become the new favroites file */
5793 new_favs
= g_strdup("");
5796 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5797 if (feof(f
) || ferror(f
))
5799 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5806 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5807 if (feof(f
) || ferror(f
)) {
5808 show_oops(t
, "%s: can't parse favorites %s",
5809 __func__
, strerror(errno
));
5814 /* as long as this isn't the one we are deleting add to file */
5817 new_favs
= g_strdup_printf("%s%s\n%s\n",
5818 new_favs
, title
, uri
);
5830 /* write back new favorites file */
5831 if ((f
= fopen(file
, "w")) == NULL
) {
5832 show_oops(t
, "%s: can't open favorites: %s",
5833 __func__
, strerror(errno
));
5837 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5850 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5853 case XT_XTP_FL_LIST
:
5854 /* nothing, just the below call to xtp_page_fl() */
5856 case XT_XTP_FL_REMOVE
:
5857 remove_favorite(t
, arg
);
5860 show_oops(t
, "%s: invalid favorites command", __func__
);
5864 xtp_page_fl(t
, NULL
);
5868 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5871 case XT_XTP_CL_LIST
:
5872 /* nothing, just xtp_page_cl() */
5874 case XT_XTP_CL_REMOVE
:
5878 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5882 xtp_page_cl(t
, NULL
);
5885 /* link an XTP class to it's session key and handler function */
5886 struct xtp_despatch
{
5889 void (*handle_func
)(struct tab
*, uint8_t, int);
5892 struct xtp_despatch xtp_despatches
[] = {
5893 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5894 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5895 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5896 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5897 { XT_XTP_INVALID
, NULL
, NULL
}
5901 * is the url xtp protocol? (xxxt://)
5902 * if so, parse and despatch correct bahvior
5905 parse_xtp_url(struct tab
*t
, const char *url
)
5907 char *dup
= NULL
, *p
, *last
;
5908 uint8_t n_tokens
= 0;
5909 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5910 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5915 * tokens array meaning:
5917 * tokens[1] = session key
5918 * tokens[2] = action
5919 * tokens[3] = optional argument
5922 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5924 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5927 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5929 /* split out the url */
5930 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5931 (p
= strtok_r(NULL
, "/", &last
))) {
5933 tokens
[n_tokens
++] = p
;
5936 /* should be atleast three fields 'class/seskey/command/arg' */
5940 dsp
= xtp_despatches
;
5941 req_class
= atoi(tokens
[0]);
5942 while (dsp
->xtp_class
) {
5943 if (dsp
->xtp_class
== req_class
) {
5950 /* did we find one atall? */
5951 if (dsp_match
== NULL
) {
5952 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5956 /* check session key and call despatch function */
5957 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5958 ret
= TRUE
; /* all is well, this was a valid xtp request */
5959 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5972 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5974 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5976 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5979 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5984 show_oops(t
, "activate_uri_entry_cb no uri");
5988 uri
+= strspn(uri
, "\t ");
5990 /* if xxxt:// treat specially */
5991 if (parse_xtp_url(t
, uri
))
5994 /* otherwise continue to load page normally */
5995 load_uri(t
, (gchar
*)uri
);
6000 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6002 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6003 char *newuri
= NULL
;
6006 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6009 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6013 if (search_string
== NULL
) {
6014 show_oops(t
, "no search_string");
6018 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6020 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6021 newuri
= g_strdup_printf(search_string
, enc_search
);
6025 webkit_web_view_load_uri(t
->wv
, newuri
);
6033 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6035 struct domain
*d
= NULL
;
6038 if (uri
== NULL
|| t
== NULL
)
6041 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6046 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6047 es
? "enable" : "disable", uri
);
6049 g_object_set(G_OBJECT(t
->settings
),
6050 "enable-html5-local-storage", es
, (char *)NULL
);
6051 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6055 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6057 struct domain
*d
= NULL
;
6060 if (uri
== NULL
|| t
== NULL
)
6063 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6068 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6069 es
? "enable" : "disable", uri
);
6071 g_object_set(G_OBJECT(t
->settings
),
6072 "enable-scripts", es
, (char *)NULL
);
6073 g_object_set(G_OBJECT(t
->settings
),
6074 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6075 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6077 button_set_stockid(t
->js_toggle
,
6078 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6082 color_address_bar(gpointer p
)
6085 struct tab
*tt
, *t
= p
;
6086 gchar
*col_str
= XT_COLOR_YELLOW
;
6088 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6090 /* make sure t still exists */
6093 TAILQ_FOREACH(tt
, &tabs
, entry
)
6099 switch (load_compare_cert(t
, NULL
)) {
6101 col_str
= XT_COLOR_BLUE
;
6104 col_str
= XT_COLOR_GREEN
;
6106 case CERT_UNTRUSTED
:
6107 col_str
= XT_COLOR_YELLOW
;
6110 col_str
= XT_COLOR_RED
;
6114 gdk_color_parse(col_str
, &color
);
6115 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6117 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6118 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6120 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6124 return (FALSE
/* kill thread */);
6128 show_ca_status(struct tab
*t
, const char *uri
)
6131 gchar
*col_str
= XT_COLOR_WHITE
;
6133 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6134 ssl_strict_certs
, ssl_ca_file
, uri
);
6141 if (ssl_ca_file
== NULL
) {
6142 if (g_str_has_prefix(uri
, "http://"))
6144 if (g_str_has_prefix(uri
, "https://")) {
6145 col_str
= XT_COLOR_RED
;
6150 if (g_str_has_prefix(uri
, "http://") ||
6151 !g_str_has_prefix(uri
, "https://"))
6154 /* thread the coloring of the address bar */
6155 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
,
6156 color_address_bar
, t
, NULL
);
6161 gdk_color_parse(col_str
, &color
);
6162 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6164 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6165 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6167 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6172 free_favicon(struct tab
*t
)
6174 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6175 __func__
, t
->icon_download
, t
->icon_request
);
6177 if (t
->icon_request
)
6178 g_object_unref(t
->icon_request
);
6179 if (t
->icon_dest_uri
)
6180 g_free(t
->icon_dest_uri
);
6182 t
->icon_request
= NULL
;
6183 t
->icon_dest_uri
= NULL
;
6187 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6189 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6190 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6192 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6193 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6195 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6196 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6200 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6202 GdkPixbuf
*pb_scaled
;
6204 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6205 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6206 GDK_INTERP_BILINEAR
);
6210 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6211 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6213 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6214 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6216 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6217 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6219 if (pb_scaled
!= pb
)
6220 g_object_unref(pb_scaled
);
6224 xt_icon_from_file(struct tab
*t
, char *file
)
6228 if (g_str_has_prefix(file
, "file://"))
6229 file
+= strlen("file://");
6231 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6233 xt_icon_from_pixbuf(t
, pb
);
6236 xt_icon_from_name(t
, "text-html");
6240 is_valid_icon(char *file
)
6243 const char *mime_type
;
6247 gf
= g_file_new_for_path(file
);
6248 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6250 mime_type
= g_file_info_get_content_type(fi
);
6251 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6252 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6253 g_strcmp0(mime_type
, "image/png") == 0 ||
6254 g_strcmp0(mime_type
, "image/gif") == 0 ||
6255 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6263 set_favicon_from_file(struct tab
*t
, char *file
)
6267 if (t
== NULL
|| file
== NULL
)
6270 if (g_str_has_prefix(file
, "file://"))
6271 file
+= strlen("file://");
6272 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6274 if (!stat(file
, &sb
)) {
6275 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6276 /* corrupt icon so trash it */
6277 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6280 /* no need to set icon to default here */
6284 xt_icon_from_file(t
, file
);
6288 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6291 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6292 struct tab
*tt
= NULL
, *t
= NULL
;
6295 * find the webview instead of passing in the tab as it could have been
6296 * deleted from underneath us.
6298 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6307 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6308 __func__
, t
->tab_id
, status
);
6311 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6313 t
->icon_download
= NULL
;
6316 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6319 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6322 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6324 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6325 __func__
, t
->tab_id
);
6326 t
->icon_download
= NULL
;
6329 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6332 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6333 __func__
, t
->icon_dest_uri
);
6334 set_favicon_from_file(t
, t
->icon_dest_uri
);
6335 /* these will be freed post callback */
6336 t
->icon_request
= NULL
;
6337 t
->icon_download
= NULL
;
6345 abort_favicon_download(struct tab
*t
)
6347 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6349 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6350 if (t
->icon_download
) {
6351 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6352 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6353 webkit_download_cancel(t
->icon_download
);
6354 t
->icon_download
= NULL
;
6359 xt_icon_from_name(t
, "text-html");
6363 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6365 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6367 if (uri
== NULL
|| t
== NULL
)
6370 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6371 /* take icon from WebKitIconDatabase */
6374 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6376 xt_icon_from_pixbuf(t
, pb
);
6379 xt_icon_from_name(t
, "text-html");
6380 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6381 /* download icon to cache dir */
6382 gchar
*name_hash
, file
[PATH_MAX
];
6385 if (t
->icon_request
) {
6386 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6390 /* check to see if we got the icon in cache */
6391 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6392 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6395 if (!stat(file
, &sb
)) {
6396 if (sb
.st_size
> 0) {
6397 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6399 set_favicon_from_file(t
, file
);
6403 /* corrupt icon so trash it */
6404 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6409 /* create download for icon */
6410 t
->icon_request
= webkit_network_request_new(uri
);
6411 if (t
->icon_request
== NULL
) {
6412 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6417 t
->icon_download
= webkit_download_new(t
->icon_request
);
6418 if (t
->icon_download
== NULL
)
6421 /* we have to free icon_dest_uri later */
6422 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6423 webkit_download_set_destination_uri(t
->icon_download
,
6426 if (webkit_download_get_status(t
->icon_download
) ==
6427 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6428 g_object_unref(t
->icon_request
);
6429 g_free(t
->icon_dest_uri
);
6430 t
->icon_request
= NULL
;
6431 t
->icon_dest_uri
= NULL
;
6435 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6436 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6438 webkit_download_start(t
->icon_download
);
6443 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6445 const gchar
*uri
= NULL
, *title
= NULL
;
6446 struct history
*h
, find
;
6450 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6451 webkit_web_view_get_load_status(wview
),
6452 get_uri(t
) ? get_uri(t
) : "NOTHING");
6455 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6459 switch (webkit_web_view_get_load_status(wview
)) {
6460 case WEBKIT_LOAD_PROVISIONAL
:
6462 abort_favicon_download(t
);
6463 #if GTK_CHECK_VERSION(2, 20, 0)
6464 gtk_widget_show(t
->spinner
);
6465 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6467 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6469 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6471 /* assume we are a new address */
6472 gdk_color_parse("white", &color
);
6473 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6474 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6476 /* take focus if we are visible */
6482 case WEBKIT_LOAD_COMMITTED
:
6487 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6493 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6495 /* check if js white listing is enabled */
6496 if (enable_cookie_whitelist
)
6497 check_and_set_cookie(uri
, t
);
6498 if (enable_js_whitelist
)
6499 check_and_set_js(uri
, t
);
6505 /* we know enough to autosave the session */
6506 if (session_autosave
) {
6511 show_ca_status(t
, uri
);
6514 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6518 case WEBKIT_LOAD_FINISHED
:
6524 if (!strncmp(uri
, "http://", strlen("http://")) ||
6525 !strncmp(uri
, "https://", strlen("https://")) ||
6526 !strncmp(uri
, "file://", strlen("file://"))) {
6528 h
= RB_FIND(history_list
, &hl
, &find
);
6530 title
= get_title(t
, FALSE
);
6531 h
= g_malloc(sizeof *h
);
6532 h
->uri
= g_strdup(uri
);
6533 h
->title
= g_strdup(title
);
6534 RB_INSERT(history_list
, &hl
, h
);
6535 completion_add_uri(h
->uri
);
6536 update_history_tabs(NULL
);
6540 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6541 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6542 case WEBKIT_LOAD_FAILED
:
6545 #if GTK_CHECK_VERSION(2, 20, 0)
6546 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6547 gtk_widget_hide(t
->spinner
);
6550 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6555 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6557 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6558 webkit_web_view_can_go_back(wview
));
6560 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6561 webkit_web_view_can_go_forward(wview
));
6566 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6567 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6570 * XXX this function is wrong
6571 * it overwrites perfectly good urls with garbage on load errors
6572 * those happen often when popups fail to resolve dns
6576 t
->tmp_uri
= g_strdup(uri
);
6577 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6578 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6579 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6580 set_status(t
, uri
, XT_STATUS_NOTHING
);
6587 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6589 const gchar
*title
= NULL
, *win_title
= NULL
;
6591 title
= get_title(t
, FALSE
);
6592 win_title
= get_title(t
, TRUE
);
6593 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6594 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6595 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6596 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6600 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6602 run_script(t
, JS_HINTING
);
6606 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6608 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6609 progress
== 100 ? 0 : (double)progress
/ 100);
6610 if (show_url
== 0) {
6611 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6612 progress
== 100 ? 0 : (double)progress
/ 100);
6615 update_statusbar_position(NULL
, NULL
);
6619 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6620 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6621 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6624 WebKitWebNavigationReason reason
;
6625 struct domain
*d
= NULL
;
6628 show_oops(NULL
, "webview_npd_cb invalid parameters");
6632 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6634 webkit_network_request_get_uri(request
));
6636 uri
= (char *)webkit_network_request_get_uri(request
);
6638 /* if this is an xtp url, we don't load anything else */
6639 if (parse_xtp_url(t
, uri
))
6642 if (t
->ctrl_click
) {
6644 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6645 webkit_web_policy_decision_ignore(pd
);
6646 return (TRUE
); /* we made the decission */
6650 * This is a little hairy but it comes down to this:
6651 * when we run in whitelist mode we have to assist the browser in
6652 * opening the URL that it would have opened in a new tab.
6654 reason
= webkit_web_navigation_action_get_reason(na
);
6655 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6656 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6657 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6658 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6660 webkit_web_policy_decision_use(pd
);
6661 return (TRUE
); /* we made the decision */
6668 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6671 struct domain
*d
= NULL
;
6673 WebKitWebView
*webview
= NULL
;
6675 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6676 webkit_web_view_get_uri(wv
));
6679 /* open in current tab */
6681 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6682 uri
= webkit_web_view_get_uri(wv
);
6683 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6686 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6688 } else if (enable_scripts
== 1) {
6689 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6697 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6700 struct domain
*d
= NULL
;
6702 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6704 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6705 uri
= webkit_web_view_get_uri(wv
);
6706 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6710 } else if (enable_scripts
== 1)
6717 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6719 /* we can not eat the event without throwing gtk off so defer it */
6721 /* catch middle click */
6722 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6727 /* catch ctrl click */
6728 if (e
->type
== GDK_BUTTON_RELEASE
&&
6729 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6734 return (XT_CB_PASSTHROUGH
);
6738 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6740 struct mime_type
*m
;
6742 m
= find_mime_type(mime_type
);
6750 show_oops(t
, "can't fork mime handler");
6760 execlp(m
->mt_action
, m
->mt_action
,
6761 webkit_network_request_get_uri(request
), (void *)NULL
);
6770 get_mime_type(char *file
)
6772 const char *mime_type
;
6776 if (g_str_has_prefix(file
, "file://"))
6777 file
+= strlen("file://");
6779 gf
= g_file_new_for_path(file
);
6780 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6782 mime_type
= g_file_info_get_content_type(fi
);
6790 run_download_mimehandler(char *mime_type
, char *file
)
6792 struct mime_type
*m
;
6794 m
= find_mime_type(mime_type
);
6800 show_oops(NULL
, "can't fork download mime handler");
6810 if (g_str_has_prefix(file
, "file://"))
6811 file
+= strlen("file://");
6812 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6821 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6824 WebKitDownloadStatus status
;
6825 const gchar
*file
= NULL
, *mime
= NULL
;
6827 if (download
== NULL
)
6829 status
= webkit_download_get_status(download
);
6830 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6833 file
= webkit_download_get_destination_uri(download
);
6836 mime
= get_mime_type((char *)file
);
6840 run_download_mimehandler((char *)mime
, (char *)file
);
6844 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6845 WebKitNetworkRequest
*request
, char *mime_type
,
6846 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6849 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6853 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6854 t
->tab_id
, mime_type
);
6856 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6857 webkit_web_policy_decision_ignore(decision
);
6862 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6863 webkit_web_policy_decision_download(decision
);
6871 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6875 const gchar
*suggested_name
;
6876 gchar
*filename
= NULL
;
6878 struct download
*download_entry
;
6881 if (wk_download
== NULL
|| t
== NULL
) {
6882 show_oops(NULL
, "%s invalid parameters", __func__
);
6886 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6887 if (suggested_name
== NULL
)
6888 return (FALSE
); /* abort download */
6899 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6901 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6902 filename
: suggested_name
);
6904 } while (!stat(uri
+ strlen("file://"), &sb
));
6906 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6907 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6909 webkit_download_set_destination_uri(wk_download
, uri
);
6911 if (webkit_download_get_status(wk_download
) ==
6912 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6913 show_oops(t
, "%s: download failed to start", __func__
);
6915 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6917 /* connect "download first" mime handler */
6918 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6919 G_CALLBACK(download_status_changed_cb
), NULL
);
6921 download_entry
= g_malloc(sizeof(struct download
));
6922 download_entry
->download
= wk_download
;
6923 download_entry
->tab
= t
;
6924 download_entry
->id
= next_download_id
++;
6925 RB_INSERT(download_list
, &downloads
, download_entry
);
6926 /* get from history */
6927 g_object_ref(wk_download
);
6928 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6929 show_oops(t
, "Download of '%s' started...",
6930 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6939 /* sync other download manager tabs */
6940 update_download_tabs(NULL
);
6943 * NOTE: never redirect/render the current tab before this
6944 * function returns. This will cause the download to never start.
6946 return (ret
); /* start download */
6950 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6952 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6955 show_oops(NULL
, "webview_hover_cb");
6960 set_status(t
, uri
, XT_STATUS_LINK
);
6963 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6968 mark(struct tab
*t
, struct karg
*arg
)
6974 if ((index
= marktoindex(mark
)) == -1)
6977 if (arg
->i
== XT_MARK_SET
)
6978 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6979 else if (arg
->i
== XT_MARK_GOTO
) {
6980 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6981 show_oops(t
, "mark '%c' does not exist", mark
);
6984 /* XXX t->mark[index] can be bigger than the maximum if ajax or
6985 something changes the document size */
6986 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
6993 marks_clear(struct tab
*t
)
6997 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
6998 t
->mark
[i
] = XT_INVALID_MARK
;
7004 char file
[PATH_MAX
];
7005 char *line
= NULL
, *p
, mark
;
7010 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7011 if ((f
= fopen(file
, "r+")) == NULL
) {
7012 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7016 for (i
= 1; ; i
++) {
7017 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7018 if (feof(f
) || ferror(f
))
7020 if (strlen(line
) == 0 || line
[0] == '#') {
7026 p
= strtok(line
, " \t");
7028 if (p
== NULL
|| strlen(p
) != 1 ||
7029 (index
= marktoindex(*p
)) == -1) {
7030 warnx("corrupt quickmarks file, line %d", i
);
7035 p
= strtok(NULL
, " \t");
7036 if (qmarks
[index
] != NULL
)
7037 g_free(qmarks
[index
]);
7038 qmarks
[index
] = g_strdup(p
);
7049 char file
[PATH_MAX
];
7053 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7054 if ((f
= fopen(file
, "r+")) == NULL
) {
7055 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7059 for (i
= 0; i
< XT_NOMARKS
; i
++)
7060 if (qmarks
[i
] != NULL
)
7061 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7069 qmark(struct tab
*t
, struct karg
*arg
)
7074 mark
= arg
->s
[strlen(arg
->s
)-1];
7075 index
= marktoindex(mark
);
7081 if (qmarks
[index
] != NULL
)
7082 g_free(qmarks
[index
]);
7084 qmarks_load(); /* sync if multiple instances */
7085 qmarks
[index
] = g_strdup(get_uri(t
));
7089 if (qmarks
[index
] != NULL
)
7090 load_uri(t
, qmarks
[index
]);
7092 show_oops(t
, "quickmark \"%c\" does not exist",
7098 if (qmarks
[index
] != NULL
)
7099 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7101 show_oops(t
, "quickmark \"%c\" does not exist",
7112 go_up(struct tab
*t
, struct karg
*args
)
7118 levels
= atoi(args
->s
);
7122 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7123 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7125 tmp
+= strlen(XT_PROTO_DELIM
);
7127 /* if an uri starts with a slash, leave it alone (for file:///) */
7134 p
= strrchr(tmp
, '/');
7148 gototab(struct tab
*t
, struct karg
*args
)
7151 struct karg arg
= {0, NULL
, -1};
7153 tab
= atoi(args
->s
);
7155 arg
.i
= XT_TAB_NEXT
;
7164 zoom_amount(struct tab
*t
, struct karg
*arg
)
7166 struct karg narg
= {0, NULL
, -1};
7168 narg
.i
= atoi(arg
->s
);
7169 resizetab(t
, &narg
);
7175 flip_colon(struct tab
*t
, struct karg
*arg
)
7177 struct karg narg
= {0, NULL
, -1};
7180 if (t
== NULL
|| arg
== NULL
)
7183 p
= strstr(arg
->s
, ":");
7195 /* buffer commands receive the regex that triggered them in arg.s */
7196 char bcmd
[XT_BUFCMD_SZ
];
7200 #define XT_PRE_NO (0)
7201 #define XT_PRE_YES (1)
7202 #define XT_PRE_MAYBE (2)
7204 int (*func
)(struct tab
*, struct karg
*);
7208 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7209 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7210 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7211 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7212 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7213 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7214 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7215 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7216 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7217 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7218 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7219 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7220 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7221 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7222 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7223 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7224 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7225 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7229 buffercmd_init(void)
7233 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7234 regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7239 buffercmd_abort(struct tab
*t
)
7243 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7244 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7247 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7248 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7252 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7254 struct karg arg
= {0, NULL
, -1};
7257 arg
.s
= g_strdup(bcmd
);
7259 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7260 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7270 buffercmd_addkey(struct tab
*t
, guint keyval
)
7273 char s
[XT_BUFCMD_SZ
];
7275 if (keyval
== GDK_Escape
) {
7277 return (XT_CB_HANDLED
);
7280 /* key with modifier or non-ascii character */
7281 if (!isascii(keyval
))
7282 return (XT_CB_PASSTHROUGH
);
7284 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7285 "to buffer \"%s\"\n", keyval
, bcmd
);
7287 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7288 if (bcmd
[i
] == '\0') {
7293 /* buffer full, ignore input */
7294 if (i
>= LENGTH(bcmd
) -1) {
7295 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7297 return (XT_CB_HANDLED
);
7300 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7302 /* find exact match */
7303 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7304 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7305 (size_t) 0, NULL
, 0) == 0) {
7306 buffercmd_execute(t
, &buffercmds
[i
]);
7310 /* find non exact matches to see if we need to abort ot not */
7311 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7312 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7315 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7316 if (isdigit(bcmd
[0])) {
7317 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7321 if (sscanf(bcmd
, "%s", s
) == 0)
7324 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7325 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7328 if (sscanf(bcmd
, "%s", s
) == 0)
7331 if (c
== -1 && buffercmds
[i
].precount
)
7333 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7336 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7337 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7340 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7345 return (XT_CB_HANDLED
);
7349 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7351 struct key_binding
*k
;
7353 /* handle keybindings if buffercmd is empty.
7354 if not empty, allow commands like C-n */
7355 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7356 TAILQ_FOREACH(k
, &kbl
, entry
)
7357 if (e
->keyval
== k
->key
7358 && (entry
? k
->use_in_entry
: 1)) {
7360 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7361 return (cmd_execute(t
, k
->cmd
));
7362 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7363 return (cmd_execute(t
, k
->cmd
));
7367 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7368 return buffercmd_addkey(t
, e
->keyval
);
7370 return (XT_CB_PASSTHROUGH
);
7374 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7376 char s
[2], buf
[128];
7377 const char *errstr
= NULL
;
7379 /* don't use w directly; use t->whatever instead */
7382 show_oops(NULL
, "wv_keypress_after_cb");
7383 return (XT_CB_PASSTHROUGH
);
7386 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7387 e
->keyval
, e
->state
, t
);
7391 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7393 return (XT_CB_HANDLED
);
7397 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7399 /* we have a string */
7401 /* we have a number */
7402 snprintf(buf
, sizeof buf
,
7403 "vimprobable_fire(%s)", t
->hint_num
);
7410 /* XXX unfuck this */
7411 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7412 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7413 /* last input was numerical */
7415 l
= strlen(t
->hint_num
);
7422 t
->hint_num
[l
] = '\0';
7426 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7427 /* last input was alphanumerical */
7429 l
= strlen(t
->hint_buf
);
7436 t
->hint_buf
[l
] = '\0';
7446 /* numerical input */
7447 if (CLEAN(e
->state
) == 0 &&
7448 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7449 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7450 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7451 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7452 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7456 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7457 "invalid link number\n");
7460 snprintf(buf
, sizeof buf
,
7461 "vimprobable_update_hints(%s)",
7463 t
->hint_mode
= XT_HINT_NUMERICAL
;
7467 /* empty the counter buffer */
7468 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7469 return (XT_CB_HANDLED
);
7472 /* alphanumerical input */
7473 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7474 e
->keyval
<= GDK_z
) ||
7475 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7476 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7477 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7478 e
->keyval
<= GDK_9
) ||
7479 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7480 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7481 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7482 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7483 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7484 " %s\n", t
->hint_buf
);
7486 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7489 snprintf(buf
, sizeof buf
,
7490 "vimprobable_show_hints('%s')", t
->hint_buf
);
7491 t
->hint_mode
= XT_HINT_ALPHANUM
;
7494 /* empty the counter buffer */
7495 bzero(t
->hint_num
, sizeof t
->hint_num
);
7496 return (XT_CB_HANDLED
);
7499 return (XT_CB_HANDLED
);
7502 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7503 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7504 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7507 return (handle_keypress(t
, e
, 0));
7511 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7515 /* Hide buffers, if they are visible, with escape. */
7516 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7517 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7518 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7520 return (XT_CB_HANDLED
);
7523 return (XT_CB_PASSTHROUGH
);
7527 search_continue(struct tab
*t
)
7529 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7530 gboolean rv
= FALSE
;
7534 if (strlen(c
) == 1) {
7535 webkit_web_view_unmark_text_matches(t
->wv
);
7540 t
->search_forward
= TRUE
;
7541 else if (c
[0] == '?')
7542 t
->search_forward
= FALSE
;
7552 search_cb(struct tab
*t
)
7554 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7557 if (search_continue(t
) == FALSE
)
7561 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7563 /* not found, mark red */
7564 gdk_color_parse(XT_COLOR_RED
, &color
);
7565 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7566 /* unmark and remove selection */
7567 webkit_web_view_unmark_text_matches(t
->wv
);
7568 /* my kingdom for a way to unselect text in webview */
7570 /* found, highlight all */
7571 webkit_web_view_unmark_text_matches(t
->wv
);
7572 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7573 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7574 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7575 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7583 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7585 const gchar
*c
= gtk_entry_get_text(w
);
7588 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7589 return (XT_CB_PASSTHROUGH
);
7592 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7593 e
->keyval
, e
->state
, t
);
7595 if (search_continue(t
) == FALSE
)
7598 /* if search length is > 4 then no longer play timeout games */
7599 if (strlen(c
) > 4) {
7601 g_source_remove(t
->search_id
);
7608 /* reestablish a new timer if the user types fast */
7610 g_source_remove(t
->search_id
);
7611 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7614 return (XT_CB_PASSTHROUGH
);
7618 match_uri(const gchar
*uri
, const gchar
*key
) {
7621 gboolean match
= FALSE
;
7625 if (!strncmp(key
, uri
, len
))
7628 voffset
= strstr(uri
, "/") + 2;
7629 if (!strncmp(key
, voffset
, len
))
7631 else if (g_str_has_prefix(voffset
, "www.")) {
7632 voffset
= voffset
+ strlen("www.");
7633 if (!strncmp(key
, voffset
, len
))
7642 cmd_getlist(int id
, char *key
)
7647 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7648 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7649 if (match_uri(h
->uri
, key
)) {
7650 cmd_status
.list
[c
] = (char *)h
->uri
;
7659 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7661 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7662 if (cmds
[i
].level
< dep
)
7664 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7666 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7674 cmd_getnext(int dir
)
7676 cmd_status
.index
+= dir
;
7678 if (cmd_status
.index
< 0)
7679 cmd_status
.index
= cmd_status
.len
- 1;
7680 else if (cmd_status
.index
>= cmd_status
.len
)
7681 cmd_status
.index
= 0;
7683 return cmd_status
.list
[cmd_status
.index
];
7687 cmd_tokenize(char *s
, char *tokens
[])
7691 size_t len
= strlen(s
);
7694 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7695 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7696 tok
= strtok_r(NULL
, " ", &last
), i
++)
7706 cmd_complete(struct tab
*t
, char *str
, int dir
)
7708 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7709 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7711 char *tok
, *match
, *s
= g_strdup(str
);
7713 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7716 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7719 for (i
= 0; isdigit(s
[i
]); i
++)
7722 for (; isspace(s
[i
]); i
++)
7727 levels
= cmd_tokenize(s
, tokens
);
7729 for (i
= 0; i
< levels
- 1; i
++) {
7732 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7733 if (cmds
[j
].level
< dep
)
7735 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7739 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7746 if (matchcount
== 1) {
7747 strlcat(res
, tok
, sizeof res
);
7748 strlcat(res
, " ", sizeof res
);
7758 if (cmd_status
.index
== -1)
7759 cmd_getlist(parent
, tokens
[i
]);
7761 if (cmd_status
.len
> 0) {
7762 match
= cmd_getnext(dir
);
7763 strlcat(res
, match
, sizeof res
);
7764 gtk_entry_set_text(w
, res
);
7765 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7772 cmd_execute(struct tab
*t
, char *str
)
7774 struct cmd
*cmd
= NULL
;
7775 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7777 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7778 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7779 struct karg arg
= {0, NULL
, -1};
7784 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7790 while (isspace(s
[0]))
7793 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7794 prefix
= atoi(prefixstr
);
7798 for (tok
= strtok_r(s
, " ", &last
); tok
;
7799 tok
= strtok_r(NULL
, " ", &last
)) {
7801 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7802 if (cmds
[j
].level
< dep
)
7804 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7806 if (cmds
[j
].level
== dep
&&
7807 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7811 if (len
== strlen(cmds
[j
].cmd
)) {
7817 if (matchcount
== 1) {
7822 show_oops(t
, "Invalid command: %s", str
);
7830 arg
.precount
= prefix
;
7831 else if (cmd_prefix
> 0)
7832 arg
.precount
= cmd_prefix
;
7834 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
7835 show_oops(t
, "No prefix allowed: %s", str
);
7839 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7840 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7841 arg
.precount
= atoi(arg
.s
);
7842 if (arg
.precount
<= 0) {
7843 if (arg
.s
[0] == '0')
7844 show_oops(t
, "Zero count");
7846 show_oops(t
, "Trailing characters");
7851 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
7852 __func__
, arg
.precount
, arg
.s
);
7868 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7871 show_oops(NULL
, "entry_key_cb invalid parameters");
7872 return (XT_CB_PASSTHROUGH
);
7875 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7876 e
->keyval
, e
->state
, t
);
7880 if (e
->keyval
== GDK_Escape
) {
7881 /* don't use focus_webview(t) because we want to type :cmds */
7882 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7885 return (handle_keypress(t
, e
, 1));
7889 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7891 int rv
= XT_CB_HANDLED
;
7892 const gchar
*c
= gtk_entry_get_text(w
);
7895 show_oops(NULL
, "cmd_keypress_cb parameters");
7896 return (XT_CB_PASSTHROUGH
);
7899 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7900 e
->keyval
, e
->state
, t
);
7904 e
->keyval
= GDK_Escape
;
7905 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7906 e
->keyval
= GDK_Escape
;
7908 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7909 e
->keyval
!= GDK_ISO_Left_Tab
)
7910 cmd_status
.index
= -1;
7912 switch (e
->keyval
) {
7915 cmd_complete(t
, (char *)&c
[1], 1);
7917 case GDK_ISO_Left_Tab
:
7919 cmd_complete(t
, (char *)&c
[1], -1);
7923 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7931 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7932 webkit_web_view_unmark_text_matches(t
->wv
);
7936 rv
= XT_CB_PASSTHROUGH
;
7942 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7945 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7946 return (XT_CB_PASSTHROUGH
);
7948 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7953 if (show_url
== 0 || t
->focus_wv
)
7956 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7958 return (XT_CB_PASSTHROUGH
);
7962 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7965 const gchar
*c
= gtk_entry_get_text(entry
);
7968 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7972 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7979 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7985 if (c
[0] == '/' || c
[0] == '?') {
7986 /* see if there is a timer pending */
7988 g_source_remove(t
->search_id
);
7993 if (t
->search_text
) {
7994 g_free(t
->search_text
);
7995 t
->search_text
= NULL
;
7998 t
->search_text
= g_strdup(s
);
8000 g_free(global_search
);
8001 global_search
= g_strdup(s
);
8002 t
->search_forward
= c
[0] == '/';
8014 backward_cb(GtkWidget
*w
, struct tab
*t
)
8019 show_oops(NULL
, "backward_cb invalid parameters");
8023 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8030 forward_cb(GtkWidget
*w
, struct tab
*t
)
8035 show_oops(NULL
, "forward_cb invalid parameters");
8039 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8041 a
.i
= XT_NAV_FORWARD
;
8046 home_cb(GtkWidget
*w
, struct tab
*t
)
8049 show_oops(NULL
, "home_cb invalid parameters");
8053 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8059 stop_cb(GtkWidget
*w
, struct tab
*t
)
8061 WebKitWebFrame
*frame
;
8064 show_oops(NULL
, "stop_cb invalid parameters");
8068 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8070 frame
= webkit_web_view_get_main_frame(t
->wv
);
8071 if (frame
== NULL
) {
8072 show_oops(t
, "stop_cb: no frame");
8076 webkit_web_frame_stop_loading(frame
);
8077 abort_favicon_download(t
);
8081 setup_webkit(struct tab
*t
)
8083 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8084 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8085 FALSE
, (char *)NULL
);
8087 warnx("webkit does not have \"enable-dns-prefetching\" property");
8088 g_object_set(G_OBJECT(t
->settings
),
8089 "user-agent", t
->user_agent
, (char *)NULL
);
8090 g_object_set(G_OBJECT(t
->settings
),
8091 "enable-scripts", enable_scripts
, (char *)NULL
);
8092 g_object_set(G_OBJECT(t
->settings
),
8093 "enable-plugins", enable_plugins
, (char *)NULL
);
8094 g_object_set(G_OBJECT(t
->settings
),
8095 "javascript-can-open-windows-automatically", enable_scripts
,
8097 g_object_set(G_OBJECT(t
->settings
),
8098 "enable-html5-database", FALSE
, (char *)NULL
);
8099 g_object_set(G_OBJECT(t
->settings
),
8100 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8101 g_object_set(G_OBJECT(t
->settings
),
8102 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8103 g_object_set(G_OBJECT(t
->settings
),
8104 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8105 g_object_set(G_OBJECT(t
->wv
),
8106 "full-content-zoom", TRUE
, (char *)NULL
);
8108 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8112 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8114 struct tab
*ti
, *t
= NULL
;
8115 gdouble view_size
, value
, max
;
8118 TAILQ_FOREACH(ti
, &tabs
, entry
)
8119 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8127 if (adjustment
== NULL
)
8128 adjustment
= gtk_scrolled_window_get_vadjustment(
8129 GTK_SCROLLED_WINDOW(t
->browser_win
));
8131 view_size
= gtk_adjustment_get_page_size(adjustment
);
8132 value
= gtk_adjustment_get_value(adjustment
);
8133 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8136 position
= g_strdup("All");
8137 else if (value
== max
)
8138 position
= g_strdup("Bot");
8139 else if (value
== 0)
8140 position
= g_strdup("Top");
8142 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8144 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8151 create_browser(struct tab
*t
)
8155 GtkAdjustment
*adjustment
;
8158 show_oops(NULL
, "create_browser invalid parameters");
8162 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8163 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8164 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8165 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8167 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8168 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8169 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8171 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8172 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8175 t
->settings
= webkit_web_settings_new();
8177 if (user_agent
== NULL
) {
8178 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8180 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8183 t
->user_agent
= g_strdup(user_agent
);
8185 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8188 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8189 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8190 G_CALLBACK(update_statusbar_position
), NULL
);
8202 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8203 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8204 gtk_widget_set_name(w
, "xxxterm");
8205 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8206 g_signal_connect(G_OBJECT(w
), "delete_event",
8207 G_CALLBACK (gtk_main_quit
), NULL
);
8213 create_kiosk_toolbar(struct tab
*t
)
8215 GtkWidget
*toolbar
= NULL
, *b
;
8217 b
= gtk_hbox_new(FALSE
, 0);
8219 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8221 /* backward button */
8222 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8223 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8224 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8225 G_CALLBACK(backward_cb
), t
);
8226 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8228 /* forward button */
8229 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8230 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8231 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8232 G_CALLBACK(forward_cb
), t
);
8233 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8236 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8237 gtk_widget_set_sensitive(t
->gohome
, true);
8238 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8239 G_CALLBACK(home_cb
), t
);
8240 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8242 /* create widgets but don't use them */
8243 t
->uri_entry
= gtk_entry_new();
8244 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8245 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8246 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8252 create_toolbar(struct tab
*t
)
8254 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8256 b
= gtk_hbox_new(FALSE
, 0);
8258 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8260 /* backward button */
8261 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8262 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8263 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8264 G_CALLBACK(backward_cb
), t
);
8265 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8267 /* forward button */
8268 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8269 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8270 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8271 G_CALLBACK(forward_cb
), t
);
8272 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8276 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8277 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8278 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8279 G_CALLBACK(stop_cb
), t
);
8280 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8284 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8285 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8286 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8287 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8288 G_CALLBACK(js_toggle_cb
), t
);
8289 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8291 t
->uri_entry
= gtk_entry_new();
8292 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8293 G_CALLBACK(activate_uri_entry_cb
), t
);
8294 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8295 G_CALLBACK(entry_key_cb
), t
);
8297 eb1
= gtk_hbox_new(FALSE
, 0);
8298 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8299 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8300 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8303 if (search_string
) {
8305 t
->search_entry
= gtk_entry_new();
8306 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8307 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8308 G_CALLBACK(activate_search_entry_cb
), t
);
8309 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8310 G_CALLBACK(entry_key_cb
), t
);
8311 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8312 eb2
= gtk_hbox_new(FALSE
, 0);
8313 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8314 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8316 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8323 create_buffers(struct tab
*t
)
8325 GtkCellRenderer
*renderer
;
8328 view
= gtk_tree_view_new();
8330 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8332 renderer
= gtk_cell_renderer_text_new();
8333 gtk_tree_view_insert_column_with_attributes
8334 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
8336 renderer
= gtk_cell_renderer_text_new();
8337 gtk_tree_view_insert_column_with_attributes
8338 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8341 gtk_tree_view_set_model
8342 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8348 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8349 GtkTreeViewColumn
*col
, struct tab
*t
)
8354 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8356 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8359 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8360 set_current_tab(id
- 1);
8366 /* after tab reordering/creation/removal */
8373 TAILQ_FOREACH(t
, &tabs
, entry
) {
8374 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8375 if (t
->tab_id
> maxid
)
8378 gtk_widget_show(t
->tab_elems
.sep
);
8381 TAILQ_FOREACH(t
, &tabs
, entry
) {
8382 if (t
->tab_id
== maxid
) {
8383 gtk_widget_hide(t
->tab_elems
.sep
);
8389 /* after active tab change */
8391 recolor_compact_tabs(void)
8397 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8398 TAILQ_FOREACH(t
, &tabs
, entry
)
8399 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8402 curid
= gtk_notebook_get_current_page(notebook
);
8403 TAILQ_FOREACH(t
, &tabs
, entry
)
8404 if (t
->tab_id
== curid
) {
8405 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8406 gtk_widget_modify_fg(t
->tab_elems
.label
,
8407 GTK_STATE_NORMAL
, &color
);
8413 set_current_tab(int page_num
)
8415 buffercmd_abort(get_current_tab());
8416 gtk_notebook_set_current_page(notebook
, page_num
);
8417 recolor_compact_tabs();
8421 undo_close_tab_save(struct tab
*t
)
8425 struct undo
*u1
, *u2
;
8427 WebKitWebHistoryItem
*item
;
8429 if ((uri
= get_uri(t
)) == NULL
)
8432 u1
= g_malloc0(sizeof(struct undo
));
8433 u1
->uri
= g_strdup(uri
);
8435 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8437 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8438 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8441 /* forward history */
8442 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8446 u1
->history
= g_list_prepend(u1
->history
,
8447 webkit_web_history_item_copy(item
));
8448 items
= g_list_next(items
);
8453 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8454 u1
->history
= g_list_prepend(u1
->history
,
8455 webkit_web_history_item_copy(item
));
8459 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8463 u1
->history
= g_list_prepend(u1
->history
,
8464 webkit_web_history_item_copy(item
));
8465 items
= g_list_next(items
);
8468 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8470 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8471 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8472 TAILQ_REMOVE(&undos
, u2
, entry
);
8474 g_list_free(u2
->history
);
8483 delete_tab(struct tab
*t
)
8487 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8493 TAILQ_REMOVE(&tabs
, t
, entry
);
8495 /* Halt all webkit activity. */
8496 abort_favicon_download(t
);
8497 webkit_web_view_stop_loading(t
->wv
);
8499 /* Save the tab, so we can undo the close. */
8500 undo_close_tab_save(t
);
8502 if (browser_mode
== XT_BM_KIOSK
) {
8503 gtk_widget_destroy(t
->uri_entry
);
8504 gtk_widget_destroy(t
->stop
);
8505 gtk_widget_destroy(t
->js_toggle
);
8508 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8509 gtk_widget_destroy(t
->vbox
);
8513 g_source_remove(t
->search_id
);
8515 g_free(t
->user_agent
);
8516 g_free(t
->stylesheet
);
8520 if (TAILQ_EMPTY(&tabs
)) {
8521 if (browser_mode
== XT_BM_KIOSK
)
8522 create_new_tab(home
, NULL
, 1, -1);
8524 create_new_tab(NULL
, NULL
, 1, -1);
8527 /* recreate session */
8528 if (session_autosave
) {
8534 recolor_compact_tabs();
8538 update_statusbar_zoom(struct tab
*t
)
8541 char s
[16] = { '\0' };
8543 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8544 if ((zoom
<= 0.99 || zoom
>= 1.01))
8545 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8546 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8550 setzoom_webkit(struct tab
*t
, int adjust
)
8552 #define XT_ZOOMPERCENT 0.04
8557 show_oops(NULL
, "setzoom_webkit invalid parameters");
8561 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8562 if (adjust
== XT_ZOOM_IN
)
8563 zoom
+= XT_ZOOMPERCENT
;
8564 else if (adjust
== XT_ZOOM_OUT
)
8565 zoom
-= XT_ZOOMPERCENT
;
8566 else if (adjust
> 0)
8567 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8569 show_oops(t
, "setzoom_webkit invalid zoom value");
8573 if (zoom
< XT_ZOOMPERCENT
)
8574 zoom
= XT_ZOOMPERCENT
;
8575 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8576 update_statusbar_zoom(t
);
8580 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8582 struct tab
*t
= (struct tab
*) data
;
8584 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8586 switch (event
->button
) {
8588 set_current_tab(t
->tab_id
);
8599 append_tab(struct tab
*t
)
8604 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8605 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8609 create_sbe(int width
)
8613 sbe
= gtk_entry_new();
8614 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8615 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8616 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8617 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8618 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8619 gtk_widget_set_size_request(sbe
, width
, -1);
8625 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8630 WebKitWebHistoryItem
*item
;
8634 int sbe_p
= 0, sbe_b
= 0,
8637 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8639 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8640 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8644 t
= g_malloc0(sizeof *t
);
8646 if (title
== NULL
) {
8647 title
= "(untitled)";
8651 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8653 /* label + button for tab */
8654 b
= gtk_hbox_new(FALSE
, 0);
8657 #if GTK_CHECK_VERSION(2, 20, 0)
8658 t
->spinner
= gtk_spinner_new();
8660 t
->label
= gtk_label_new(title
);
8661 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8662 gtk_widget_set_size_request(t
->label
, 100, 0);
8663 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8664 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8665 gtk_widget_set_size_request(b
, 130, 0);
8667 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8668 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8669 #if GTK_CHECK_VERSION(2, 20, 0)
8670 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8674 if (browser_mode
== XT_BM_KIOSK
) {
8675 t
->toolbar
= create_kiosk_toolbar(t
);
8676 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8679 t
->toolbar
= create_toolbar(t
);
8681 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8689 t
->browser_win
= create_browser(t
);
8690 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8692 /* oops message for user feedback */
8693 t
->oops
= gtk_entry_new();
8694 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8695 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8696 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8697 gdk_color_parse(XT_COLOR_RED
, &color
);
8698 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8699 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8700 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8703 t
->cmd
= gtk_entry_new();
8704 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8705 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8706 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8707 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8710 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8712 t
->sbe
.statusbar
= gtk_entry_new();
8713 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8714 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8715 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8716 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8718 /* create these widgets only if specified in statusbar_elems */
8720 t
->sbe
.position
= create_sbe(40);
8721 t
->sbe
.zoom
= create_sbe(40);
8722 t
->sbe
.buffercmd
= create_sbe(60);
8724 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8726 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8729 /* gtk widgets cannot be added to a box twice. sbe_* variables
8730 make sure of this */
8731 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8735 GtkWidget
*sep
= gtk_vseparator_new();
8737 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8738 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8739 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8740 FALSE
, FALSE
, FALSE
);
8745 warnx("flag \"%c\" specified more than "
8746 "once in statusbar_elems\n", *p
);
8750 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8751 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8755 warnx("flag \"%c\" specified more than "
8756 "once in statusbar_elems\n", *p
);
8760 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8761 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8765 warnx("flag \"%c\" specified more than "
8766 "once in statusbar_elems\n", *p
);
8770 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8771 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8774 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8779 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8782 t
->buffers
= create_buffers(t
);
8783 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8785 /* xtp meaning is normal by default */
8786 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8788 /* set empty favicon */
8789 xt_icon_from_name(t
, "text-html");
8791 /* and show it all */
8792 gtk_widget_show_all(b
);
8793 gtk_widget_show_all(t
->vbox
);
8795 /* compact tab bar */
8796 t
->tab_elems
.label
= gtk_label_new(title
);
8797 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8798 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8799 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8800 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8802 t
->tab_elems
.eventbox
= gtk_event_box_new();
8803 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8804 t
->tab_elems
.sep
= gtk_vseparator_new();
8806 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8807 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8808 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8809 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8810 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8811 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8813 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8815 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8817 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8820 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8822 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8824 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8827 id
= position
>= 0 ? position
:
8828 gtk_notebook_get_current_page(notebook
) + 1;
8829 if (id
> gtk_notebook_get_n_pages(notebook
))
8832 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8833 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8834 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8835 t
->tab_elems
.eventbox
, id
);
8840 #if GTK_CHECK_VERSION(2, 20, 0)
8841 /* turn spinner off if we are a new tab without uri */
8843 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8844 gtk_widget_hide(t
->spinner
);
8847 /* make notebook tabs reorderable */
8848 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8850 /* compact tabs clickable */
8851 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8852 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8854 g_object_connect(G_OBJECT(t
->cmd
),
8855 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8856 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8857 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8858 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8861 /* reuse wv_button_cb to hide oops */
8862 g_object_connect(G_OBJECT(t
->oops
),
8863 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8866 g_signal_connect(t
->buffers
,
8867 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8868 g_object_connect(G_OBJECT(t
->buffers
),
8869 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8871 g_object_connect(G_OBJECT(t
->wv
),
8872 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8873 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8874 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8875 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8876 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8877 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8878 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8879 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8880 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8881 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8882 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8883 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8884 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8885 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8886 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8888 g_signal_connect(t
->wv
,
8889 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8891 * XXX this puts invalid url in uri_entry and that is undesirable
8894 g_signal_connect(t
->wv
,
8895 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8897 g_signal_connect(t
->wv
,
8898 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8900 /* hijack the unused keys as if we were the browser */
8901 g_object_connect(G_OBJECT(t
->toolbar
),
8902 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8905 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8906 G_CALLBACK(tab_close_cb
), t
);
8912 url_set_visibility();
8913 statusbar_set_visibility();
8916 set_current_tab(t
->tab_id
);
8917 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8922 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8926 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8931 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8932 /* restore the tab's history */
8933 if (u
&& u
->history
) {
8937 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8938 items
= g_list_next(items
);
8941 item
= g_list_nth_data(u
->history
, u
->back
);
8943 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8946 g_list_free(u
->history
);
8948 webkit_web_back_forward_list_clear(t
->bfl
);
8950 recolor_compact_tabs();
8951 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
8956 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8962 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8964 if (gtk_notebook_get_current_page(notebook
) == -1)
8967 TAILQ_FOREACH(t
, &tabs
, entry
) {
8968 if (t
->tab_id
== pn
) {
8969 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
8972 uri
= get_title(t
, TRUE
);
8973 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8979 /* can't use focus_webview here */
8980 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8987 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8990 struct tab
*t
= NULL
, *tt
;
8994 TAILQ_FOREACH(tt
, &tabs
, entry
)
8995 if (tt
->tab_id
== pn
) {
9000 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9002 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9007 menuitem_response(struct tab
*t
)
9009 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9013 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9015 GtkWidget
*menu
, *menu_items
;
9016 GdkEventButton
*bevent
;
9020 if (event
->type
== GDK_BUTTON_PRESS
) {
9021 bevent
= (GdkEventButton
*) event
;
9022 menu
= gtk_menu_new();
9024 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9025 if ((uri
= get_uri(ti
)) == NULL
)
9026 /* XXX make sure there is something to print */
9027 /* XXX add gui pages in here to look purdy */
9029 menu_items
= gtk_menu_item_new_with_label(uri
);
9030 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9031 gtk_widget_show(menu_items
);
9033 g_signal_connect_swapped((menu_items
),
9034 "activate", G_CALLBACK(menuitem_response
),
9038 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9039 bevent
->button
, bevent
->time
);
9041 /* unref object so it'll free itself when popped down */
9042 #if !GTK_CHECK_VERSION(3, 0, 0)
9043 /* XXX does not need unref with gtk+3? */
9044 g_object_ref_sink(menu
);
9045 g_object_unref(menu
);
9048 return (TRUE
/* eat event */);
9051 return (FALSE
/* propagate */);
9055 icon_size_map(int icon_size
)
9057 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9058 icon_size
> GTK_ICON_SIZE_DIALOG
)
9059 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9065 create_button(char *name
, char *stockid
, int size
)
9067 GtkWidget
*button
, *image
;
9071 rcstring
= g_strdup_printf(
9072 "style \"%s-style\"\n"
9074 " GtkWidget::focus-padding = 0\n"
9075 " GtkWidget::focus-line-width = 0\n"
9079 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9080 gtk_rc_parse_string(rcstring
);
9082 button
= gtk_button_new();
9083 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9084 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9086 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9087 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9088 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9089 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9090 gtk_widget_set_name(button
, name
);
9091 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9097 button_set_stockid(GtkWidget
*button
, char *stockid
)
9101 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9102 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9103 gtk_button_set_image(GTK_BUTTON(button
), image
);
9107 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9110 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9113 if (xterm_workaround
== 0)
9117 * xterm doesn't play nice with clipboards because it clears the
9118 * primary when clicked. We rely on primary being set to properly
9119 * handle middle mouse button clicks (paste). So when someone clears
9120 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9121 * other application behavior (as in DON'T clear primary).
9124 p
= gtk_clipboard_wait_for_text(primary
);
9126 if (gdk_property_get(gdk_get_default_root_window(),
9128 gdk_atom_intern("STRING", FALSE
),
9130 1024 * 1024 /* picked out of my butt */,
9136 /* yes sir, we need to NUL the string */
9138 gtk_clipboard_set_text(primary
, p
, -1);
9152 char file
[PATH_MAX
];
9155 vbox
= gtk_vbox_new(FALSE
, 0);
9156 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9157 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9158 #if !GTK_CHECK_VERSION(3, 0, 0)
9159 /* XXX seems to be needed with gtk+2 */
9160 gtk_notebook_set_tab_hborder(notebook
, 0);
9161 gtk_notebook_set_tab_vborder(notebook
, 0);
9163 gtk_notebook_set_scrollable(notebook
, TRUE
);
9164 gtk_notebook_set_show_border(notebook
, FALSE
);
9165 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9167 abtn
= gtk_button_new();
9168 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9169 gtk_widget_set_size_request(arrow
, -1, -1);
9170 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9171 gtk_widget_set_size_request(abtn
, -1, 20);
9173 #if GTK_CHECK_VERSION(2, 20, 0)
9174 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9176 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9178 /* compact tab bar */
9179 tab_bar
= gtk_hbox_new(TRUE
, 0);
9181 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9182 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9183 gtk_widget_set_size_request(vbox
, -1, -1);
9185 g_object_connect(G_OBJECT(notebook
),
9186 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9188 g_object_connect(G_OBJECT(notebook
),
9189 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9190 NULL
, (char *)NULL
);
9191 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9192 G_CALLBACK(arrow_cb
), NULL
);
9194 main_window
= create_window();
9195 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9198 for (i
= 0; i
< LENGTH(icons
); i
++) {
9199 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9200 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9201 l
= g_list_append(l
, pb
);
9203 gtk_window_set_default_icon_list(l
);
9205 /* clipboard work around */
9206 if (xterm_workaround
)
9208 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9209 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9211 gtk_widget_show_all(abtn
);
9212 gtk_widget_show_all(main_window
);
9213 notebook_tab_set_visibility();
9217 set_hook(void **hook
, char *name
)
9220 errx(1, "set_hook");
9222 if (*hook
== NULL
) {
9223 *hook
= dlsym(RTLD_NEXT
, name
);
9225 errx(1, "can't hook %s", name
);
9229 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9231 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9233 g_return_val_if_fail(cookie1
, FALSE
);
9234 g_return_val_if_fail(cookie2
, FALSE
);
9236 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9237 !strcmp (cookie1
->value
, cookie2
->value
) &&
9238 !strcmp (cookie1
->path
, cookie2
->path
) &&
9239 !strcmp (cookie1
->domain
, cookie2
->domain
));
9243 transfer_cookies(void)
9246 SoupCookie
*sc
, *pc
;
9248 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9250 for (;cf
; cf
= cf
->next
) {
9252 sc
= soup_cookie_copy(pc
);
9253 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9256 soup_cookies_free(cf
);
9260 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9265 print_cookie("soup_cookie_jar_delete_cookie", c
);
9267 if (cookies_enabled
== 0)
9270 if (jar
== NULL
|| c
== NULL
)
9273 /* find and remove from persistent jar */
9274 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9276 for (;cf
; cf
= cf
->next
) {
9278 if (soup_cookie_equal(ci
, c
)) {
9279 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9284 soup_cookies_free(cf
);
9286 /* delete from session jar */
9287 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9291 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9293 struct domain
*d
= NULL
;
9297 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9298 jar
, p_cookiejar
, s_cookiejar
);
9300 if (cookies_enabled
== 0)
9303 /* see if we are up and running */
9304 if (p_cookiejar
== NULL
) {
9305 _soup_cookie_jar_add_cookie(jar
, cookie
);
9308 /* disallow p_cookiejar adds, shouldn't happen */
9309 if (jar
== p_cookiejar
)
9313 if (jar
== NULL
|| cookie
== NULL
)
9316 if (enable_cookie_whitelist
&&
9317 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9319 DNPRINTF(XT_D_COOKIE
,
9320 "soup_cookie_jar_add_cookie: reject %s\n",
9322 if (save_rejected_cookies
) {
9323 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9324 show_oops(NULL
, "can't open reject cookie file");
9327 fseek(r_cookie_f
, 0, SEEK_END
);
9328 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9329 cookie
->http_only
? "#HttpOnly_" : "",
9331 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9333 cookie
->secure
? "TRUE" : "FALSE",
9335 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9342 if (!allow_volatile_cookies
)
9346 if (cookie
->expires
== NULL
&& session_timeout
) {
9347 soup_cookie_set_expires(cookie
,
9348 soup_date_new_from_now(session_timeout
));
9349 print_cookie("modified add cookie", cookie
);
9352 /* see if we are white listed for persistence */
9353 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9354 /* add to persistent jar */
9355 c
= soup_cookie_copy(cookie
);
9356 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9357 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9360 /* add to session jar */
9361 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9362 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9368 char file
[PATH_MAX
];
9370 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9371 "soup_cookie_jar_add_cookie");
9372 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9373 "soup_cookie_jar_delete_cookie");
9375 if (cookies_enabled
== 0)
9379 * the following code is intricate due to overriding several libsoup
9381 * do not alter order of these operations.
9384 /* rejected cookies */
9385 if (save_rejected_cookies
)
9386 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9389 /* persistent cookies */
9390 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9391 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9393 /* session cookies */
9394 s_cookiejar
= soup_cookie_jar_new();
9395 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9396 cookie_policy
, (void *)NULL
);
9399 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9403 setup_proxy(char *uri
)
9408 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9409 soup_uri_free(proxy_uri
);
9413 if (http_proxy
!= uri
) {
9420 http_proxy
= g_strdup(uri
);
9421 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9422 suri
= soup_uri_new(http_proxy
);
9423 if (!(suri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(suri
)))
9424 g_object_set(session
, "proxy-uri", proxy_uri
,
9427 soup_uri_free(suri
);
9432 set_http_proxy(char *proxy
)
9439 /* see if we need to clear it instead */
9440 if (strlen(proxy
) == 0) {
9445 uri
= soup_uri_new(proxy
);
9446 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9457 send_cmd_to_socket(char *cmd
)
9460 struct sockaddr_un sa
;
9462 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9463 warnx("%s: socket", __func__
);
9467 sa
.sun_family
= AF_UNIX
;
9468 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9469 work_dir
, XT_SOCKET_FILE
);
9472 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9473 warnx("%s: connect", __func__
);
9477 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9478 warnx("%s: send", __func__
);
9489 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9492 char str
[XT_MAX_URL_LENGTH
];
9493 socklen_t t
= sizeof(struct sockaddr_un
);
9494 struct sockaddr_un sa
;
9499 gint fd
= g_io_channel_unix_get_fd(source
);
9501 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9506 if (getpeereid(s
, &uid
, &gid
) == -1) {
9510 if (uid
!= getuid() || gid
!= getgid()) {
9511 warnx("unauthorized user");
9517 warnx("not a valid user");
9521 n
= recv(s
, str
, sizeof(str
), 0);
9525 tt
= TAILQ_LAST(&tabs
, tab_list
);
9526 cmd_execute(tt
, str
);
9534 struct sockaddr_un sa
;
9536 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9537 warn("is_running: socket");
9541 sa
.sun_family
= AF_UNIX
;
9542 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9543 work_dir
, XT_SOCKET_FILE
);
9546 /* connect to see if there is a listener */
9547 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9548 rv
= 0; /* not running */
9550 rv
= 1; /* already running */
9561 struct sockaddr_un sa
;
9563 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9564 warn("build_socket: socket");
9568 sa
.sun_family
= AF_UNIX
;
9569 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9570 work_dir
, XT_SOCKET_FILE
);
9573 /* connect to see if there is a listener */
9574 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9575 /* no listener so we will */
9576 unlink(sa
.sun_path
);
9578 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9579 warn("build_socket: bind");
9583 if (listen(s
, 1) == -1) {
9584 warn("build_socket: listen");
9597 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9598 GtkTreeIter
*iter
, struct tab
*t
)
9602 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9610 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9611 GtkTreeIter
*iter
, struct tab
*t
)
9615 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9616 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9617 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9624 completion_add_uri(const gchar
*uri
)
9628 /* add uri to list_store */
9629 gtk_list_store_append(completion_model
, &iter
);
9630 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9634 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9635 GtkTreeIter
*iter
, gpointer user_data
)
9638 gboolean match
= FALSE
;
9640 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9646 match
= match_uri(value
, key
);
9653 completion_add(struct tab
*t
)
9655 /* enable completion for tab */
9656 t
->completion
= gtk_entry_completion_new();
9657 gtk_entry_completion_set_text_column(t
->completion
, 0);
9658 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9659 gtk_entry_completion_set_model(t
->completion
,
9660 GTK_TREE_MODEL(completion_model
));
9661 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9663 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9664 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9665 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9666 G_CALLBACK(completion_select_cb
), t
);
9667 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9668 G_CALLBACK(completion_hover_cb
), t
);
9676 if (stat(dir
, &sb
)) {
9677 if (mkdir(dir
, S_IRWXU
) == -1)
9678 err(1, "mkdir %s", dir
);
9680 err(1, "stat %s", dir
);
9682 if (S_ISDIR(sb
.st_mode
) == 0)
9683 errx(1, "%s not a dir", dir
);
9684 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9685 warnx("fixing invalid permissions on %s", dir
);
9686 if (chmod(dir
, S_IRWXU
) == -1)
9687 err(1, "chmod %s", dir
);
9695 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9701 main(int argc
, char *argv
[])
9704 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9705 char conf
[PATH_MAX
] = { '\0' };
9706 char file
[PATH_MAX
];
9707 char *env_proxy
= NULL
;
9711 struct sigaction sact
;
9712 GIOChannel
*channel
;
9717 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9721 RB_INIT(&downloads
);
9725 TAILQ_INIT(&aliases
);
9730 /* fiddle with ulimits */
9731 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9734 /* just use them all */
9735 rlp
.rlim_cur
= rlp
.rlim_max
;
9736 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9738 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9740 else if (rlp
.rlim_cur
<= 256)
9741 startpage_add("%s requires at least 256 file "
9742 "descriptors, currently it has up to %d available",
9743 __progname
, rlp
.rlim_cur
);
9746 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9755 errx(0 , "Version: %s", version
);
9758 strlcpy(conf
, optarg
, sizeof(conf
));
9761 strlcpy(named_session
, optarg
, sizeof(named_session
));
9782 gnutls_global_init();
9784 /* generate session keys for xtp pages */
9785 generate_xtp_session_key(&dl_session_key
);
9786 generate_xtp_session_key(&hl_session_key
);
9787 generate_xtp_session_key(&cl_session_key
);
9788 generate_xtp_session_key(&fl_session_key
);
9791 if (!g_thread_supported()) {
9792 g_thread_init(NULL
);
9794 gdk_threads_enter();
9796 gtk_init(&argc
, &argv
);
9799 bzero(&sact
, sizeof(sact
));
9800 sigemptyset(&sact
.sa_mask
);
9801 sact
.sa_handler
= sigchild
;
9802 sact
.sa_flags
= SA_NOCLDSTOP
;
9803 sigaction(SIGCHLD
, &sact
, NULL
);
9805 /* set download dir */
9806 pwd
= getpwuid(getuid());
9808 errx(1, "invalid user %d", getuid());
9809 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9811 /* compile buffer command regexes */
9814 /* set default string settings */
9815 home
= g_strdup("https://www.cyphertite.com");
9816 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9817 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9818 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9819 cmd_font_name
= g_strdup("monospace normal 9");
9820 oops_font_name
= g_strdup("monospace normal 9");
9821 statusbar_font_name
= g_strdup("monospace normal 9");
9822 tabbar_font_name
= g_strdup("monospace normal 9");
9823 statusbar_elems
= g_strdup("BP");
9825 /* read config file */
9826 if (strlen(conf
) == 0)
9827 snprintf(conf
, sizeof conf
, "%s/.%s",
9828 pwd
->pw_dir
, XT_CONF_FILE
);
9829 config_parse(conf
, 0);
9832 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9833 oops_font
= pango_font_description_from_string(oops_font_name
);
9834 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9835 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9837 /* working directory */
9838 if (strlen(work_dir
) == 0)
9839 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9840 pwd
->pw_dir
, XT_DIR
);
9843 /* icon cache dir */
9844 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9848 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9852 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9853 work_dir
, XT_SESSIONS_DIR
);
9854 xxx_dir(sessions_dir
);
9856 /* runtime settings that can override config file */
9857 if (runtime_settings
[0] != '\0')
9858 config_parse(runtime_settings
, 1);
9861 if (!strcmp(download_dir
, pwd
->pw_dir
))
9862 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9863 xxx_dir(download_dir
);
9865 /* favorites file */
9866 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9867 if (stat(file
, &sb
)) {
9868 warnx("favorites file doesn't exist, creating it");
9869 if ((f
= fopen(file
, "w")) == NULL
)
9870 err(1, "favorites");
9874 /* quickmarks file */
9875 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9876 if (stat(file
, &sb
)) {
9877 warnx("quickmarks file doesn't exist, creating it");
9878 if ((f
= fopen(file
, "w")) == NULL
)
9879 err(1, "quickmarks");
9884 session
= webkit_get_default_session();
9889 if (stat(ssl_ca_file
, &sb
)) {
9890 warnx("no CA file: %s", ssl_ca_file
);
9891 g_free(ssl_ca_file
);
9894 g_object_set(session
,
9895 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9896 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9901 env_proxy
= getenv("http_proxy");
9903 setup_proxy(env_proxy
);
9905 setup_proxy(http_proxy
);
9908 send_cmd_to_socket(argv
[0]);
9912 /* set some connection parameters */
9913 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9914 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9917 /* see if there is already an xxxterm running */
9918 if (single_instance
&& is_running()) {
9920 warnx("already running");
9925 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9926 send_cmd_to_socket(cmd
);
9936 /* uri completion */
9937 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9940 buffers_store
= gtk_list_store_new
9941 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9947 notebook_tab_set_visibility();
9949 if (save_global_history
)
9950 restore_global_history();
9952 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9953 restore_saved_tabs();
9955 a
.s
= named_session
;
9956 a
.i
= XT_SES_DONOTHING
;
9957 open_tabs(NULL
, &a
);
9960 /* see if we have an exception */
9961 if (!TAILQ_EMPTY(&spl
)) {
9962 create_new_tab("about:startpage", NULL
, focus
, -1);
9967 create_new_tab(argv
[0], NULL
, focus
, -1);
9974 if (TAILQ_EMPTY(&tabs
))
9975 create_new_tab(home
, NULL
, 1, -1);
9978 if ((s
= build_socket()) != -1) {
9979 channel
= g_io_channel_unix_new(s
);
9980 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
9985 if (!g_thread_supported()) {
9986 gdk_threads_leave();
9989 gnutls_global_deinit();