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 * multi letter commands
26 * pre and post counts for commands
27 * autocompletion on various inputs
28 * create privacy browsing
29 * - encrypted local data
45 #include <sys/types.h>
47 #if defined(__linux__)
48 #include "linux/util.h"
49 #include "linux/tree.h"
50 #elif defined(__FreeBSD__)
52 #include "freebsd/util.h"
58 #include <sys/queue.h>
60 #include <sys/socket.h>
63 #include <sys/resource.h>
66 #include <gdk/gdkkeysyms.h>
68 #if GTK_CHECK_VERSION(3,0,0)
69 /* we still use GDK_* instead of GDK_KEY_* */
70 #include <gdk/gdkkeysyms-compat.h>
73 #include <webkit/webkit.h>
74 #include <libsoup/soup.h>
75 #include <gnutls/gnutls.h>
76 #include <JavaScriptCore/JavaScript.h>
77 #include <gnutls/x509.h>
79 #include "javascript.h"
82 javascript.h borrowed from vimprobable2 under the following license:
84 Copyright (c) 2009 Leon Winter
85 Copyright (c) 2009 Hannes Schueller
86 Copyright (c) 2009 Matto Fransen
88 Permission is hereby granted, free of charge, to any person obtaining a copy
89 of this software and associated documentation files (the "Software"), to deal
90 in the Software without restriction, including without limitation the rights
91 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
92 copies of the Software, and to permit persons to whom the Software is
93 furnished to do so, subject to the following conditions:
95 The above copyright notice and this permission notice shall be included in
96 all copies or substantial portions of the Software.
98 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
99 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
100 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
101 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
102 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
103 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
107 static char *version
= "$xxxterm$";
109 /* hooked functions */
110 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
111 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
116 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
117 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
118 #define XT_D_MOVE 0x0001
119 #define XT_D_KEY 0x0002
120 #define XT_D_TAB 0x0004
121 #define XT_D_URL 0x0008
122 #define XT_D_CMD 0x0010
123 #define XT_D_NAV 0x0020
124 #define XT_D_DOWNLOAD 0x0040
125 #define XT_D_CONFIG 0x0080
126 #define XT_D_JS 0x0100
127 #define XT_D_FAVORITE 0x0200
128 #define XT_D_PRINTING 0x0400
129 #define XT_D_COOKIE 0x0800
130 #define XT_D_KEYBINDING 0x1000
131 #define XT_D_CLIP 0x2000
132 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) & \
170 TAILQ_ENTRY(tab
) entry
;
172 GtkWidget
*tab_content
;
181 GtkWidget
*uri_entry
;
182 GtkWidget
*search_entry
;
184 GtkWidget
*browser_win
;
185 GtkWidget
*statusbar_box
;
187 GtkWidget
*statusbar
;
197 GtkWidget
*js_toggle
;
198 GtkEntryCompletion
*completion
;
202 WebKitWebHistoryItem
*item
;
203 WebKitWebBackForwardList
*bfl
;
206 WebKitNetworkRequest
*icon_request
;
207 WebKitDownload
*icon_download
;
208 gchar
*icon_dest_uri
;
210 /* adjustments for browser */
213 GtkAdjustment
*adjust_h
;
214 GtkAdjustment
*adjust_v
;
220 int xtp_meaning
; /* identifies dls/favorites */
225 #define XT_HINT_NONE (0)
226 #define XT_HINT_NUMERICAL (1)
227 #define XT_HINT_ALPHANUM (2)
231 /* custom stylesheet */
240 WebKitWebSettings
*settings
;
244 TAILQ_HEAD(tab_list
, tab
);
247 RB_ENTRY(history
) entry
;
251 RB_HEAD(history_list
, history
);
254 RB_ENTRY(download
) entry
;
256 WebKitDownload
*download
;
259 RB_HEAD(download_list
, download
);
262 RB_ENTRY(domain
) entry
;
264 int handy
; /* app use */
266 RB_HEAD(domain_list
, domain
);
269 TAILQ_ENTRY(undo
) entry
;
272 int back
; /* Keeps track of how many back
273 * history items there are. */
275 TAILQ_HEAD(undo_tailq
, undo
);
277 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
278 int next_download_id
= 1;
287 #define XT_NAME ("XXXTerm")
288 #define XT_DIR (".xxxterm")
289 #define XT_CACHE_DIR ("cache")
290 #define XT_CERT_DIR ("certs/")
291 #define XT_SESSIONS_DIR ("sessions/")
292 #define XT_CONF_FILE ("xxxterm.conf")
293 #define XT_FAVS_FILE ("favorites")
294 #define XT_SAVED_TABS_FILE ("main_session")
295 #define XT_RESTART_TABS_FILE ("restart_tabs")
296 #define XT_SOCKET_FILE ("socket")
297 #define XT_HISTORY_FILE ("history")
298 #define XT_REJECT_FILE ("rejected.txt")
299 #define XT_COOKIE_FILE ("cookies.txt")
300 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
301 #define XT_CB_HANDLED (TRUE)
302 #define XT_CB_PASSTHROUGH (FALSE)
303 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
304 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
305 #define XT_DLMAN_REFRESH "10"
306 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
307 "td{overflow: hidden;" \
308 " padding: 2px 2px 2px 2px;" \
309 " border: 1px solid black;" \
310 " vertical-align:top;" \
311 " word-wrap: break-word}\n" \
312 "tr:hover{background: #ffff99}\n" \
313 "th{background-color: #cccccc;" \
314 " border: 1px solid black}\n" \
315 "table{width: 100%%;" \
316 " border: 1px black solid;" \
317 " border-collapse:collapse}\n" \
319 "border: 1px solid black;" \
322 ".progress-inner{float: left;" \
324 " background: green}\n" \
325 ".dlstatus{font-size: small;" \
326 " text-align: center}\n" \
328 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
329 #define XT_MAX_UNDO_CLOSE_TAB (32)
330 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
331 #define XT_PRINT_EXTRA_MARGIN 10
334 #define XT_COLOR_RED "#cc0000"
335 #define XT_COLOR_YELLOW "#ffff66"
336 #define XT_COLOR_BLUE "lightblue"
337 #define XT_COLOR_GREEN "#99ff66"
338 #define XT_COLOR_WHITE "white"
339 #define XT_COLOR_BLACK "black"
341 #define XT_COLOR_CT_BACKGROUND "#000000"
342 #define XT_COLOR_CT_INACTIVE "#dddddd"
343 #define XT_COLOR_CT_ACTIVE "#bbbb00"
344 #define XT_COLOR_CT_SEPARATOR "#555555"
347 * xxxterm "protocol" (xtp)
348 * We use this for managing stuff like downloads and favorites. They
349 * make magical HTML pages in memory which have xxxt:// links in order
350 * to communicate with xxxterm's internals. These links take the format:
351 * xxxt://class/session_key/action/arg
353 * Don't begin xtp class/actions as 0. atoi returns that on error.
355 * Typically we have not put addition of items in this framework, as
356 * adding items is either done via an ex-command or via a keybinding instead.
359 #define XT_XTP_STR "xxxt://"
361 /* XTP classes (xxxt://<class>) */
362 #define XT_XTP_INVALID 0 /* invalid */
363 #define XT_XTP_DL 1 /* downloads */
364 #define XT_XTP_HL 2 /* history */
365 #define XT_XTP_CL 3 /* cookies */
366 #define XT_XTP_FL 4 /* favorites */
368 /* XTP download actions */
369 #define XT_XTP_DL_LIST 1
370 #define XT_XTP_DL_CANCEL 2
371 #define XT_XTP_DL_REMOVE 3
373 /* XTP history actions */
374 #define XT_XTP_HL_LIST 1
375 #define XT_XTP_HL_REMOVE 2
377 /* XTP cookie actions */
378 #define XT_XTP_CL_LIST 1
379 #define XT_XTP_CL_REMOVE 2
381 /* XTP cookie actions */
382 #define XT_XTP_FL_LIST 1
383 #define XT_XTP_FL_REMOVE 2
386 #define XT_MOVE_INVALID (0)
387 #define XT_MOVE_DOWN (1)
388 #define XT_MOVE_UP (2)
389 #define XT_MOVE_BOTTOM (3)
390 #define XT_MOVE_TOP (4)
391 #define XT_MOVE_PAGEDOWN (5)
392 #define XT_MOVE_PAGEUP (6)
393 #define XT_MOVE_HALFDOWN (7)
394 #define XT_MOVE_HALFUP (8)
395 #define XT_MOVE_LEFT (9)
396 #define XT_MOVE_FARLEFT (10)
397 #define XT_MOVE_RIGHT (11)
398 #define XT_MOVE_FARRIGHT (12)
400 #define XT_TAB_LAST (-4)
401 #define XT_TAB_FIRST (-3)
402 #define XT_TAB_PREV (-2)
403 #define XT_TAB_NEXT (-1)
404 #define XT_TAB_INVALID (0)
405 #define XT_TAB_NEW (1)
406 #define XT_TAB_DELETE (2)
407 #define XT_TAB_DELQUIT (3)
408 #define XT_TAB_OPEN (4)
409 #define XT_TAB_UNDO_CLOSE (5)
410 #define XT_TAB_SHOW (6)
411 #define XT_TAB_HIDE (7)
412 #define XT_TAB_NEXTSTYLE (8)
414 #define XT_NAV_INVALID (0)
415 #define XT_NAV_BACK (1)
416 #define XT_NAV_FORWARD (2)
417 #define XT_NAV_RELOAD (3)
418 #define XT_NAV_RELOAD_CACHE (4)
420 #define XT_FOCUS_INVALID (0)
421 #define XT_FOCUS_URI (1)
422 #define XT_FOCUS_SEARCH (2)
424 #define XT_SEARCH_INVALID (0)
425 #define XT_SEARCH_NEXT (1)
426 #define XT_SEARCH_PREV (2)
428 #define XT_PASTE_CURRENT_TAB (0)
429 #define XT_PASTE_NEW_TAB (1)
431 #define XT_FONT_SET (0)
433 #define XT_URL_SHOW (1)
434 #define XT_URL_HIDE (2)
436 #define XT_WL_TOGGLE (1<<0)
437 #define XT_WL_ENABLE (1<<1)
438 #define XT_WL_DISABLE (1<<2)
439 #define XT_WL_FQDN (1<<3) /* default */
440 #define XT_WL_TOPLEVEL (1<<4)
441 #define XT_WL_PERSISTENT (1<<5)
442 #define XT_WL_SESSION (1<<6)
443 #define XT_WL_RELOAD (1<<7)
445 #define XT_SHOW (1<<7)
446 #define XT_DELETE (1<<8)
447 #define XT_SAVE (1<<9)
448 #define XT_OPEN (1<<10)
450 #define XT_CMD_OPEN (0)
451 #define XT_CMD_OPEN_CURRENT (1)
452 #define XT_CMD_TABNEW (2)
453 #define XT_CMD_TABNEW_CURRENT (3)
455 #define XT_STATUS_NOTHING (0)
456 #define XT_STATUS_LINK (1)
457 #define XT_STATUS_URI (2)
458 #define XT_STATUS_LOADING (3)
460 #define XT_SES_DONOTHING (0)
461 #define XT_SES_CLOSETABS (1)
463 #define XT_BM_NORMAL (0)
464 #define XT_BM_WHITELIST (1)
465 #define XT_BM_KIOSK (2)
467 #define XT_PREFIX (1<<0)
468 #define XT_USERARG (1<<1)
469 #define XT_URLARG (1<<2)
470 #define XT_INTARG (1<<3)
472 #define XT_TABS_NORMAL 0
473 #define XT_TABS_COMPACT 1
481 TAILQ_ENTRY(mime_type
) entry
;
483 TAILQ_HEAD(mime_type_list
, mime_type
);
489 TAILQ_ENTRY(alias
) entry
;
491 TAILQ_HEAD(alias_list
, alias
);
493 /* settings that require restart */
494 int tabless
= 0; /* allow only 1 tab */
495 int enable_socket
= 0;
496 int single_instance
= 0; /* only allow one xxxterm to run */
497 int fancy_bar
= 1; /* fancy toolbar */
498 int browser_mode
= XT_BM_NORMAL
;
499 int enable_localstorage
= 0;
501 /* runtime settings */
502 int show_tabs
= 1; /* show tabs on notebook */
503 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
504 int show_url
= 1; /* show url toolbar on notebook */
505 int show_statusbar
= 0; /* vimperator style status bar */
506 int ctrl_click_focus
= 0; /* ctrl click gets focus */
507 int cookies_enabled
= 1; /* enable cookies */
508 int read_only_cookies
= 0; /* enable to not write cookies */
509 int enable_scripts
= 1;
510 int enable_plugins
= 0;
511 int default_font_size
= 12;
512 gfloat default_zoom_level
= 1.0;
513 int window_height
= 768;
514 int window_width
= 1024;
515 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
516 int refresh_interval
= 10; /* download refresh interval */
517 int enable_cookie_whitelist
= 0;
518 int enable_js_whitelist
= 0;
519 int session_timeout
= 3600; /* cookie session timeout */
520 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
521 char *ssl_ca_file
= NULL
;
522 char *resource_dir
= NULL
;
523 gboolean ssl_strict_certs
= FALSE
;
524 int append_next
= 1; /* append tab after current tab */
526 char *search_string
= NULL
;
527 char *http_proxy
= NULL
;
528 char download_dir
[PATH_MAX
];
529 char runtime_settings
[PATH_MAX
]; /* override of settings */
530 int allow_volatile_cookies
= 0;
531 int save_global_history
= 0; /* save global history to disk */
532 char *user_agent
= NULL
;
533 int save_rejected_cookies
= 0;
534 int session_autosave
= 0;
535 int guess_search
= 0;
536 int dns_prefetch
= FALSE
;
537 gint max_connections
= 25;
538 gint max_host_connections
= 5;
539 gint enable_spell_checking
= 0;
540 char *spell_check_languages
= NULL
;
542 char *cmd_font_name
= NULL
;
543 char *oops_font_name
= NULL
;
544 char *statusbar_font_name
= NULL
;
545 char *tabbar_font_name
= NULL
;
546 PangoFontDescription
*cmd_font
;
547 PangoFontDescription
*oops_font
;
548 PangoFontDescription
*statusbar_font
;
549 PangoFontDescription
*tabbar_font
;
551 int btn_down
; /* M1 down in any wv */
555 int set_browser_mode(struct settings
*, char *);
556 int set_cookie_policy(struct settings
*, char *);
557 int set_download_dir(struct settings
*, char *);
558 int set_runtime_dir(struct settings
*, char *);
559 int set_tab_style(struct settings
*, char *);
560 int set_work_dir(struct settings
*, char *);
561 int add_alias(struct settings
*, char *);
562 int add_mime_type(struct settings
*, char *);
563 int add_cookie_wl(struct settings
*, char *);
564 int add_js_wl(struct settings
*, char *);
565 int add_kb(struct settings
*, char *);
566 void button_set_stockid(GtkWidget
*, char *);
567 GtkWidget
* create_button(char *, char *, int);
569 char *get_browser_mode(struct settings
*);
570 char *get_cookie_policy(struct settings
*);
571 char *get_download_dir(struct settings
*);
572 char *get_runtime_dir(struct settings
*);
573 char *get_tab_style(struct settings
*);
574 char *get_work_dir(struct settings
*);
576 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
577 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
578 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
579 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
580 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
582 void recalc_tabs(void);
583 void recolor_compact_tabs(void);
584 void set_current_tab(int page_num
);
585 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
588 int (*set
)(struct settings
*, char *);
589 char *(*get
)(struct settings
*);
590 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
593 struct special s_browser_mode
= {
599 struct special s_cookie
= {
605 struct special s_alias
= {
611 struct special s_mime
= {
617 struct special s_js
= {
623 struct special s_kb
= {
629 struct special s_cookie_wl
= {
635 struct special s_download_dir
= {
641 struct special s_work_dir
= {
647 struct special s_tab_style
= {
656 #define XT_S_INVALID (0)
659 #define XT_S_FLOAT (3)
661 #define XT_SF_RESTART (1<<0)
662 #define XT_SF_RUNTIME (1<<1)
668 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
669 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
670 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
671 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
672 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
673 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
674 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
675 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
676 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
677 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
678 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
679 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
680 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
681 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
682 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
683 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
684 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
685 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
686 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
687 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
688 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
689 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
690 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
691 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
692 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
693 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
694 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
695 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
696 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
697 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
698 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
699 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
700 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
701 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
702 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
703 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
704 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
705 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
706 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
707 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
708 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
709 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
710 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
713 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
714 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
715 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
716 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
718 /* runtime settings */
719 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
720 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
721 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
722 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
723 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
726 int about(struct tab
*, struct karg
*);
727 int blank(struct tab
*, struct karg
*);
728 int ca_cmd(struct tab
*, struct karg
*);
729 int cookie_show_wl(struct tab
*, struct karg
*);
730 int js_show_wl(struct tab
*, struct karg
*);
731 int help(struct tab
*, struct karg
*);
732 int set(struct tab
*, struct karg
*);
733 int stats(struct tab
*, struct karg
*);
734 int marco(struct tab
*, struct karg
*);
735 const char * marco_message(int *);
736 int xtp_page_cl(struct tab
*, struct karg
*);
737 int xtp_page_dl(struct tab
*, struct karg
*);
738 int xtp_page_fl(struct tab
*, struct karg
*);
739 int xtp_page_hl(struct tab
*, struct karg
*);
740 void xt_icon_from_file(struct tab
*, char *);
741 const gchar
*get_uri(struct tab
*);
742 const gchar
*get_title(struct tab
*, bool);
744 #define XT_URI_ABOUT ("about:")
745 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
746 #define XT_URI_ABOUT_ABOUT ("about")
747 #define XT_URI_ABOUT_BLANK ("blank")
748 #define XT_URI_ABOUT_CERTS ("certs")
749 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
750 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
751 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
752 #define XT_URI_ABOUT_FAVORITES ("favorites")
753 #define XT_URI_ABOUT_HELP ("help")
754 #define XT_URI_ABOUT_HISTORY ("history")
755 #define XT_URI_ABOUT_JSWL ("jswl")
756 #define XT_URI_ABOUT_SET ("set")
757 #define XT_URI_ABOUT_STATS ("stats")
758 #define XT_URI_ABOUT_MARCO ("marco")
762 int (*func
)(struct tab
*, struct karg
*);
764 { XT_URI_ABOUT_ABOUT
, about
},
765 { XT_URI_ABOUT_BLANK
, blank
},
766 { XT_URI_ABOUT_CERTS
, ca_cmd
},
767 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
768 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
769 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
770 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
771 { XT_URI_ABOUT_HELP
, help
},
772 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
773 { XT_URI_ABOUT_JSWL
, js_show_wl
},
774 { XT_URI_ABOUT_SET
, set
},
775 { XT_URI_ABOUT_STATS
, stats
},
776 { XT_URI_ABOUT_MARCO
, marco
},
779 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
780 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
781 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
782 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
783 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
784 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
785 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
788 extern char *__progname
;
791 GtkWidget
*main_window
;
792 GtkNotebook
*notebook
;
794 GtkWidget
*arrow
, *abtn
;
795 struct tab_list tabs
;
796 struct history_list hl
;
797 struct download_list downloads
;
798 struct domain_list c_wl
;
799 struct domain_list js_wl
;
800 struct undo_tailq undos
;
801 struct keybinding_list kbl
;
803 int updating_dl_tabs
= 0;
804 int updating_hl_tabs
= 0;
805 int updating_cl_tabs
= 0;
806 int updating_fl_tabs
= 0;
808 uint64_t blocked_cookies
= 0;
809 char named_session
[PATH_MAX
];
810 int icon_size_map(int);
812 GtkListStore
*completion_model
;
813 void completion_add(struct tab
*);
814 void completion_add_uri(const gchar
*);
815 GtkListStore
*buffers_store
;
816 void xxx_dir(char *);
821 int saved_errno
, status
;
826 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
830 if (errno
!= ECHILD
) {
832 clog_warn("sigchild: waitpid:");
838 if (WIFEXITED(status
)) {
839 if (WEXITSTATUS(status
) != 0) {
841 clog_warnx("sigchild: child exit status: %d",
842 WEXITSTATUS(status));
847 clog_warnx("sigchild: child is terminated abnormally");
856 is_g_object_setting(GObject
*o
, char *str
)
858 guint n_props
= 0, i
;
859 GParamSpec
**proplist
;
861 if (! G_IS_OBJECT(o
))
864 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
867 for (i
=0; i
< n_props
; i
++) {
868 if (! strcmp(proplist
[i
]->name
, str
))
875 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
879 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
881 "<title>%s</title>\n"
890 addstyles
? XT_PAGE_STYLE
: "",
899 * Display a web page from a HTML string in memory, rather than from a URL
902 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
907 /* we set this to indicate we want to manually do navaction */
909 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
911 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
913 /* set t->xtp_meaning */
914 for (i
= 0; i
< LENGTH(about_list
); i
++)
915 if (!strcmp(title
, about_list
[i
].name
)) {
920 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
921 #if GTK_CHECK_VERSION(2, 20, 0)
922 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
923 gtk_widget_hide(t
->spinner
);
925 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
926 xt_icon_from_file(t
, file
);
931 get_current_tab(void)
935 TAILQ_FOREACH(t
, &tabs
, entry
) {
936 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
940 warnx("%s: no current tab", __func__
);
946 set_status(struct tab
*t
, gchar
*s
, int status
)
954 case XT_STATUS_LOADING
:
955 type
= g_strdup_printf("Loading: %s", s
);
959 type
= g_strdup_printf("Link: %s", s
);
961 t
->status
= g_strdup(gtk_entry_get_text(
962 GTK_ENTRY(t
->sbe
.statusbar
)));
966 type
= g_strdup_printf("%s", s
);
968 t
->status
= g_strdup(type
);
972 t
->status
= g_strdup(s
);
974 case XT_STATUS_NOTHING
:
979 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
985 hide_cmd(struct tab
*t
)
987 gtk_widget_hide(t
->cmd
);
991 show_cmd(struct tab
*t
)
993 gtk_widget_hide(t
->oops
);
994 gtk_widget_show(t
->cmd
);
998 hide_buffers(struct tab
*t
)
1000 gtk_widget_hide(t
->buffers
);
1001 gtk_list_store_clear(buffers_store
);
1011 sort_tabs_by_page_num(struct tab
***stabs
)
1016 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1018 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1020 TAILQ_FOREACH(t
, &tabs
, entry
)
1021 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1027 buffers_make_list(void)
1030 const gchar
*title
= NULL
;
1032 struct tab
**stabs
= NULL
;
1034 num_tabs
= sort_tabs_by_page_num(&stabs
);
1036 for (i
= 0; i
< num_tabs
; i
++)
1038 gtk_list_store_append(buffers_store
, &iter
);
1039 title
= get_title(stabs
[i
], FALSE
);
1040 gtk_list_store_set(buffers_store
, &iter
,
1041 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1051 show_buffers(struct tab
*t
)
1053 buffers_make_list();
1054 gtk_widget_show(t
->buffers
);
1055 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1059 toggle_buffers(struct tab
*t
)
1061 if (gtk_widget_get_visible(t
->buffers
))
1068 buffers(struct tab
*t
, struct karg
*args
)
1076 hide_oops(struct tab
*t
)
1078 gtk_widget_hide(t
->oops
);
1082 show_oops(struct tab
*at
, const char *fmt
, ...)
1086 struct tab
*t
= NULL
;
1092 if ((t
= get_current_tab()) == NULL
)
1098 if (vasprintf(&msg
, fmt
, ap
) == -1)
1099 errx(1, "show_oops failed");
1102 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1103 gtk_widget_hide(t
->cmd
);
1104 gtk_widget_show(t
->oops
);
1108 get_as_string(struct settings
*s
)
1119 warnx("get_as_string skip %s\n", s
->name
);
1120 } else if (s
->type
== XT_S_INT
)
1121 r
= g_strdup_printf("%d", *s
->ival
);
1122 else if (s
->type
== XT_S_STR
)
1123 r
= g_strdup(*s
->sval
);
1124 else if (s
->type
== XT_S_FLOAT
)
1125 r
= g_strdup_printf("%f", *s
->fval
);
1127 r
= g_strdup_printf("INVALID TYPE");
1133 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1138 for (i
= 0; i
< LENGTH(rs
); i
++) {
1139 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1140 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1142 s
= get_as_string(&rs
[i
]);
1143 cb(&rs
[i
], s
, cb_args
);
1150 set_browser_mode(struct settings
*s
, char *val
)
1152 if (!strcmp(val
, "whitelist")) {
1153 browser_mode
= XT_BM_WHITELIST
;
1154 allow_volatile_cookies
= 0;
1155 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1156 cookies_enabled
= 1;
1157 enable_cookie_whitelist
= 1;
1158 read_only_cookies
= 0;
1159 save_rejected_cookies
= 0;
1160 session_timeout
= 3600;
1162 enable_js_whitelist
= 1;
1163 enable_localstorage
= 0;
1164 } else if (!strcmp(val
, "normal")) {
1165 browser_mode
= XT_BM_NORMAL
;
1166 allow_volatile_cookies
= 0;
1167 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1168 cookies_enabled
= 1;
1169 enable_cookie_whitelist
= 0;
1170 read_only_cookies
= 0;
1171 save_rejected_cookies
= 0;
1172 session_timeout
= 3600;
1174 enable_js_whitelist
= 0;
1175 enable_localstorage
= 1;
1176 } else if (!strcmp(val
, "kiosk")) {
1177 browser_mode
= XT_BM_KIOSK
;
1178 allow_volatile_cookies
= 0;
1179 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1180 cookies_enabled
= 1;
1181 enable_cookie_whitelist
= 0;
1182 read_only_cookies
= 0;
1183 save_rejected_cookies
= 0;
1184 session_timeout
= 3600;
1186 enable_js_whitelist
= 0;
1187 enable_localstorage
= 1;
1197 get_browser_mode(struct settings
*s
)
1201 if (browser_mode
== XT_BM_WHITELIST
)
1202 r
= g_strdup("whitelist");
1203 else if (browser_mode
== XT_BM_NORMAL
)
1204 r
= g_strdup("normal");
1205 else if (browser_mode
== XT_BM_KIOSK
)
1206 r
= g_strdup("kiosk");
1214 set_cookie_policy(struct settings
*s
, char *val
)
1216 if (!strcmp(val
, "no3rdparty"))
1217 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1218 else if (!strcmp(val
, "accept"))
1219 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1220 else if (!strcmp(val
, "reject"))
1221 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1229 get_cookie_policy(struct settings
*s
)
1233 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1234 r
= g_strdup("no3rdparty");
1235 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1236 r
= g_strdup("accept");
1237 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1238 r
= g_strdup("reject");
1246 get_download_dir(struct settings
*s
)
1248 if (download_dir
[0] == '\0')
1250 return (g_strdup(download_dir
));
1254 set_download_dir(struct settings
*s
, char *val
)
1257 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1258 pwd
->pw_dir
, &val
[1]);
1260 strlcpy(download_dir
, val
, sizeof download_dir
);
1267 * We use these to prevent people putting xxxt:// URLs on
1268 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1270 #define XT_XTP_SES_KEY_SZ 8
1271 #define XT_XTP_SES_KEY_HEX_FMT \
1272 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1273 char *dl_session_key
; /* downloads */
1274 char *hl_session_key
; /* history list */
1275 char *cl_session_key
; /* cookie list */
1276 char *fl_session_key
; /* favorites list */
1278 char work_dir
[PATH_MAX
];
1279 char certs_dir
[PATH_MAX
];
1280 char cache_dir
[PATH_MAX
];
1281 char sessions_dir
[PATH_MAX
];
1282 char cookie_file
[PATH_MAX
];
1283 SoupURI
*proxy_uri
= NULL
;
1284 SoupSession
*session
;
1285 SoupCookieJar
*s_cookiejar
;
1286 SoupCookieJar
*p_cookiejar
;
1287 char rc_fname
[PATH_MAX
];
1289 struct mime_type_list mtl
;
1290 struct alias_list aliases
;
1293 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1294 void delete_tab(struct tab
*);
1295 void adjustfont_webkit(struct tab
*, int);
1296 int run_script(struct tab
*, char *);
1297 int download_rb_cmp(struct download
*, struct download
*);
1298 gboolean
cmd_execute(struct tab
*t
, char *str
);
1301 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1303 return (strcmp(h1
->uri
, h2
->uri
));
1305 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1308 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1310 return (strcmp(d1
->d
, d2
->d
));
1312 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1315 get_work_dir(struct settings
*s
)
1317 if (work_dir
[0] == '\0')
1319 return (g_strdup(work_dir
));
1323 set_work_dir(struct settings
*s
, char *val
)
1326 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1327 pwd
->pw_dir
, &val
[1]);
1329 strlcpy(work_dir
, val
, sizeof work_dir
);
1335 get_tab_style(struct settings
*s
)
1337 if (tab_style
== XT_TABS_NORMAL
)
1338 return (g_strdup("normal"));
1340 return (g_strdup("compact"));
1344 set_tab_style(struct settings
*s
, char *val
)
1346 if (!strcmp(val
, "normal"))
1347 tab_style
= XT_TABS_NORMAL
;
1348 else if (!strcmp(val
, "compact"))
1349 tab_style
= XT_TABS_COMPACT
;
1357 * generate a session key to secure xtp commands.
1358 * pass in a ptr to the key in question and it will
1359 * be modified in place.
1362 generate_xtp_session_key(char **key
)
1364 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1370 /* make a new one */
1371 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1372 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1373 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1374 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1376 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1380 * validate a xtp session key.
1384 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1386 if (strcmp(trusted
, untrusted
) != 0) {
1387 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1396 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1398 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1400 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1402 struct valid_url_types
{
1413 valid_url_type(char *url
)
1417 for (i
= 0; i
< LENGTH(vut
); i
++)
1418 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1425 print_cookie(char *msg
, SoupCookie
*c
)
1431 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1432 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1433 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1434 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1435 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1436 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1437 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1438 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1439 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1440 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1444 walk_alias(struct settings
*s
,
1445 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1450 if (s
== NULL
|| cb
== NULL
) {
1451 show_oops(NULL
, "walk_alias invalid parameters");
1455 TAILQ_FOREACH(a
, &aliases
, entry
) {
1456 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1457 cb(s
, str
, cb_args
);
1463 match_alias(char *url_in
)
1467 char *url_out
= NULL
, *search
, *enc_arg
;
1469 search
= g_strdup(url_in
);
1471 if (strsep(&arg
, " \t") == NULL
) {
1472 show_oops(NULL
, "match_alias: NULL URL");
1476 TAILQ_FOREACH(a
, &aliases
, entry
) {
1477 if (!strcmp(search
, a
->a_name
))
1482 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1485 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1486 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1489 url_out
= g_strdup_printf(a
->a_uri
, "");
1497 guess_url_type(char *url_in
)
1500 char *url_out
= NULL
, *enc_search
= NULL
;
1502 url_out
= match_alias(url_in
);
1503 if (url_out
!= NULL
)
1508 * If there is no dot nor slash in the string and it isn't a
1509 * path to a local file and doesn't resolves to an IP, assume
1510 * that the user wants to search for the string.
1513 if (strchr(url_in
, '.') == NULL
&&
1514 strchr(url_in
, '/') == NULL
&&
1515 stat(url_in
, &sb
) != 0 &&
1516 gethostbyname(url_in
) == NULL
) {
1518 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1519 url_out
= g_strdup_printf(search_string
, enc_search
);
1525 /* XXX not sure about this heuristic */
1526 if (stat(url_in
, &sb
) == 0)
1527 url_out
= g_strdup_printf("file://%s", url_in
);
1529 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1531 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1537 load_uri(struct tab
*t
, gchar
*uri
)
1540 gchar
*newuri
= NULL
;
1546 /* Strip leading spaces. */
1547 while (*uri
&& isspace(*uri
))
1550 if (strlen(uri
) == 0) {
1555 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1557 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1558 for (i
= 0; i
< LENGTH(about_list
); i
++)
1559 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1560 bzero(&args
, sizeof args
);
1561 about_list
[i
].func(t
, &args
);
1562 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1566 show_oops(t
, "invalid about page");
1570 if (valid_url_type(uri
)) {
1571 newuri
= guess_url_type(uri
);
1575 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1576 webkit_web_view_load_uri(t
->wv
, uri
);
1583 get_uri(struct tab
*t
)
1585 const gchar
*uri
= NULL
;
1587 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
)
1588 uri
= webkit_web_view_get_uri(t
->wv
);
1590 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, about_list
[t
->xtp_meaning
].name
);
1596 get_title(struct tab
*t
, bool window
)
1598 const gchar
*set
= NULL
, *title
= NULL
;
1600 title
= webkit_web_view_get_title(t
->wv
);
1601 set
= title
? title
: get_uri(t
);
1603 if (!set
|| t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
) {
1604 set
= window
? XT_NAME
: "(untitled)";
1611 add_alias(struct settings
*s
, char *line
)
1614 struct alias
*a
= NULL
;
1616 if (s
== NULL
|| line
== NULL
) {
1617 show_oops(NULL
, "add_alias invalid parameters");
1622 a
= g_malloc(sizeof(*a
));
1624 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1625 show_oops(NULL
, "add_alias: incomplete alias definition");
1628 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1629 show_oops(NULL
, "add_alias: invalid alias definition");
1633 a
->a_name
= g_strdup(alias
);
1634 a
->a_uri
= g_strdup(l
);
1636 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1638 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1648 add_mime_type(struct settings
*s
, char *line
)
1652 struct mime_type
*m
= NULL
;
1653 int downloadfirst
= 0;
1655 /* XXX this could be smarter */
1657 if (line
== NULL
|| strlen(line
) == 0) {
1658 show_oops(NULL
, "add_mime_type invalid parameters");
1667 m
= g_malloc(sizeof(*m
));
1669 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1670 show_oops(NULL
, "add_mime_type: invalid mime_type");
1673 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1674 mime_type
[strlen(mime_type
) - 1] = '\0';
1679 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1680 show_oops(NULL
, "add_mime_type: invalid mime_type");
1684 m
->mt_type
= g_strdup(mime_type
);
1685 m
->mt_action
= g_strdup(l
);
1686 m
->mt_download
= downloadfirst
;
1688 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1689 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1691 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1701 find_mime_type(char *mime_type
)
1703 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1705 TAILQ_FOREACH(m
, &mtl
, entry
) {
1706 if (m
->mt_default
&&
1707 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1710 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1723 walk_mime_type(struct settings
*s
,
1724 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1726 struct mime_type
*m
;
1729 if (s
== NULL
|| cb
== NULL
) {
1730 show_oops(NULL
, "walk_mime_type invalid parameters");
1734 TAILQ_FOREACH(m
, &mtl
, entry
) {
1735 str
= g_strdup_printf("%s%s --> %s",
1737 m
->mt_default
? "*" : "",
1739 cb(s
, str
, cb_args
);
1745 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1750 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1753 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1755 /* treat *.moo.com the same as .moo.com */
1756 if (str
[0] == '*' && str
[1] == '.')
1758 else if (str
[0] == '.')
1763 d
= g_malloc(sizeof *d
);
1765 d
->d
= g_strdup_printf(".%s", str
);
1767 d
->d
= g_strdup(str
);
1770 if (RB_INSERT(domain_list
, wl
, d
))
1773 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1784 add_cookie_wl(struct settings
*s
, char *entry
)
1786 wl_add(entry
, &c_wl
, 1);
1791 walk_cookie_wl(struct settings
*s
,
1792 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1796 if (s
== NULL
|| cb
== NULL
) {
1797 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1801 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1802 cb(s
, d
->d
, cb_args
);
1806 walk_js_wl(struct settings
*s
,
1807 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1811 if (s
== NULL
|| cb
== NULL
) {
1812 show_oops(NULL
, "walk_js_wl invalid parameters");
1816 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1817 cb(s
, d
->d
, cb_args
);
1821 add_js_wl(struct settings
*s
, char *entry
)
1823 wl_add(entry
, &js_wl
, 1 /* persistent */);
1828 wl_find(const gchar
*search
, struct domain_list
*wl
)
1831 struct domain
*d
= NULL
, dfind
;
1834 if (search
== NULL
|| wl
== NULL
)
1836 if (strlen(search
) < 2)
1839 if (search
[0] != '.')
1840 s
= g_strdup_printf(".%s", search
);
1842 s
= g_strdup(search
);
1844 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1847 d
= RB_FIND(domain_list
, wl
, &dfind
);
1861 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1867 if (s
== NULL
|| wl
== NULL
)
1870 if (!strncmp(s
, "http://", strlen("http://")))
1871 s
= &s
[strlen("http://")];
1872 else if (!strncmp(s
, "https://", strlen("https://")))
1873 s
= &s
[strlen("https://")];
1878 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1879 /* chop string at first slash */
1880 if (s
[i
] == '/' || s
[i
] == '\0') {
1883 r
= wl_find(ss
, wl
);
1892 get_toplevel_domain(char *domain
)
1899 if (strlen(domain
) < 2)
1902 s
= &domain
[strlen(domain
) - 1];
1903 while (s
!= domain
) {
1919 settings_add(char *var
, char *val
)
1926 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1927 if (strcmp(var
, rs
[i
].name
))
1931 if (rs
[i
].s
->set(&rs
[i
], val
))
1932 errx(1, "invalid value for %s: %s", var
, val
);
1936 switch (rs
[i
].type
) {
1945 errx(1, "invalid sval for %s",
1959 errx(1, "invalid type for %s", var
);
1968 config_parse(char *filename
, int runtime
)
1971 char *line
, *cp
, *var
, *val
;
1972 size_t len
, lineno
= 0;
1974 char file
[PATH_MAX
];
1977 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1979 if (filename
== NULL
)
1982 if (runtime
&& runtime_settings
[0] != '\0') {
1983 snprintf(file
, sizeof file
, "%s/%s",
1984 work_dir
, runtime_settings
);
1985 if (stat(file
, &sb
)) {
1986 warnx("runtime file doesn't exist, creating it");
1987 if ((f
= fopen(file
, "w")) == NULL
)
1989 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1993 strlcpy(file
, filename
, sizeof file
);
1995 if ((config
= fopen(file
, "r")) == NULL
) {
1996 warn("config_parse: cannot open %s", filename
);
2001 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2002 if (feof(config
) || ferror(config
))
2006 cp
+= (long)strspn(cp
, WS
);
2007 if (cp
[0] == '\0') {
2013 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2014 errx(1, "invalid config file entry: %s", line
);
2016 cp
+= (long)strspn(cp
, WS
);
2018 if ((val
= strsep(&cp
, "\0")) == NULL
)
2021 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2022 handled
= settings_add(var
, val
);
2024 errx(1, "invalid conf file entry: %s=%s", var
, val
);
2033 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2039 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2043 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2046 JSStringGetUTF8CString(jsref
, s
, l
);
2047 JSStringRelease(jsref
);
2053 disable_hints(struct tab
*t
)
2055 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2056 bzero(t
->hint_num
, sizeof t
->hint_num
);
2057 run_script(t
, "vimprobable_clear()");
2059 t
->hint_mode
= XT_HINT_NONE
;
2063 enable_hints(struct tab
*t
)
2065 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2066 run_script(t
, "vimprobable_show_hints()");
2068 t
->hint_mode
= XT_HINT_NONE
;
2071 #define XT_JS_OPEN ("open;")
2072 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2073 #define XT_JS_FIRE ("fire;")
2074 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2075 #define XT_JS_FOUND ("found;")
2076 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2079 run_script(struct tab
*t
, char *s
)
2081 JSGlobalContextRef ctx
;
2082 WebKitWebFrame
*frame
;
2084 JSValueRef val
, exception
;
2087 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2088 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2090 frame
= webkit_web_view_get_main_frame(t
->wv
);
2091 ctx
= webkit_web_frame_get_global_context(frame
);
2093 str
= JSStringCreateWithUTF8CString(s
);
2094 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2095 NULL
, 0, &exception
);
2096 JSStringRelease(str
);
2098 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2100 es
= js_ref_to_string(ctx
, exception
);
2101 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2105 es
= js_ref_to_string(ctx
, val
);
2106 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2108 /* handle return value right here */
2109 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2111 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
2114 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2115 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2116 &es
[XT_JS_FIRE_LEN
]);
2121 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2122 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2133 hint(struct tab
*t
, struct karg
*args
)
2136 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2138 if (t
->hints_on
== 0)
2147 apply_style(struct tab
*t
)
2149 g_object_set(G_OBJECT(t
->settings
),
2150 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2154 userstyle(struct tab
*t
, struct karg
*args
)
2156 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2160 g_object_set(G_OBJECT(t
->settings
),
2161 "user-stylesheet-uri", NULL
, (char *)NULL
);
2170 * Doesn't work fully, due to the following bug:
2171 * https://bugs.webkit.org/show_bug.cgi?id=51747
2174 restore_global_history(void)
2176 char file
[PATH_MAX
];
2181 const char delim
[3] = {'\\', '\\', '\0'};
2183 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2185 if ((f
= fopen(file
, "r")) == NULL
) {
2186 warnx("%s: fopen", __func__
);
2191 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2192 if (feof(f
) || ferror(f
))
2195 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2196 if (feof(f
) || ferror(f
)) {
2198 warnx("%s: broken history file\n", __func__
);
2202 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2203 webkit_web_history_item_new_with_data(uri
, title
);
2204 h
= g_malloc(sizeof(struct history
));
2205 h
->uri
= g_strdup(uri
);
2206 h
->title
= g_strdup(title
);
2207 RB_INSERT(history_list
, &hl
, h
);
2208 completion_add_uri(h
->uri
);
2210 warnx("%s: failed to restore history\n", __func__
);
2226 save_global_history_to_disk(struct tab
*t
)
2228 char file
[PATH_MAX
];
2232 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2234 if ((f
= fopen(file
, "w")) == NULL
) {
2235 show_oops(t
, "%s: global history file: %s",
2236 __func__
, strerror(errno
));
2240 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2241 if (h
->uri
&& h
->title
)
2242 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2251 quit(struct tab
*t
, struct karg
*args
)
2253 if (save_global_history
)
2254 save_global_history_to_disk(t
);
2262 open_tabs(struct tab
*t
, struct karg
*a
)
2264 char file
[PATH_MAX
];
2268 struct tab
*ti
, *tt
;
2273 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2274 if ((f
= fopen(file
, "r")) == NULL
)
2277 ti
= TAILQ_LAST(&tabs
, tab_list
);
2280 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2281 if (feof(f
) || ferror(f
))
2284 /* retrieve session name */
2285 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2286 strlcpy(named_session
,
2287 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2288 sizeof named_session
);
2292 if (uri
&& strlen(uri
))
2293 create_new_tab(uri
, NULL
, 1, -1);
2299 /* close open tabs */
2300 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2302 tt
= TAILQ_FIRST(&tabs
);
2322 restore_saved_tabs(void)
2324 char file
[PATH_MAX
];
2325 int unlink_file
= 0;
2330 snprintf(file
, sizeof file
, "%s/%s",
2331 sessions_dir
, XT_RESTART_TABS_FILE
);
2332 if (stat(file
, &sb
) == -1)
2333 a
.s
= XT_SAVED_TABS_FILE
;
2336 a
.s
= XT_RESTART_TABS_FILE
;
2339 a
.i
= XT_SES_DONOTHING
;
2340 rv
= open_tabs(NULL
, &a
);
2349 save_tabs(struct tab
*t
, struct karg
*a
)
2351 char file
[PATH_MAX
];
2353 int num_tabs
= 0, i
;
2354 struct tab
**stabs
= NULL
;
2359 snprintf(file
, sizeof file
, "%s/%s",
2360 sessions_dir
, named_session
);
2362 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2364 if ((f
= fopen(file
, "w")) == NULL
) {
2365 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2369 /* save session name */
2370 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2372 /* Save tabs, in the order they are arranged in the notebook. */
2373 num_tabs
= sort_tabs_by_page_num(&stabs
);
2375 for (i
= 0; i
< num_tabs
; i
++)
2376 if (stabs
[i
] && get_uri(stabs
[i
]) != NULL
)
2377 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2381 /* try and make sure this gets to disk NOW. XXX Backup first? */
2382 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2383 show_oops(t
, "May not have managed to save session: %s",
2393 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2405 yank_uri(struct tab
*t
, struct karg
*args
)
2408 GtkClipboard
*clipboard
;
2410 if ((uri
= get_uri(t
)) == NULL
)
2413 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2414 gtk_clipboard_set_text(clipboard
, uri
, -1);
2420 paste_uri(struct tab
*t
, struct karg
*args
)
2422 GtkClipboard
*clipboard
;
2423 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2425 gchar
*p
= NULL
, *uri
;
2427 /* try primary clipboard first */
2428 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2429 p
= gtk_clipboard_wait_for_text(clipboard
);
2431 /* if it failed get whatever text is in cut_buffer0 */
2433 if (gdk_property_get(gdk_get_default_root_window(),
2435 gdk_atom_intern("STRING", FALSE
),
2437 65536 /* picked out of my butt */,
2443 /* yes sir, we need to NUL the string */
2449 while (*uri
&& isspace(*uri
))
2451 if (strlen(uri
) == 0) {
2452 show_oops(t
, "empty paste buffer");
2455 if (guess_search
== 0 && valid_url_type(uri
)) {
2456 /* we can be clever and paste this in search box */
2457 show_oops(t
, "not a valid URL");
2461 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2463 else if (args
->i
== XT_PASTE_NEW_TAB
)
2464 create_new_tab(uri
, NULL
, 1, -1);
2475 find_domain(const gchar
*s
, int add_dot
)
2478 char *r
= NULL
, *ss
= NULL
;
2483 if (!strncmp(s
, "http://", strlen("http://")))
2484 s
= &s
[strlen("http://")];
2485 else if (!strncmp(s
, "https://", strlen("https://")))
2486 s
= &s
[strlen("https://")];
2492 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2493 /* chop string at first slash */
2494 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2497 r
= g_strdup_printf(".%s", ss
);
2508 toggle_cwl(struct tab
*t
, struct karg
*args
)
2512 char *dom
= NULL
, *dom_toggle
= NULL
;
2519 dom
= find_domain(uri
, 1);
2520 d
= wl_find(dom
, &c_wl
);
2527 if (args
->i
& XT_WL_TOGGLE
)
2529 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2531 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2534 if (args
->i
& XT_WL_TOPLEVEL
)
2535 dom_toggle
= get_toplevel_domain(dom
);
2540 /* enable cookies for domain */
2541 wl_add(dom_toggle
, &c_wl
, 0);
2543 /* disable cookies for domain */
2544 RB_REMOVE(domain_list
, &c_wl
, d
);
2546 if (args
->i
& XT_WL_RELOAD
)
2547 webkit_web_view_reload(t
->wv
);
2554 toggle_js(struct tab
*t
, struct karg
*args
)
2559 char *dom
= NULL
, *dom_toggle
= NULL
;
2564 g_object_get(G_OBJECT(t
->settings
),
2565 "enable-scripts", &es
, (char *)NULL
);
2566 if (args
->i
& XT_WL_TOGGLE
)
2568 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2570 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2576 dom
= find_domain(uri
, 1);
2578 if (uri
== NULL
|| dom
== NULL
) {
2579 show_oops(t
, "Can't toggle domain in JavaScript white list");
2583 if (args
->i
& XT_WL_TOPLEVEL
)
2584 dom_toggle
= get_toplevel_domain(dom
);
2589 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2590 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2592 d
= wl_find(dom_toggle
, &js_wl
);
2594 RB_REMOVE(domain_list
, &js_wl
, d
);
2595 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2597 g_object_set(G_OBJECT(t
->settings
),
2598 "enable-scripts", es
, (char *)NULL
);
2599 g_object_set(G_OBJECT(t
->settings
),
2600 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2601 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2603 if (args
->i
& XT_WL_RELOAD
)
2604 webkit_web_view_reload(t
->wv
);
2612 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2616 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2619 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2624 toggle_src(struct tab
*t
, struct karg
*args
)
2631 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2632 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2633 webkit_web_view_reload(t
->wv
);
2639 focus_webview(struct tab
*t
)
2644 /* only grab focus if we are visible */
2645 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2646 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2650 focus(struct tab
*t
, struct karg
*args
)
2652 if (t
== NULL
|| args
== NULL
)
2658 if (args
->i
== XT_FOCUS_URI
)
2659 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2660 else if (args
->i
== XT_FOCUS_SEARCH
)
2661 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2667 stats(struct tab
*t
, struct karg
*args
)
2669 char *page
, *body
, *s
, line
[64 * 1024];
2670 uint64_t line_count
= 0;
2674 show_oops(NULL
, "stats invalid parameters");
2677 if (save_rejected_cookies
) {
2678 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2680 s
= fgets(line
, sizeof line
, r_cookie_f
);
2681 if (s
== NULL
|| feof(r_cookie_f
) ||
2687 snprintf(line
, sizeof line
,
2688 "<br/>Cookies blocked(*) total: %llu", line_count
);
2690 show_oops(t
, "Can't open blocked cookies file: %s",
2694 body
= g_strdup_printf(
2695 "Cookies blocked(*) this session: %llu"
2697 "<p><small><b>*</b> results vary based on settings</small></p>",
2701 page
= get_html_page("Statistics", body
, "", 0);
2704 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2711 marco(struct tab
*t
, struct karg
*args
)
2713 char *page
, line
[64 * 1024];
2717 show_oops(NULL
, "marco invalid parameters");
2720 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2722 page
= get_html_page("Marco Sez...", line
, "", 0);
2724 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2731 blank(struct tab
*t
, struct karg
*args
)
2734 show_oops(NULL
, "blank invalid parameters");
2736 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2741 about(struct tab
*t
, struct karg
*args
)
2746 show_oops(NULL
, "about invalid parameters");
2748 body
= g_strdup_printf("<b>Version: %s</b><p>"
2751 "<li>Marco Peereboom <marco@peereboom.us></li>"
2752 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2753 "<li>Edd Barrett <vext01@gmail.com> </li>"
2754 "<li>Todd T. Fries <todd@fries.net> </li>"
2755 "<li>Raphael Graf <r@undefined.ch> </li>"
2757 "Copyrights and licenses can be found on the XXXTerm "
2758 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2762 page
= get_html_page("About", body
, "", 0);
2765 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2772 help(struct tab
*t
, struct karg
*args
)
2774 char *page
, *head
, *body
;
2777 show_oops(NULL
, "help invalid parameters");
2779 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2780 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2782 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
2783 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2784 "cgi-bin/man-cgi?xxxterm</a>";
2786 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
2788 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2795 * update all favorite tabs apart from one. Pass NULL if
2796 * you want to update all.
2799 update_favorite_tabs(struct tab
*apart_from
)
2802 if (!updating_fl_tabs
) {
2803 updating_fl_tabs
= 1; /* stop infinite recursion */
2804 TAILQ_FOREACH(t
, &tabs
, entry
)
2805 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2806 && (t
!= apart_from
))
2807 xtp_page_fl(t
, NULL
);
2808 updating_fl_tabs
= 0;
2812 /* show a list of favorites (bookmarks) */
2814 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2816 char file
[PATH_MAX
];
2818 char *uri
= NULL
, *title
= NULL
;
2819 size_t len
, lineno
= 0;
2821 char *body
, *tmp
, *page
= NULL
;
2822 const char delim
[3] = {'\\', '\\', '\0'};
2824 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2827 warn("%s: bad param", __func__
);
2829 /* new session key */
2830 if (!updating_fl_tabs
)
2831 generate_xtp_session_key(&fl_session_key
);
2833 /* open favorites */
2834 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2835 if ((f
= fopen(file
, "r")) == NULL
) {
2836 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2841 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2842 "<th style='width: 40px'>#</th><th>Link</th>"
2843 "<th style='width: 40px'>Rm</th></tr>\n");
2846 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2847 if (feof(f
) || ferror(f
))
2855 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2856 if (feof(f
) || ferror(f
)) {
2857 show_oops(t
, "favorites file corrupt");
2863 body
= g_strdup_printf("%s<tr>"
2865 "<td><a href='%s'>%s</a></td>"
2866 "<td style='text-align: center'>"
2867 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2869 body
, i
, uri
, title
,
2870 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2882 /* if none, say so */
2885 body
= g_strdup_printf("%s<tr>"
2886 "<td colspan='3' style='text-align: center'>"
2887 "No favorites - To add one use the 'favadd' command."
2888 "</td></tr>", body
);
2893 body
= g_strdup_printf("%s</table>", body
);
2903 page
= get_html_page("Favorites", body
, "", 1);
2904 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
2908 update_favorite_tabs(t
);
2917 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2918 size_t cert_count
, char *title
)
2920 gnutls_datum_t cinfo
;
2924 body
= g_strdup("");
2926 for (i
= 0; i
< cert_count
; i
++) {
2927 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2932 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2933 body
, i
, cinfo
.data
);
2934 gnutls_free(cinfo
.data
);
2938 tmp
= get_html_page(title
, body
, "", 0);
2941 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2946 ca_cmd(struct tab
*t
, struct karg
*args
)
2949 int rv
= 1, certs
= 0, certs_read
;
2952 gnutls_x509_crt_t
*c
= NULL
;
2953 char *certs_buf
= NULL
, *s
;
2955 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2956 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
2960 if (fstat(fileno(f
), &sb
) == -1) {
2961 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
2965 certs_buf
= g_malloc(sb
.st_size
+ 1);
2966 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2967 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2970 certs_buf
[sb
.st_size
] = '\0';
2973 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2975 s
+= strlen("BEGIN CERTIFICATE");
2978 bzero(&dt
, sizeof dt
);
2979 dt
.data
= (unsigned char *)certs_buf
;
2980 dt
.size
= sb
.st_size
;
2981 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2982 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
2983 GNUTLS_X509_FMT_PEM
, 0);
2984 if (certs_read
<= 0) {
2985 show_oops(t
, "No cert(s) available");
2988 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3001 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
3004 struct addrinfo hints
, *res
= NULL
, *ai
;
3008 if (uri
&& !g_str_has_prefix(uri
, "https://"))
3011 su
= soup_uri_new(uri
);
3014 if (!SOUP_URI_VALID_FOR_HTTP(su
))
3017 snprintf(port
, sizeof port
, "%d", su
->port
);
3018 bzero(&hints
, sizeof(struct addrinfo
));
3019 hints
.ai_flags
= AI_CANONNAME
;
3020 hints
.ai_family
= AF_UNSPEC
;
3021 hints
.ai_socktype
= SOCK_STREAM
;
3023 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
3026 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3027 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3030 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3033 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3037 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
3042 strlcpy(domain
, su
->host
, domain_sz
);
3053 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3056 gnutls_deinit(gsession
);
3058 gnutls_certificate_free_credentials(xcred
);
3064 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3065 gnutls_certificate_credentials_t
*xc
)
3067 gnutls_certificate_credentials_t xcred
;
3068 gnutls_session_t gsession
;
3071 if (gs
== NULL
|| xc
== NULL
)
3077 gnutls_certificate_allocate_credentials(&xcred
);
3078 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3079 GNUTLS_X509_FMT_PEM
);
3080 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3081 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3082 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3083 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3084 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3085 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3087 gnutls_error_is_fatal(rv
),
3088 gnutls_strerror_name(rv
));
3089 stop_tls(gsession
, xcred
);
3093 gnutls_credentials_type_t cred
;
3094 cred
= gnutls_auth_get_type(gsession
);
3095 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3096 stop_tls(gsession
, xcred
);
3108 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3112 const gnutls_datum_t
*cl
;
3113 gnutls_x509_crt_t
*all_certs
;
3116 if (certs
== NULL
|| cert_count
== NULL
)
3118 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3120 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3124 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3125 for (i
= 0; i
< len
; i
++) {
3126 gnutls_x509_crt_init(&all_certs
[i
]);
3127 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3128 GNUTLS_X509_FMT_PEM
< 0)) {
3142 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3146 for (i
= 0; i
< cert_count
; i
++)
3147 gnutls_x509_crt_deinit(certs
[i
]);
3152 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3153 size_t cert_count
, char *domain
)
3156 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3161 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3164 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3165 if ((f
= fopen(file
, "w")) == NULL
) {
3166 show_oops(t
, "Can't create cert file %s %s",
3167 file
, strerror(errno
));
3171 for (i
= 0; i
< cert_count
; i
++) {
3172 cert_buf_sz
= sizeof cert_buf
;
3173 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3174 cert_buf
, &cert_buf_sz
)) {
3175 show_oops(t
, "gnutls_x509_crt_export failed");
3178 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3179 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3184 /* not the best spot but oh well */
3185 gdk_color_parse("lightblue", &color
);
3186 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3187 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
3188 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
3189 gdk_color_parse(XT_COLOR_BLACK
, &color
);
3190 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
3191 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
3197 load_compare_cert(struct tab
*t
, struct karg
*args
)
3200 char domain
[8182], file
[PATH_MAX
];
3201 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3202 int s
= -1, rv
= 1, i
;
3206 gnutls_session_t gsession
;
3207 gnutls_x509_crt_t
*certs
;
3208 gnutls_certificate_credentials_t xcred
;
3213 if ((uri
= get_uri(t
)) == NULL
)
3216 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3220 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3221 show_oops(t
, "Start TLS failed");
3226 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3227 show_oops(t
, "Can't get connection certificates");
3231 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3232 if ((f
= fopen(file
, "r")) == NULL
)
3235 for (i
= 0; i
< cert_count
; i
++) {
3236 cert_buf_sz
= sizeof cert_buf
;
3237 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3238 cert_buf
, &cert_buf_sz
)) {
3241 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3242 rv
= -1; /* critical */
3245 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3246 rv
= -1; /* critical */
3255 free_connection_certs(certs
, cert_count
);
3257 /* we close the socket first for speed */
3260 stop_tls(gsession
, xcred
);
3266 cert_cmd(struct tab
*t
, struct karg
*args
)
3272 gnutls_session_t gsession
;
3273 gnutls_x509_crt_t
*certs
;
3274 gnutls_certificate_credentials_t xcred
;
3279 if (ssl_ca_file
== NULL
) {
3280 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3284 if ((uri
= get_uri(t
)) == NULL
) {
3285 show_oops(t
, "Invalid URI");
3289 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3290 show_oops(t
, "Invalid certificate URI: %s", uri
);
3295 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3296 show_oops(t
, "Start TLS failed");
3301 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3302 show_oops(t
, "get_connection_certs failed");
3306 if (args
->i
& XT_SHOW
)
3307 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3308 else if (args
->i
& XT_SAVE
)
3309 save_certs(t
, certs
, cert_count
, domain
);
3311 free_connection_certs(certs
, cert_count
);
3313 /* we close the socket first for speed */
3316 stop_tls(gsession
, xcred
);
3322 remove_cookie(int index
)
3328 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3330 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3332 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3336 print_cookie("remove cookie", c
);
3337 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3342 soup_cookies_free(cf
);
3348 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3353 body
= g_strdup("");
3356 if (args
->i
& XT_WL_PERSISTENT
) {
3358 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3360 RB_FOREACH(d
, domain_list
, wl
) {
3364 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3370 if (args
->i
& XT_WL_SESSION
) {
3372 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3374 RB_FOREACH(d
, domain_list
, wl
) {
3378 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3383 tmp
= get_html_page(title
, body
, "", 0);
3386 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3388 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3394 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3396 char file
[PATH_MAX
];
3398 char *line
= NULL
, *lt
= NULL
;
3401 char *dom
= NULL
, *dom_save
= NULL
;
3407 if (t
== NULL
|| args
== NULL
)
3410 if (runtime_settings
[0] == '\0')
3413 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3414 if ((f
= fopen(file
, "r+")) == NULL
)
3418 dom
= find_domain(uri
, 1);
3419 if (uri
== NULL
|| dom
== NULL
) {
3420 show_oops(t
, "Can't add domain to %s white list",
3421 js
? "JavaScript" : "cookie");
3425 if (args
->i
& XT_WL_TOPLEVEL
) {
3427 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3428 show_oops(t
, "invalid domain: %s", dom
);
3431 } else if (args
->i
& XT_WL_FQDN
) {
3437 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3440 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3443 if (!strcmp(line
, lt
))
3449 fprintf(f
, "%s\n", lt
);
3454 d
= wl_find(dom_save
, &js_wl
);
3456 settings_add("js_wl", dom_save
);
3457 d
= wl_find(dom_save
, &js_wl
);
3461 d
= wl_find(dom_save
, &c_wl
);
3463 settings_add("cookie_wl", dom_save
);
3464 d
= wl_find(dom_save
, &c_wl
);
3468 /* find and add to persistent jar */
3469 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3470 for (;cf
; cf
= cf
->next
) {
3472 if (!strcmp(dom_save
, ci
->domain
) ||
3473 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3474 c
= soup_cookie_copy(ci
);
3475 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3478 soup_cookies_free(cf
);
3496 js_show_wl(struct tab
*t
, struct karg
*args
)
3498 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3499 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3505 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3507 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3508 wl_show(t
, args
, "Cookie White List", &c_wl
);
3514 cookie_cmd(struct tab
*t
, struct karg
*args
)
3516 if (args
->i
& XT_SHOW
)
3517 wl_show(t
, args
, "Cookie White List", &c_wl
);
3518 else if (args
->i
& XT_WL_TOGGLE
) {
3519 args
->i
|= XT_WL_RELOAD
;
3520 toggle_cwl(t
, args
);
3521 } else if (args
->i
& XT_SAVE
) {
3522 args
->i
|= XT_WL_RELOAD
;
3523 wl_save(t
, args
, 0);
3524 } else if (args
->i
& XT_DELETE
)
3525 show_oops(t
, "'cookie delete' currently unimplemented");
3531 js_cmd(struct tab
*t
, struct karg
*args
)
3533 if (args
->i
& XT_SHOW
)
3534 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3535 else if (args
->i
& XT_SAVE
) {
3536 args
->i
|= XT_WL_RELOAD
;
3537 wl_save(t
, args
, 1);
3538 } else if (args
->i
& XT_WL_TOGGLE
) {
3539 args
->i
|= XT_WL_RELOAD
;
3541 } else if (args
->i
& XT_DELETE
)
3542 show_oops(t
, "'js delete' currently unimplemented");
3548 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3550 js_toggle_cb(t
->js_toggle
, t
);
3556 add_favorite(struct tab
*t
, struct karg
*args
)
3558 char file
[PATH_MAX
];
3561 size_t urilen
, linelen
;
3562 const gchar
*uri
, *title
;
3567 /* don't allow adding of xtp pages to favorites */
3568 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3569 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3573 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3574 if ((f
= fopen(file
, "r+")) == NULL
) {
3575 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3579 title
= get_title(t
, FALSE
);
3582 if (title
== NULL
|| uri
== NULL
) {
3583 show_oops(t
, "can't add page to favorites");
3587 urilen
= strlen(uri
);
3590 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3591 if (feof(f
) || ferror(f
))
3594 if (linelen
== urilen
&& !strcmp(line
, uri
))
3601 fprintf(f
, "\n%s\n%s", title
, uri
);
3607 update_favorite_tabs(NULL
);
3613 navaction(struct tab
*t
, struct karg
*args
)
3615 WebKitWebHistoryItem
*item
;
3617 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3618 t
->tab_id
, args
->i
);
3621 if (args
->i
== XT_NAV_BACK
)
3622 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3624 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3626 return (XT_CB_PASSTHROUGH
);
3627 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3629 return (XT_CB_PASSTHROUGH
);
3634 webkit_web_view_go_back(t
->wv
);
3636 case XT_NAV_FORWARD
:
3637 webkit_web_view_go_forward(t
->wv
);
3640 webkit_web_view_reload(t
->wv
);
3642 case XT_NAV_RELOAD_CACHE
:
3643 webkit_web_view_reload_bypass_cache(t
->wv
);
3646 return (XT_CB_PASSTHROUGH
);
3650 move(struct tab
*t
, struct karg
*args
)
3652 GtkAdjustment
*adjust
;
3653 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3658 case XT_MOVE_BOTTOM
:
3660 case XT_MOVE_PAGEDOWN
:
3661 case XT_MOVE_PAGEUP
:
3662 case XT_MOVE_HALFDOWN
:
3663 case XT_MOVE_HALFUP
:
3664 adjust
= t
->adjust_v
;
3667 adjust
= t
->adjust_h
;
3671 pos
= gtk_adjustment_get_value(adjust
);
3672 ps
= gtk_adjustment_get_page_size(adjust
);
3673 upper
= gtk_adjustment_get_upper(adjust
);
3674 lower
= gtk_adjustment_get_lower(adjust
);
3675 si
= gtk_adjustment_get_step_increment(adjust
);
3676 pi
= gtk_adjustment_get_page_increment(adjust
);
3679 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3680 "max %f si %f pi %f\n",
3681 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3682 pos
, ps
, upper
, lower
, max
, si
, pi
);
3688 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3693 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3695 case XT_MOVE_BOTTOM
:
3696 case XT_MOVE_FARRIGHT
:
3697 gtk_adjustment_set_value(adjust
, max
);
3700 case XT_MOVE_FARLEFT
:
3701 gtk_adjustment_set_value(adjust
, lower
);
3703 case XT_MOVE_PAGEDOWN
:
3705 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3707 case XT_MOVE_PAGEUP
:
3709 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3711 case XT_MOVE_HALFDOWN
:
3713 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3715 case XT_MOVE_HALFUP
:
3717 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3720 return (XT_CB_PASSTHROUGH
);
3723 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3725 return (XT_CB_HANDLED
);
3729 url_set_visibility(void)
3733 TAILQ_FOREACH(t
, &tabs
, entry
)
3734 if (show_url
== 0) {
3735 gtk_widget_hide(t
->toolbar
);
3738 gtk_widget_show(t
->toolbar
);
3742 notebook_tab_set_visibility()
3744 if (show_tabs
== 0) {
3745 gtk_widget_hide(tab_bar
);
3746 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3748 if (tab_style
== XT_TABS_NORMAL
) {
3749 gtk_widget_hide(tab_bar
);
3750 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3751 } else if (tab_style
== XT_TABS_COMPACT
) {
3752 gtk_widget_show(tab_bar
);
3753 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3759 statusbar_set_visibility(void)
3763 TAILQ_FOREACH(t
, &tabs
, entry
)
3764 if (show_statusbar
== 0) {
3765 gtk_widget_hide(t
->statusbar_box
);
3768 gtk_widget_show(t
->statusbar_box
);
3772 url_set(struct tab
*t
, int enable_url_entry
)
3777 show_url
= enable_url_entry
;
3779 if (enable_url_entry
) {
3780 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3781 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3782 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
3784 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3785 GTK_ENTRY_ICON_PRIMARY
);
3787 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3788 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
3789 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3790 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
3796 fullscreen(struct tab
*t
, struct karg
*args
)
3798 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3801 return (XT_CB_PASSTHROUGH
);
3803 if (show_url
== 0) {
3811 url_set_visibility();
3812 notebook_tab_set_visibility();
3814 return (XT_CB_HANDLED
);
3818 statustoggle(struct tab
*t
, struct karg
*args
)
3820 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3822 if (show_statusbar
== 1) {
3824 statusbar_set_visibility();
3825 } else if (show_statusbar
== 0) {
3827 statusbar_set_visibility();
3829 return (XT_CB_HANDLED
);
3833 urlaction(struct tab
*t
, struct karg
*args
)
3835 int rv
= XT_CB_HANDLED
;
3837 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3840 return (XT_CB_PASSTHROUGH
);
3844 if (show_url
== 0) {
3846 url_set_visibility();
3850 if (show_url
== 1) {
3852 url_set_visibility();
3860 tabaction(struct tab
*t
, struct karg
*args
)
3862 int rv
= XT_CB_HANDLED
;
3863 char *url
= args
->s
;
3867 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3870 return (XT_CB_PASSTHROUGH
);
3874 if (strlen(url
) > 0)
3875 create_new_tab(url
, NULL
, 1, args
->p
);
3877 create_new_tab(NULL
, NULL
, 1, args
->p
);
3883 TAILQ_FOREACH(tt
, &tabs
, entry
)
3884 if (tt
->tab_id
== args
->p
- 1) {
3889 case XT_TAB_DELQUIT
:
3890 if (gtk_notebook_get_n_pages(notebook
) > 1)
3896 if (strlen(url
) > 0)
3899 rv
= XT_CB_PASSTHROUGH
;
3905 if (show_tabs
== 0) {
3907 notebook_tab_set_visibility();
3911 if (show_tabs
== 1) {
3913 notebook_tab_set_visibility();
3916 case XT_TAB_NEXTSTYLE
:
3917 if (tab_style
== XT_TABS_NORMAL
) {
3918 tab_style
= XT_TABS_COMPACT
;
3919 recolor_compact_tabs();
3922 tab_style
= XT_TABS_NORMAL
;
3923 notebook_tab_set_visibility();
3925 case XT_TAB_UNDO_CLOSE
:
3926 if (undo_count
== 0) {
3927 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3931 u
= TAILQ_FIRST(&undos
);
3932 create_new_tab(u
->uri
, u
, 1, -1);
3934 TAILQ_REMOVE(&undos
, u
, entry
);
3936 /* u->history is freed in create_new_tab() */
3941 rv
= XT_CB_PASSTHROUGH
;
3955 resizetab(struct tab
*t
, struct karg
*args
)
3957 if (t
== NULL
|| args
== NULL
) {
3958 show_oops(NULL
, "resizetab invalid parameters");
3959 return (XT_CB_PASSTHROUGH
);
3962 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3963 t
->tab_id
, args
->i
);
3965 adjustfont_webkit(t
, args
->i
);
3967 return (XT_CB_HANDLED
);
3971 movetab(struct tab
*t
, struct karg
*args
)
3975 if (t
== NULL
|| args
== NULL
) {
3976 show_oops(NULL
, "movetab invalid parameters");
3977 return (XT_CB_PASSTHROUGH
);
3980 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3981 t
->tab_id
, args
->i
);
3983 if (args
->i
>= XT_TAB_INVALID
)
3984 return (XT_CB_PASSTHROUGH
);
3986 if (TAILQ_EMPTY(&tabs
))
3987 return (XT_CB_PASSTHROUGH
);
3989 n
= gtk_notebook_get_n_pages(notebook
);
3990 dest
= gtk_notebook_get_current_page(notebook
);
3995 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4004 dest
-= args
->p
% n
;
4017 return (XT_CB_PASSTHROUGH
);
4020 if (dest
< 0 || dest
>= n
)
4021 return (XT_CB_PASSTHROUGH
);
4022 if (t
->tab_id
== dest
) {
4023 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4024 return (XT_CB_HANDLED
);
4027 set_current_tab(dest
);
4029 return (XT_CB_HANDLED
);
4035 command(struct tab
*t
, struct karg
*args
)
4037 char *s
= NULL
, *ss
= NULL
;
4041 if (t
== NULL
|| args
== NULL
) {
4042 show_oops(NULL
, "command invalid parameters");
4043 return (XT_CB_PASSTHROUGH
);
4054 if (cmd_prefix
== 0)
4057 ss
= g_strdup_printf(":%d", cmd_prefix
);
4068 case XT_CMD_OPEN_CURRENT
:
4071 case XT_CMD_TABNEW_CURRENT
:
4072 if (!s
) /* FALL THROUGH? */
4074 if ((uri
= get_uri(t
)) != NULL
) {
4075 ss
= g_strdup_printf("%s%s", s
, uri
);
4080 show_oops(t
, "command: invalid opcode %d", args
->i
);
4081 return (XT_CB_PASSTHROUGH
);
4084 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4086 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4087 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4088 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4090 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4091 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4096 return (XT_CB_HANDLED
);
4100 * Return a new string with a download row (in html)
4101 * appended. Old string is freed.
4104 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4107 WebKitDownloadStatus stat
;
4108 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4110 char cur_sz
[FMT_SCALED_STRSIZE
];
4111 char tot_sz
[FMT_SCALED_STRSIZE
];
4114 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4116 /* All actions wil take this form:
4117 * xxxt://class/seskey
4119 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4120 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4122 stat
= webkit_download_get_status(dl
->download
);
4125 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4126 status_html
= g_strdup_printf("Finished");
4127 cmd_html
= g_strdup_printf(
4128 "<a href='%s%d/%d'>Remove</a>",
4129 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4131 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4132 /* gather size info */
4133 progress
= 100 * webkit_download_get_progress(dl
->download
);
4136 webkit_download_get_current_size(dl
->download
), cur_sz
);
4138 webkit_download_get_total_size(dl
->download
), tot_sz
);
4140 status_html
= g_strdup_printf(
4141 "<div style='width: 100%%' align='center'>"
4142 "<div class='progress-outer'>"
4143 "<div class='progress-inner' style='width: %.2f%%'>"
4144 "</div></div></div>"
4145 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4146 progress
, cur_sz
, tot_sz
, progress
);
4148 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4149 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4153 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4154 status_html
= g_strdup_printf("Cancelled");
4155 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4156 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4158 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4159 status_html
= g_strdup_printf("Error!");
4160 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4161 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4163 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4164 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4165 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4166 status_html
= g_strdup_printf("Starting");
4169 show_oops(t
, "%s: unknown download status", __func__
);
4172 new_html
= g_strdup_printf(
4173 "%s\n<tr><td>%s</td><td>%s</td>"
4174 "<td style='text-align:center'>%s</td></tr>\n",
4175 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4176 status_html
, cmd_html
);
4180 g_free(status_html
);
4191 * update all download tabs apart from one. Pass NULL if
4192 * you want to update all.
4195 update_download_tabs(struct tab
*apart_from
)
4198 if (!updating_dl_tabs
) {
4199 updating_dl_tabs
= 1; /* stop infinite recursion */
4200 TAILQ_FOREACH(t
, &tabs
, entry
)
4201 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4202 && (t
!= apart_from
))
4203 xtp_page_dl(t
, NULL
);
4204 updating_dl_tabs
= 0;
4209 * update all cookie tabs apart from one. Pass NULL if
4210 * you want to update all.
4213 update_cookie_tabs(struct tab
*apart_from
)
4216 if (!updating_cl_tabs
) {
4217 updating_cl_tabs
= 1; /* stop infinite recursion */
4218 TAILQ_FOREACH(t
, &tabs
, entry
)
4219 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4220 && (t
!= apart_from
))
4221 xtp_page_cl(t
, NULL
);
4222 updating_cl_tabs
= 0;
4227 * update all history tabs apart from one. Pass NULL if
4228 * you want to update all.
4231 update_history_tabs(struct tab
*apart_from
)
4235 if (!updating_hl_tabs
) {
4236 updating_hl_tabs
= 1; /* stop infinite recursion */
4237 TAILQ_FOREACH(t
, &tabs
, entry
)
4238 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4239 && (t
!= apart_from
))
4240 xtp_page_hl(t
, NULL
);
4241 updating_hl_tabs
= 0;
4245 /* cookie management XTP page */
4247 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4249 char *body
, *page
, *tmp
;
4250 int i
= 1; /* all ids start 1 */
4251 GSList
*sc
, *pc
, *pc_start
;
4253 char *type
, *table_headers
, *last_domain
;
4255 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4258 show_oops(NULL
, "%s invalid parameters", __func__
);
4262 /* Generate a new session key */
4263 if (!updating_cl_tabs
)
4264 generate_xtp_session_key(&cl_session_key
);
4267 table_headers
= g_strdup_printf("<table><tr>"
4270 "<th style='width:200px'>Value</th>"
4274 "<th>HTTP<br />only</th>"
4275 "<th style='width:40px'>Rm</th></tr>\n");
4277 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4278 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4282 last_domain
= strdup("");
4283 for (; sc
; sc
= sc
->next
) {
4286 if (strcmp(last_domain
, c
->domain
) != 0) {
4289 last_domain
= strdup(c
->domain
);
4293 body
= g_strdup_printf("%s</table>"
4295 body
, c
->domain
, table_headers
);
4299 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4300 c
->domain
, table_headers
);
4305 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4306 if (soup_cookie_equal(pc
->data
, c
)) {
4307 type
= "Session + Persistent";
4312 body
= g_strdup_printf(
4315 "<td style='word-wrap:normal'>%s</td>"
4317 " <textarea rows='4'>%s</textarea>"
4323 "<td style='text-align:center'>"
4324 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4331 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4346 soup_cookies_free(sc
);
4347 soup_cookies_free(pc
);
4349 /* small message if there are none */
4351 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4352 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4355 body
= g_strdup_printf("%s</table>", body
);
4358 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4360 g_free(table_headers
);
4361 g_free(last_domain
);
4363 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4364 update_cookie_tabs(t
);
4372 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4374 char *body
, *page
, *tmp
;
4376 int i
= 1; /* all ids start 1 */
4378 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4381 show_oops(NULL
, "%s invalid parameters", __func__
);
4385 /* Generate a new session key */
4386 if (!updating_hl_tabs
)
4387 generate_xtp_session_key(&hl_session_key
);
4390 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4391 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4393 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4395 body
= g_strdup_printf(
4397 "<td><a href='%s'>%s</a></td>"
4399 "<td style='text-align: center'>"
4400 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4401 body
, h
->uri
, h
->uri
, h
->title
,
4402 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4403 XT_XTP_HL_REMOVE
, i
);
4409 /* small message if there are none */
4412 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4413 "colspan='3'>No History</td></tr>\n", body
);
4418 body
= g_strdup_printf("%s</table>", body
);
4421 page
= get_html_page("History", body
, "", TRUE
);
4425 * update all history manager tabs as the xtp session
4426 * key has now changed. No need to update the current tab.
4427 * Already did that above.
4429 update_history_tabs(t
);
4431 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4438 * Generate a web page detailing the status of any downloads
4441 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4443 struct download
*dl
;
4444 char *body
, *page
, *tmp
;
4448 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4451 show_oops(NULL
, "%s invalid parameters", __func__
);
4456 * Generate a new session key for next page instance.
4457 * This only happens for the top level call to xtp_page_dl()
4458 * in which case updating_dl_tabs is 0.
4460 if (!updating_dl_tabs
)
4461 generate_xtp_session_key(&dl_session_key
);
4463 /* header - with refresh so as to update */
4464 if (refresh_interval
>= 1)
4465 ref
= g_strdup_printf(
4466 "<meta http-equiv='refresh' content='%u"
4467 ";url=%s%d/%s/%d' />\n",
4476 body
= g_strdup_printf("<div align='center'>"
4477 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4478 "</p><table><tr><th style='width: 60%%'>"
4479 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4480 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4482 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4483 body
= xtp_page_dl_row(t
, body
, dl
);
4487 /* message if no downloads in list */
4490 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4491 " style='text-align: center'>"
4492 "No downloads</td></tr>\n", body
);
4497 body
= g_strdup_printf("%s</table></div>", body
);
4500 page
= get_html_page("Downloads", body
, ref
, 1);
4505 * update all download manager tabs as the xtp session
4506 * key has now changed. No need to update the current tab.
4507 * Already did that above.
4509 update_download_tabs(t
);
4511 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4518 search(struct tab
*t
, struct karg
*args
)
4522 if (t
== NULL
|| args
== NULL
) {
4523 show_oops(NULL
, "search invalid parameters");
4526 if (t
->search_text
== NULL
) {
4527 if (global_search
== NULL
)
4528 return (XT_CB_PASSTHROUGH
);
4530 t
->search_text
= g_strdup(global_search
);
4531 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4532 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4536 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4537 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4540 case XT_SEARCH_NEXT
:
4541 d
= t
->search_forward
;
4543 case XT_SEARCH_PREV
:
4544 d
= !t
->search_forward
;
4547 return (XT_CB_PASSTHROUGH
);
4550 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4552 return (XT_CB_HANDLED
);
4555 struct settings_args
{
4561 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4564 struct settings_args
*sa
= cb_args
;
4569 if (s
->flags
& XT_SF_RUNTIME
)
4575 *sa
->body
= g_strdup_printf(
4577 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4578 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4590 set(struct tab
*t
, struct karg
*args
)
4592 char *body
, *page
, *tmp
;
4594 struct settings_args sa
;
4596 bzero(&sa
, sizeof sa
);
4600 body
= g_strdup_printf("<div align='center'><table><tr>"
4601 "<th align='left'>Setting</th>"
4602 "<th align='left'>Value</th></tr>\n");
4604 settings_walk(print_setting
, &sa
);
4607 /* small message if there are none */
4610 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4611 "colspan='2'>No settings</td></tr>\n", body
);
4616 body
= g_strdup_printf("%s</table></div>", body
);
4619 page
= get_html_page("Settings", body
, "", 0);
4623 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4627 return (XT_CB_PASSTHROUGH
);
4631 session_save(struct tab
*t
, char *filename
)
4636 if (strlen(filename
) == 0)
4639 if (filename
[0] == '.' || filename
[0] == '/')
4643 if (save_tabs(t
, &a
))
4645 strlcpy(named_session
, filename
, sizeof named_session
);
4653 session_open(struct tab
*t
, char *filename
)
4658 if (strlen(filename
) == 0)
4661 if (filename
[0] == '.' || filename
[0] == '/')
4665 a
.i
= XT_SES_CLOSETABS
;
4666 if (open_tabs(t
, &a
))
4669 strlcpy(named_session
, filename
, sizeof named_session
);
4677 session_delete(struct tab
*t
, char *filename
)
4679 char file
[PATH_MAX
];
4682 if (strlen(filename
) == 0)
4685 if (filename
[0] == '.' || filename
[0] == '/')
4688 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4692 if (!strcmp(filename
, named_session
))
4693 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4694 sizeof named_session
);
4702 session_cmd(struct tab
*t
, struct karg
*args
)
4704 char *filename
= args
->s
;
4709 if (args
->i
& XT_SHOW
)
4710 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4711 XT_SAVED_TABS_FILE
: named_session
);
4712 else if (args
->i
& XT_SAVE
) {
4713 if (session_save(t
, filename
)) {
4714 show_oops(t
, "Can't save session: %s",
4715 filename
? filename
: "INVALID");
4718 } else if (args
->i
& XT_OPEN
) {
4719 if (session_open(t
, filename
)) {
4720 show_oops(t
, "Can't open session: %s",
4721 filename
? filename
: "INVALID");
4724 } else if (args
->i
& XT_DELETE
) {
4725 if (session_delete(t
, filename
)) {
4726 show_oops(t
, "Can't delete session: %s",
4727 filename
? filename
: "INVALID");
4732 return (XT_CB_PASSTHROUGH
);
4736 * Make a hardcopy of the page
4739 print_page(struct tab
*t
, struct karg
*args
)
4741 WebKitWebFrame
*frame
;
4743 GtkPrintOperation
*op
;
4744 GtkPrintOperationAction action
;
4745 GtkPrintOperationResult print_res
;
4746 GError
*g_err
= NULL
;
4747 int marg_l
, marg_r
, marg_t
, marg_b
;
4749 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4751 ps
= gtk_page_setup_new();
4752 op
= gtk_print_operation_new();
4753 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4754 frame
= webkit_web_view_get_main_frame(t
->wv
);
4756 /* the default margins are too small, so we will bump them */
4757 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4758 XT_PRINT_EXTRA_MARGIN
;
4759 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4760 XT_PRINT_EXTRA_MARGIN
;
4761 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4762 XT_PRINT_EXTRA_MARGIN
;
4763 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4764 XT_PRINT_EXTRA_MARGIN
;
4767 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4768 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4769 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4770 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4772 gtk_print_operation_set_default_page_setup(op
, ps
);
4774 /* this appears to free 'op' and 'ps' */
4775 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4777 /* check it worked */
4778 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4779 show_oops(NULL
, "can't print: %s", g_err
->message
);
4780 g_error_free (g_err
);
4788 go_home(struct tab
*t
, struct karg
*args
)
4795 restart(struct tab
*t
, struct karg
*args
)
4799 a
.s
= XT_RESTART_TABS_FILE
;
4801 execvp(start_argv
[0], start_argv
);
4807 #define CTRL GDK_CONTROL_MASK
4808 #define MOD1 GDK_MOD1_MASK
4809 #define SHFT GDK_SHIFT_MASK
4811 /* inherent to GTK not all keys will be caught at all times */
4812 /* XXX sort key bindings */
4813 struct key_binding
{
4818 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4820 { "cookiejar", MOD1
, 0, GDK_j
},
4821 { "downloadmgr", MOD1
, 0, GDK_d
},
4822 { "history", MOD1
, 0, GDK_h
},
4823 { "print", CTRL
, 0, GDK_p
},
4824 { "search", 0, 0, GDK_slash
},
4825 { "searchb", 0, 0, GDK_question
},
4826 { "statustoggle", CTRL
, 0, GDK_n
},
4827 { "command", 0, 0, GDK_colon
},
4828 { "qa", CTRL
, 0, GDK_q
},
4829 { "restart", MOD1
, 0, GDK_q
},
4830 { "js toggle", CTRL
, 0, GDK_j
},
4831 { "cookie toggle", MOD1
, 0, GDK_c
},
4832 { "togglesrc", CTRL
, 0, GDK_s
},
4833 { "yankuri", 0, 0, GDK_y
},
4834 { "pasteuricur", 0, 0, GDK_p
},
4835 { "pasteurinew", 0, 0, GDK_P
},
4836 { "toplevel toggle", 0, 0, GDK_F4
},
4837 { "help", 0, 0, GDK_F1
},
4840 { "searchnext", 0, 0, GDK_n
},
4841 { "searchprevious", 0, 0, GDK_N
},
4844 { "focusaddress", 0, 0, GDK_F6
},
4845 { "focussearch", 0, 0, GDK_F7
},
4848 { "hinting", 0, 0, GDK_f
},
4850 /* custom stylesheet */
4851 { "userstyle", 0, 0, GDK_i
},
4854 { "goback", 0, 0, GDK_BackSpace
},
4855 { "goback", MOD1
, 0, GDK_Left
},
4856 { "goforward", SHFT
, 0, GDK_BackSpace
},
4857 { "goforward", MOD1
, 0, GDK_Right
},
4858 { "reload", 0, 0, GDK_F5
},
4859 { "reload", CTRL
, 0, GDK_r
},
4860 { "reloadforce", CTRL
, 0, GDK_R
},
4861 { "reload", CTRL
, 0, GDK_l
},
4862 { "favorites", MOD1
, 1, GDK_f
},
4864 /* vertical movement */
4865 { "scrolldown", 0, 0, GDK_j
},
4866 { "scrolldown", 0, 0, GDK_Down
},
4867 { "scrollup", 0, 0, GDK_Up
},
4868 { "scrollup", 0, 0, GDK_k
},
4869 { "scrollbottom", 0, 0, GDK_G
},
4870 { "scrollbottom", 0, 0, GDK_End
},
4871 { "scrolltop", 0, 0, GDK_Home
},
4872 { "scrolltop", 0, 0, GDK_g
},
4873 { "scrollpagedown", 0, 0, GDK_space
},
4874 { "scrollpagedown", CTRL
, 0, GDK_f
},
4875 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4876 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4877 { "scrollpageup", 0, 0, GDK_Page_Up
},
4878 { "scrollpageup", CTRL
, 0, GDK_b
},
4879 { "scrollhalfup", CTRL
, 0, GDK_u
},
4880 /* horizontal movement */
4881 { "scrollright", 0, 0, GDK_l
},
4882 { "scrollright", 0, 0, GDK_Right
},
4883 { "scrollleft", 0, 0, GDK_Left
},
4884 { "scrollleft", 0, 0, GDK_h
},
4885 { "scrollfarright", 0, 0, GDK_dollar
},
4886 { "scrollfarleft", 0, 0, GDK_0
},
4889 { "tabnew", CTRL
, 0, GDK_t
},
4890 { "999tabnew", CTRL
, 0, GDK_T
},
4891 { "tabclose", CTRL
, 1, GDK_w
},
4892 { "tabundoclose", 0, 0, GDK_U
},
4893 { "tabnext 1", CTRL
, 0, GDK_1
},
4894 { "tabnext 2", CTRL
, 0, GDK_2
},
4895 { "tabnext 3", CTRL
, 0, GDK_3
},
4896 { "tabnext 4", CTRL
, 0, GDK_4
},
4897 { "tabnext 5", CTRL
, 0, GDK_5
},
4898 { "tabnext 6", CTRL
, 0, GDK_6
},
4899 { "tabnext 7", CTRL
, 0, GDK_7
},
4900 { "tabnext 8", CTRL
, 0, GDK_8
},
4901 { "tabnext 9", CTRL
, 0, GDK_9
},
4902 { "tabnext 10", CTRL
, 0, GDK_0
},
4903 { "tabfirst", CTRL
, 0, GDK_less
},
4904 { "tablast", CTRL
, 0, GDK_greater
},
4905 { "tabprevious", CTRL
, 0, GDK_Left
},
4906 { "tabnext", CTRL
, 0, GDK_Right
},
4907 { "focusout", CTRL
, 0, GDK_minus
},
4908 { "focusin", CTRL
, 0, GDK_plus
},
4909 { "focusin", CTRL
, 0, GDK_equal
},
4911 /* command aliases (handy when -S flag is used) */
4912 { "promptopen", 0, 0, GDK_F9
},
4913 { "promptopencurrent", 0, 0, GDK_F10
},
4914 { "prompttabnew", 0, 0, GDK_F11
},
4915 { "prompttabnewcurrent",0, 0, GDK_F12
},
4917 TAILQ_HEAD(keybinding_list
, key_binding
);
4920 walk_kb(struct settings
*s
,
4921 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4923 struct key_binding
*k
;
4926 if (s
== NULL
|| cb
== NULL
) {
4927 show_oops(NULL
, "walk_kb invalid parameters");
4931 TAILQ_FOREACH(k
, &kbl
, entry
) {
4937 if (gdk_keyval_name(k
->key
) == NULL
)
4940 strlcat(str
, k
->cmd
, sizeof str
);
4941 strlcat(str
, ",", sizeof str
);
4943 if (k
->mask
& GDK_SHIFT_MASK
)
4944 strlcat(str
, "S-", sizeof str
);
4945 if (k
->mask
& GDK_CONTROL_MASK
)
4946 strlcat(str
, "C-", sizeof str
);
4947 if (k
->mask
& GDK_MOD1_MASK
)
4948 strlcat(str
, "M1-", sizeof str
);
4949 if (k
->mask
& GDK_MOD2_MASK
)
4950 strlcat(str
, "M2-", sizeof str
);
4951 if (k
->mask
& GDK_MOD3_MASK
)
4952 strlcat(str
, "M3-", sizeof str
);
4953 if (k
->mask
& GDK_MOD4_MASK
)
4954 strlcat(str
, "M4-", sizeof str
);
4955 if (k
->mask
& GDK_MOD5_MASK
)
4956 strlcat(str
, "M5-", sizeof str
);
4958 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4959 cb(s
, str
, cb_args
);
4964 init_keybindings(void)
4967 struct key_binding
*k
;
4969 for (i
= 0; i
< LENGTH(keys
); i
++) {
4970 k
= g_malloc0(sizeof *k
);
4971 k
->cmd
= keys
[i
].cmd
;
4972 k
->mask
= keys
[i
].mask
;
4973 k
->use_in_entry
= keys
[i
].use_in_entry
;
4974 k
->key
= keys
[i
].key
;
4975 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4977 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4978 k
->cmd
? k
->cmd
: "unnamed key");
4983 keybinding_clearall(void)
4985 struct key_binding
*k
, *next
;
4987 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4988 next
= TAILQ_NEXT(k
, entry
);
4992 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4993 k
->cmd
? k
->cmd
: "unnamed key");
4994 TAILQ_REMOVE(&kbl
, k
, entry
);
5000 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5002 struct key_binding
*k
;
5003 guint keyval
, mask
= 0;
5006 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5008 /* Keys which are to be used in entry have been prefixed with an
5009 * exclamation mark. */
5013 /* find modifier keys */
5014 if (strstr(key
, "S-"))
5015 mask
|= GDK_SHIFT_MASK
;
5016 if (strstr(key
, "C-"))
5017 mask
|= GDK_CONTROL_MASK
;
5018 if (strstr(key
, "M1-"))
5019 mask
|= GDK_MOD1_MASK
;
5020 if (strstr(key
, "M2-"))
5021 mask
|= GDK_MOD2_MASK
;
5022 if (strstr(key
, "M3-"))
5023 mask
|= GDK_MOD3_MASK
;
5024 if (strstr(key
, "M4-"))
5025 mask
|= GDK_MOD4_MASK
;
5026 if (strstr(key
, "M5-"))
5027 mask
|= GDK_MOD5_MASK
;
5030 for (i
= strlen(key
) - 1; i
> 0; i
--)
5034 /* validate keyname */
5035 keyval
= gdk_keyval_from_name(key
);
5036 if (keyval
== GDK_VoidSymbol
) {
5037 warnx("invalid keybinding name %s", key
);
5040 /* must run this test too, gtk+ doesn't handle 10 for example */
5041 if (gdk_keyval_name(keyval
) == NULL
) {
5042 warnx("invalid keybinding name %s", key
);
5046 /* Remove eventual dupes. */
5047 TAILQ_FOREACH(k
, &kbl
, entry
)
5048 if (k
->key
== keyval
&& k
->mask
== mask
) {
5049 TAILQ_REMOVE(&kbl
, k
, entry
);
5055 k
= g_malloc0(sizeof *k
);
5056 k
->cmd
= g_strdup(cmd
);
5058 k
->use_in_entry
= use_in_entry
;
5061 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5066 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5067 k
->cmd
, gdk_keyval_name(keyval
));
5069 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5075 add_kb(struct settings
*s
, char *entry
)
5079 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5081 /* clearall is special */
5082 if (!strcmp(entry
, "clearall")) {
5083 keybinding_clearall();
5087 kb
= strstr(entry
, ",");
5093 return (keybinding_add(entry
, key
, key
[0] == '!'));
5099 int (*func
)(struct tab
*, struct karg
*);
5103 { "command", 0, command
, ':', 0 },
5104 { "search", 0, command
, '/', 0 },
5105 { "searchb", 0, command
, '?', 0 },
5106 { "togglesrc", 0, toggle_src
, 0, 0 },
5108 /* yanking and pasting */
5109 { "yankuri", 0, yank_uri
, 0, 0 },
5110 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5111 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5112 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5115 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5116 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5119 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5120 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5123 { "hinting", 0, hint
, 0, 0 },
5125 /* custom stylesheet */
5126 { "userstyle", 0, userstyle
, 0, 0 },
5129 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5130 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5131 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5132 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
5134 /* vertical movement */
5135 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5136 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5137 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5138 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5139 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5140 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5141 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5142 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5143 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5144 /* horizontal movement */
5145 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5146 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5147 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5148 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5151 { "favorites", 0, xtp_page_fl
, 0, 0 },
5152 { "fav", 0, xtp_page_fl
, 0, 0 },
5153 { "favadd", 0, add_favorite
, 0, 0 },
5155 { "qall", 0, quit
, 0, 0 },
5156 { "quitall", 0, quit
, 0, 0 },
5157 { "w", 0, save_tabs
, 0, 0 },
5158 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5159 { "help", 0, help
, 0, 0 },
5160 { "about", 0, about
, 0, 0 },
5161 { "stats", 0, stats
, 0, 0 },
5162 { "version", 0, about
, 0, 0 },
5165 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5166 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5167 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5168 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5169 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5170 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5171 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5172 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5173 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5174 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5175 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5177 /* cookie command */
5178 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5179 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5180 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5181 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5182 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5183 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5184 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5185 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5186 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5187 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5188 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5190 /* toplevel (domain) command */
5191 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5192 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5195 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5198 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5199 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5200 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5202 { "ca", 0, ca_cmd
, 0, 0 },
5203 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5204 { "dl", 0, xtp_page_dl
, 0, 0 },
5205 { "h", 0, xtp_page_hl
, 0, 0 },
5206 { "history", 0, xtp_page_hl
, 0, 0 },
5207 { "home", 0, go_home
, 0, 0 },
5208 { "restart", 0, restart
, 0, 0 },
5209 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5210 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5211 { "statustoggle", 0, statustoggle
, 0, 0 },
5213 { "print", 0, print_page
, 0, 0 },
5216 { "focusin", 0, resizetab
, 1, 0 },
5217 { "focusout", 0, resizetab
, -1, 0 },
5218 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5219 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5220 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5221 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5222 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5223 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5224 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5225 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5226 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5227 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5228 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5229 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5230 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5231 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5232 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5233 { "buffers", 0, buffers
, 0, 0 },
5234 { "ls", 0, buffers
, 0, 0 },
5235 { "tabs", 0, buffers
, 0, 0 },
5237 /* command aliases (handy when -S flag is used) */
5238 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5239 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5240 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5241 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5244 { "set", 0, set
, 0, 0 },
5245 { "fullscreen", 0, fullscreen
, 0, 0 },
5246 { "f", 0, fullscreen
, 0, 0 },
5249 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5250 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5251 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5252 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5253 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5260 } cmd_status
= {-1, 0};
5263 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5266 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5273 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5280 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5282 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5288 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5290 a
.i
= XT_NAV_FORWARD
;
5300 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5302 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5304 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5311 * cancel, remove, etc. downloads
5314 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5316 struct download find
, *d
= NULL
;
5318 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5320 /* some commands require a valid download id */
5321 if (cmd
!= XT_XTP_DL_LIST
) {
5322 /* lookup download in question */
5324 d
= RB_FIND(download_list
, &downloads
, &find
);
5327 show_oops(t
, "%s: no such download", __func__
);
5332 /* decide what to do */
5334 case XT_XTP_DL_CANCEL
:
5335 webkit_download_cancel(d
->download
);
5337 case XT_XTP_DL_REMOVE
:
5338 webkit_download_cancel(d
->download
); /* just incase */
5339 g_object_unref(d
->download
);
5340 RB_REMOVE(download_list
, &downloads
, d
);
5342 case XT_XTP_DL_LIST
:
5346 show_oops(t
, "%s: unknown command", __func__
);
5349 xtp_page_dl(t
, NULL
);
5353 * Actions on history, only does one thing for now, but
5354 * we provide the function for future actions
5357 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5359 struct history
*h
, *next
;
5363 case XT_XTP_HL_REMOVE
:
5364 /* walk backwards, as listed in reverse */
5365 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5366 next
= RB_PREV(history_list
, &hl
, h
);
5368 RB_REMOVE(history_list
, &hl
, h
);
5369 g_free((gpointer
) h
->title
);
5370 g_free((gpointer
) h
->uri
);
5377 case XT_XTP_HL_LIST
:
5378 /* Nothing - just xtp_page_hl() below */
5381 show_oops(t
, "%s: unknown command", __func__
);
5385 xtp_page_hl(t
, NULL
);
5388 /* remove a favorite */
5390 remove_favorite(struct tab
*t
, int index
)
5392 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5393 char *new_favs
, *tmp
;
5398 /* open favorites */
5399 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5401 if ((f
= fopen(file
, "r")) == NULL
) {
5402 show_oops(t
, "%s: can't open favorites: %s",
5403 __func__
, strerror(errno
));
5407 /* build a string which will become the new favroites file */
5408 new_favs
= g_strdup("");
5411 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5412 if (feof(f
) || ferror(f
))
5414 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5421 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5422 if (feof(f
) || ferror(f
)) {
5423 show_oops(t
, "%s: can't parse favorites %s",
5424 __func__
, strerror(errno
));
5429 /* as long as this isn't the one we are deleting add to file */
5432 new_favs
= g_strdup_printf("%s%s\n%s\n",
5433 new_favs
, title
, uri
);
5445 /* write back new favorites file */
5446 if ((f
= fopen(file
, "w")) == NULL
) {
5447 show_oops(t
, "%s: can't open favorites: %s",
5448 __func__
, strerror(errno
));
5452 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5465 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5468 case XT_XTP_FL_LIST
:
5469 /* nothing, just the below call to xtp_page_fl() */
5471 case XT_XTP_FL_REMOVE
:
5472 remove_favorite(t
, arg
);
5475 show_oops(t
, "%s: invalid favorites command", __func__
);
5479 xtp_page_fl(t
, NULL
);
5483 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5486 case XT_XTP_CL_LIST
:
5487 /* nothing, just xtp_page_cl() */
5489 case XT_XTP_CL_REMOVE
:
5493 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5497 xtp_page_cl(t
, NULL
);
5500 /* link an XTP class to it's session key and handler function */
5501 struct xtp_despatch
{
5504 void (*handle_func
)(struct tab
*, uint8_t, int);
5507 struct xtp_despatch xtp_despatches
[] = {
5508 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5509 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5510 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5511 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5512 { XT_XTP_INVALID
, NULL
, NULL
}
5516 * is the url xtp protocol? (xxxt://)
5517 * if so, parse and despatch correct bahvior
5520 parse_xtp_url(struct tab
*t
, const char *url
)
5522 char *dup
= NULL
, *p
, *last
;
5523 uint8_t n_tokens
= 0;
5524 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5525 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5530 * tokens array meaning:
5532 * tokens[1] = session key
5533 * tokens[2] = action
5534 * tokens[3] = optional argument
5537 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5539 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5542 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5544 /* split out the url */
5545 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5546 (p
= strtok_r(NULL
, "/", &last
))) {
5548 tokens
[n_tokens
++] = p
;
5551 /* should be atleast three fields 'class/seskey/command/arg' */
5555 dsp
= xtp_despatches
;
5556 req_class
= atoi(tokens
[0]);
5557 while (dsp
->xtp_class
) {
5558 if (dsp
->xtp_class
== req_class
) {
5565 /* did we find one atall? */
5566 if (dsp_match
== NULL
) {
5567 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5571 /* check session key and call despatch function */
5572 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5573 ret
= TRUE
; /* all is well, this was a valid xtp request */
5574 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5587 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5589 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5591 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5594 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5599 show_oops(t
, "activate_uri_entry_cb no uri");
5603 uri
+= strspn(uri
, "\t ");
5605 /* if xxxt:// treat specially */
5606 if (parse_xtp_url(t
, uri
))
5609 /* otherwise continue to load page normally */
5610 load_uri(t
, (gchar
*)uri
);
5615 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5617 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5618 char *newuri
= NULL
;
5621 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5624 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5628 if (search_string
== NULL
) {
5629 show_oops(t
, "no search_string");
5633 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5635 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5636 newuri
= g_strdup_printf(search_string
, enc_search
);
5639 webkit_web_view_load_uri(t
->wv
, newuri
);
5647 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5649 struct domain
*d
= NULL
;
5652 if (uri
== NULL
|| t
== NULL
)
5655 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5660 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5661 es
? "enable" : "disable", uri
);
5663 g_object_set(G_OBJECT(t
->settings
),
5664 "enable-scripts", es
, (char *)NULL
);
5665 g_object_set(G_OBJECT(t
->settings
),
5666 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5667 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5669 button_set_stockid(t
->js_toggle
,
5670 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5674 show_ca_status(struct tab
*t
, const char *uri
)
5676 WebKitWebFrame
*frame
;
5677 WebKitWebDataSource
*source
;
5678 WebKitNetworkRequest
*request
;
5679 SoupMessage
*message
;
5681 gchar
*col_str
= XT_COLOR_WHITE
;
5684 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5685 ssl_strict_certs
, ssl_ca_file
, uri
);
5689 if (ssl_ca_file
== NULL
) {
5690 if (g_str_has_prefix(uri
, "http://"))
5692 if (g_str_has_prefix(uri
, "https://")) {
5693 col_str
= XT_COLOR_RED
;
5698 if (g_str_has_prefix(uri
, "http://") ||
5699 !g_str_has_prefix(uri
, "https://"))
5702 frame
= webkit_web_view_get_main_frame(t
->wv
);
5703 source
= webkit_web_frame_get_data_source(frame
);
5704 request
= webkit_web_data_source_get_request(source
);
5705 message
= webkit_network_request_get_message(request
);
5707 if (message
&& (soup_message_get_flags(message
) &
5708 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5709 col_str
= XT_COLOR_GREEN
;
5712 r
= load_compare_cert(t
, NULL
);
5714 col_str
= XT_COLOR_BLUE
;
5716 col_str
= XT_COLOR_YELLOW
;
5718 col_str
= XT_COLOR_RED
;
5723 gdk_color_parse(col_str
, &color
);
5724 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5726 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5727 gtk_widget_modify_text(t
->sbe
.statusbar
,
5728 GTK_STATE_NORMAL
, &color
);
5729 gtk_widget_modify_text(t
->sbe
.position
,
5730 GTK_STATE_NORMAL
, &color
);
5731 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5732 gtk_widget_modify_base(t
->sbe
.statusbar
,
5733 GTK_STATE_NORMAL
, &color
);
5734 gtk_widget_modify_base(t
->sbe
.position
,
5735 GTK_STATE_NORMAL
, &color
);
5737 gtk_widget_modify_base(t
->sbe
.statusbar
,
5738 GTK_STATE_NORMAL
, &color
);
5739 gtk_widget_modify_base(t
->sbe
.position
,
5740 GTK_STATE_NORMAL
, &color
);
5741 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5742 gtk_widget_modify_text(t
->sbe
.statusbar
,
5743 GTK_STATE_NORMAL
, &color
);
5744 gtk_widget_modify_text(t
->sbe
.position
,
5745 GTK_STATE_NORMAL
, &color
);
5751 free_favicon(struct tab
*t
)
5753 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5754 __func__
, t
->icon_download
, t
->icon_request
);
5756 if (t
->icon_request
)
5757 g_object_unref(t
->icon_request
);
5758 if (t
->icon_dest_uri
)
5759 g_free(t
->icon_dest_uri
);
5761 t
->icon_request
= NULL
;
5762 t
->icon_dest_uri
= NULL
;
5766 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5768 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5769 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5771 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5772 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5774 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5775 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5779 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5781 GdkPixbuf
*pb_scaled
;
5783 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5784 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
5785 GDK_INTERP_BILINEAR
);
5789 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5790 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5792 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
5793 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5795 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5796 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5798 if (pb_scaled
!= pb
)
5799 g_object_unref(pb_scaled
);
5803 xt_icon_from_file(struct tab
*t
, char *file
)
5807 if (g_str_has_prefix(file
, "file://"))
5808 file
+= strlen("file://");
5810 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5812 xt_icon_from_pixbuf(t
, pb
);
5815 xt_icon_from_name(t
, "text-html");
5819 is_valid_icon(char *file
)
5822 const char *mime_type
;
5826 gf
= g_file_new_for_path(file
);
5827 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5829 mime_type
= g_file_info_get_content_type(fi
);
5830 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5831 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5832 g_strcmp0(mime_type
, "image/png") == 0 ||
5833 g_strcmp0(mime_type
, "image/gif") == 0 ||
5834 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5842 set_favicon_from_file(struct tab
*t
, char *file
)
5846 if (t
== NULL
|| file
== NULL
)
5849 if (g_str_has_prefix(file
, "file://"))
5850 file
+= strlen("file://");
5851 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5853 if (!stat(file
, &sb
)) {
5854 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5855 /* corrupt icon so trash it */
5856 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5859 /* no need to set icon to default here */
5863 xt_icon_from_file(t
, file
);
5867 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5870 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5871 struct tab
*tt
= NULL
, *t
= NULL
;
5874 * find the webview instead of passing in the tab as it could have been
5875 * deleted from underneath us.
5877 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5886 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5887 __func__
, t
->tab_id
, status
);
5890 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5892 t
->icon_download
= NULL
;
5895 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5898 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5901 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5903 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5904 __func__
, t
->tab_id
);
5905 t
->icon_download
= NULL
;
5908 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5911 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5912 __func__
, t
->icon_dest_uri
);
5913 set_favicon_from_file(t
, t
->icon_dest_uri
);
5914 /* these will be freed post callback */
5915 t
->icon_request
= NULL
;
5916 t
->icon_download
= NULL
;
5924 abort_favicon_download(struct tab
*t
)
5926 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5928 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
5929 if (t
->icon_download
) {
5930 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5931 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5932 webkit_download_cancel(t
->icon_download
);
5933 t
->icon_download
= NULL
;
5938 xt_icon_from_name(t
, "text-html");
5942 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5944 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
5946 if (uri
== NULL
|| t
== NULL
)
5949 #if WEBKIT_CHECK_VERSION(1, 4, 0)
5950 /* take icon from WebKitIconDatabase */
5953 pb
= webkit_web_view_get_icon_pixbuf(wv
);
5955 xt_icon_from_pixbuf(t
, pb
);
5958 xt_icon_from_name(t
, "text-html");
5959 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
5960 /* download icon to cache dir */
5961 gchar
*name_hash
, file
[PATH_MAX
];
5964 if (t
->icon_request
) {
5965 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5969 /* check to see if we got the icon in cache */
5970 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5971 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5974 if (!stat(file
, &sb
)) {
5975 if (sb
.st_size
> 0) {
5976 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5978 set_favicon_from_file(t
, file
);
5982 /* corrupt icon so trash it */
5983 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5988 /* create download for icon */
5989 t
->icon_request
= webkit_network_request_new(uri
);
5990 if (t
->icon_request
== NULL
) {
5991 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5996 t
->icon_download
= webkit_download_new(t
->icon_request
);
5997 if (t
->icon_download
== NULL
)
6000 /* we have to free icon_dest_uri later */
6001 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6002 webkit_download_set_destination_uri(t
->icon_download
,
6005 if (webkit_download_get_status(t
->icon_download
) ==
6006 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6007 g_object_unref(t
->icon_request
);
6008 g_free(t
->icon_dest_uri
);
6009 t
->icon_request
= NULL
;
6010 t
->icon_dest_uri
= NULL
;
6014 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6015 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6017 webkit_download_start(t
->icon_download
);
6022 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6024 const gchar
*uri
= NULL
, *title
= NULL
;
6025 struct history
*h
, find
;
6026 const gchar
*s_loading
;
6029 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6030 webkit_web_view_get_load_status(wview
), get_uri(t
) ? get_uri(t
) : "NOTHING");
6033 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6037 switch (webkit_web_view_get_load_status(wview
)) {
6038 case WEBKIT_LOAD_PROVISIONAL
:
6040 abort_favicon_download(t
);
6041 #if GTK_CHECK_VERSION(2, 20, 0)
6042 gtk_widget_show(t
->spinner
);
6043 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6045 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6047 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6049 /* take focus if we are visible */
6055 case WEBKIT_LOAD_COMMITTED
:
6057 if ((uri
= get_uri(t
)) != NULL
) {
6058 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6064 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6067 /* check if js white listing is enabled */
6068 if (enable_js_whitelist
) {
6070 check_and_set_js(uri
, t
);
6076 show_ca_status(t
, uri
);
6078 /* we know enough to autosave the session */
6079 if (session_autosave
) {
6085 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6089 case WEBKIT_LOAD_FINISHED
:
6095 if (!strncmp(uri
, "http://", strlen("http://")) ||
6096 !strncmp(uri
, "https://", strlen("https://")) ||
6097 !strncmp(uri
, "file://", strlen("file://"))) {
6099 h
= RB_FIND(history_list
, &hl
, &find
);
6101 title
= get_title(t
, FALSE
);
6102 h
= g_malloc(sizeof *h
);
6103 h
->uri
= g_strdup(uri
);
6104 h
->title
= g_strdup(title
);
6105 RB_INSERT(history_list
, &hl
, h
);
6106 completion_add_uri(h
->uri
);
6107 update_history_tabs(NULL
);
6111 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6112 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6113 case WEBKIT_LOAD_FAILED
:
6116 #if GTK_CHECK_VERSION(2, 20, 0)
6117 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6118 gtk_widget_hide(t
->spinner
);
6120 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
6121 if (s_loading
&& !strcmp(s_loading
, "Loading"))
6122 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6124 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6129 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6131 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6132 webkit_web_view_can_go_back(wview
));
6134 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6135 webkit_web_view_can_go_forward(wview
));
6139 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6141 const gchar
*title
= NULL
, *win_title
= NULL
;
6143 title
= get_title(t
, FALSE
);
6144 win_title
= get_title(t
, TRUE
);
6145 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6146 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6147 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6148 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6152 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6154 run_script(t
, JS_HINTING
);
6158 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6160 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6161 progress
== 100 ? 0 : (double)progress
/ 100);
6162 if (show_url
== 0) {
6163 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6164 progress
== 100 ? 0 : (double)progress
/ 100);
6167 update_statusbar_position(NULL
, NULL
);
6171 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6172 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6173 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6176 WebKitWebNavigationReason reason
;
6177 struct domain
*d
= NULL
;
6180 show_oops(NULL
, "webview_npd_cb invalid parameters");
6184 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6186 webkit_network_request_get_uri(request
));
6188 uri
= (char *)webkit_network_request_get_uri(request
);
6190 /* if this is an xtp url, we don't load anything else */
6191 if (parse_xtp_url(t
, uri
))
6194 if (t
->ctrl_click
) {
6196 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6197 webkit_web_policy_decision_ignore(pd
);
6198 return (TRUE
); /* we made the decission */
6202 * This is a little hairy but it comes down to this:
6203 * when we run in whitelist mode we have to assist the browser in
6204 * opening the URL that it would have opened in a new tab.
6206 reason
= webkit_web_navigation_action_get_reason(na
);
6207 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6208 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6209 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6210 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6212 webkit_web_policy_decision_use(pd
);
6213 return (TRUE
); /* we made the decision */
6220 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6223 struct domain
*d
= NULL
;
6225 WebKitWebView
*webview
= NULL
;
6227 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6228 webkit_web_view_get_uri(wv
));
6231 /* open in current tab */
6233 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6234 uri
= webkit_web_view_get_uri(wv
);
6235 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6238 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6240 } else if (enable_scripts
== 1) {
6241 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6249 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6252 struct domain
*d
= NULL
;
6254 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6256 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6257 uri
= webkit_web_view_get_uri(wv
);
6258 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6262 } else if (enable_scripts
== 1)
6269 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6271 /* we can not eat the event without throwing gtk off so defer it */
6273 /* catch middle click */
6274 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6279 /* catch ctrl click */
6280 if (e
->type
== GDK_BUTTON_RELEASE
&&
6281 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6286 return (XT_CB_PASSTHROUGH
);
6290 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6292 struct mime_type
*m
;
6294 m
= find_mime_type(mime_type
);
6302 show_oops(t
, "can't fork mime handler");
6311 execlp(m
->mt_action
, m
->mt_action
,
6312 webkit_network_request_get_uri(request
), (void *)NULL
);
6321 get_mime_type(char *file
)
6323 const char *mime_type
;
6327 if (g_str_has_prefix(file
, "file://"))
6328 file
+= strlen("file://");
6330 gf
= g_file_new_for_path(file
);
6331 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6333 mime_type
= g_file_info_get_content_type(fi
);
6341 run_download_mimehandler(char *mime_type
, char *file
)
6343 struct mime_type
*m
;
6345 m
= find_mime_type(mime_type
);
6351 show_oops(NULL
, "can't fork download mime handler");
6361 if (g_str_has_prefix(file
, "file://"))
6362 file
+= strlen("file://");
6363 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6372 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6375 WebKitDownloadStatus status
;
6376 const gchar
*file
= NULL
, *mime
= NULL
;
6378 if (download
== NULL
)
6380 status
= webkit_download_get_status(download
);
6381 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6384 file
= webkit_download_get_destination_uri(download
);
6387 mime
= get_mime_type((char *)file
);
6391 run_download_mimehandler((char *)mime
, (char *)file
);
6395 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6396 WebKitNetworkRequest
*request
, char *mime_type
,
6397 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6400 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6404 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6405 t
->tab_id
, mime_type
);
6407 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6408 webkit_web_policy_decision_ignore(decision
);
6413 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6414 webkit_web_policy_decision_download(decision
);
6422 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6426 const gchar
*suggested_name
;
6427 gchar
*filename
= NULL
;
6429 struct download
*download_entry
;
6432 if (wk_download
== NULL
|| t
== NULL
) {
6433 show_oops(NULL
, "%s invalid parameters", __func__
);
6437 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6438 if (suggested_name
== NULL
)
6439 return (FALSE
); /* abort download */
6450 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6452 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6453 filename
: suggested_name
);
6455 } while (!stat(uri
+ strlen("file://"), &sb
));
6457 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6458 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6460 webkit_download_set_destination_uri(wk_download
, uri
);
6462 if (webkit_download_get_status(wk_download
) ==
6463 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6464 show_oops(t
, "%s: download failed to start", __func__
);
6466 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6468 /* connect "download first" mime handler */
6469 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6470 G_CALLBACK(download_status_changed_cb
), NULL
);
6472 download_entry
= g_malloc(sizeof(struct download
));
6473 download_entry
->download
= wk_download
;
6474 download_entry
->tab
= t
;
6475 download_entry
->id
= next_download_id
++;
6476 RB_INSERT(download_list
, &downloads
, download_entry
);
6477 /* get from history */
6478 g_object_ref(wk_download
);
6479 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6480 show_oops(t
, "Download of '%s' started...",
6481 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6490 /* sync other download manager tabs */
6491 update_download_tabs(NULL
);
6494 * NOTE: never redirect/render the current tab before this
6495 * function returns. This will cause the download to never start.
6497 return (ret
); /* start download */
6501 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6503 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6506 show_oops(NULL
, "webview_hover_cb");
6511 set_status(t
, uri
, XT_STATUS_LINK
);
6514 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6519 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6521 struct key_binding
*k
;
6523 TAILQ_FOREACH(k
, &kbl
, entry
)
6524 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6526 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6527 return (cmd_execute(t
, k
->cmd
));
6528 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6529 return (cmd_execute(t
, k
->cmd
));
6533 return (XT_CB_PASSTHROUGH
);
6537 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6539 char s
[2], buf
[128];
6540 const char *errstr
= NULL
;
6542 /* don't use w directly; use t->whatever instead */
6545 show_oops(NULL
, "wv_keypress_after_cb");
6546 return (XT_CB_PASSTHROUGH
);
6549 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6550 e
->keyval
, e
->state
, t
);
6554 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6556 return (XT_CB_HANDLED
);
6560 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6562 /* we have a string */
6564 /* we have a number */
6565 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6573 /* XXX unfuck this */
6574 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6575 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6576 /* last input was numerical */
6578 l
= strlen(t
->hint_num
);
6585 t
->hint_num
[l
] = '\0';
6589 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6590 /* last input was alphanumerical */
6592 l
= strlen(t
->hint_buf
);
6599 t
->hint_buf
[l
] = '\0';
6609 /* numerical input */
6610 if (CLEAN(e
->state
) == 0 &&
6611 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6612 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6613 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6614 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6618 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6621 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6623 t
->hint_mode
= XT_HINT_NUMERICAL
;
6627 /* empty the counter buffer */
6628 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6629 return (XT_CB_HANDLED
);
6632 /* alphanumerical input */
6634 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6635 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6636 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6637 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6638 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6639 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6640 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6643 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6646 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6648 t
->hint_mode
= XT_HINT_ALPHANUM
;
6651 /* empty the counter buffer */
6652 bzero(t
->hint_num
, sizeof t
->hint_num
);
6653 return (XT_CB_HANDLED
);
6656 return (XT_CB_HANDLED
);
6659 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6660 if (CLEAN(e
->state
) == 0 && isdigit(s
[0])) {
6661 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
6665 return (handle_keypress(t
, e
, 0));
6669 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6673 /* Hide buffers, if they are visible, with escape. */
6674 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
6675 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6676 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6678 return (XT_CB_HANDLED
);
6681 return (XT_CB_PASSTHROUGH
);
6685 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6687 const gchar
*c
= gtk_entry_get_text(w
);
6691 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6692 e
->keyval
, e
->state
, t
);
6695 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
6696 return (XT_CB_PASSTHROUGH
);
6699 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6700 e
->keyval
, e
->state
, t
);
6704 if (strlen(c
) == 1) {
6705 webkit_web_view_unmark_text_matches(t
->wv
);
6711 else if (c
[0] == '?')
6717 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6719 /* not found, mark red */
6720 gdk_color_parse(XT_COLOR_RED
, &color
);
6721 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6722 /* unmark and remove selection */
6723 webkit_web_view_unmark_text_matches(t
->wv
);
6724 /* my kingdom for a way to unselect text in webview */
6726 /* found, highlight all */
6727 webkit_web_view_unmark_text_matches(t
->wv
);
6728 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6729 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6730 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6731 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6734 return (XT_CB_PASSTHROUGH
);
6738 match_uri(const gchar
*uri
, const gchar
*key
) {
6741 gboolean match
= FALSE
;
6745 if (!strncmp(key
, uri
, len
))
6748 voffset
= strstr(uri
, "/") + 2;
6749 if (!strncmp(key
, voffset
, len
))
6751 else if (g_str_has_prefix(voffset
, "www.")) {
6752 voffset
= voffset
+ strlen("www.");
6753 if (!strncmp(key
, voffset
, len
))
6762 cmd_getlist(int id
, char *key
)
6767 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
6768 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6769 if (match_uri(h
->uri
, key
)) {
6770 cmd_status
.list
[c
] = (char *)h
->uri
;
6779 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6781 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6782 if (cmds
[i
].level
< dep
)
6784 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6785 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6793 cmd_getnext(int dir
)
6795 cmd_status
.index
+= dir
;
6797 if (cmd_status
.index
< 0)
6798 cmd_status
.index
= cmd_status
.len
- 1;
6799 else if (cmd_status
.index
>= cmd_status
.len
)
6800 cmd_status
.index
= 0;
6802 return cmd_status
.list
[cmd_status
.index
];
6806 cmd_tokenize(char *s
, char *tokens
[])
6810 size_t len
= strlen(s
);
6811 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6813 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6823 cmd_complete(struct tab
*t
, char *str
, int dir
)
6825 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6826 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1, matchcount
= 0;
6827 char *tok
, *match
, *s
= g_strdup(str
);
6829 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6832 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
6835 for (i
= 0; isdigit(s
[i
]); i
++)
6838 for (; isspace(s
[i
]); i
++)
6843 levels
= cmd_tokenize(s
, tokens
);
6845 for (i
= 0; i
< levels
- 1; i
++) {
6848 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6849 if (cmds
[j
].level
< dep
)
6851 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, strlen(tok
))) {
6854 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
6861 if (matchcount
== 1) {
6862 strlcat(res
, tok
, sizeof res
);
6863 strlcat(res
, " ", sizeof res
);
6873 if (cmd_status
.index
== -1)
6874 cmd_getlist(parent
, tokens
[i
]);
6876 if (cmd_status
.len
> 0) {
6877 match
= cmd_getnext(dir
);
6878 strlcat(res
, match
, sizeof res
);
6879 gtk_entry_set_text(w
, res
);
6880 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6887 cmd_execute(struct tab
*t
, char *str
)
6889 struct cmd
*cmd
= NULL
;
6890 char *tok
, *last
, *s
= g_strdup(str
), *sc
, prefixstr
[4];
6891 int j
, len
, c
= 0, dep
= 0, matchcount
= 0, prefix
= -1;
6892 struct karg arg
= {0, NULL
, -1};
6893 int rv
= XT_CB_PASSTHROUGH
;
6898 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
6904 while (isspace(s
[0]))
6907 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
6908 prefix
= atoi(prefixstr
);
6912 for (tok
= strtok_r(s
, " ", &last
); tok
;
6913 tok
= strtok_r(NULL
, " ", &last
)) {
6915 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6916 if (cmds
[j
].level
< dep
)
6918 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1: strlen(tok
);
6919 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, len
)) {
6923 if (len
== strlen(cmds
[j
].cmd
)) {
6929 if (matchcount
== 1) {
6934 show_oops(t
, "Invalid command: %s", str
);
6943 else if (cmd_prefix
> 0)
6946 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
6947 show_oops(t
, "No prefix allowed: %s", str
);
6951 arg
.s
= last
? g_strdup(last
) : g_strdup("");
6952 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
6953 arg
.p
= atoi(arg
.s
);
6956 show_oops(t
, "Zero count");
6958 show_oops(t
, "Trailing characters");
6963 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
6979 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6982 show_oops(NULL
, "entry_key_cb invalid parameters");
6983 return (XT_CB_PASSTHROUGH
);
6986 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6987 e
->keyval
, e
->state
, t
);
6991 if (e
->keyval
== GDK_Escape
) {
6992 /* don't use focus_webview(t) because we want to type :cmds */
6993 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6996 return (handle_keypress(t
, e
, 1));
7000 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7002 int rv
= XT_CB_HANDLED
;
7003 const gchar
*c
= gtk_entry_get_text(w
);
7006 show_oops(NULL
, "cmd_keypress_cb parameters");
7007 return (XT_CB_PASSTHROUGH
);
7010 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7011 e
->keyval
, e
->state
, t
);
7015 e
->keyval
= GDK_Escape
;
7016 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7017 e
->keyval
= GDK_Escape
;
7019 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
7020 cmd_status
.index
= -1;
7022 switch (e
->keyval
) {
7025 cmd_complete(t
, (char *)&c
[1], 1);
7027 case GDK_ISO_Left_Tab
:
7029 cmd_complete(t
, (char *)&c
[1], -1);
7033 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7041 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7042 webkit_web_view_unmark_text_matches(t
->wv
);
7046 rv
= XT_CB_PASSTHROUGH
;
7052 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7055 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7056 return (XT_CB_PASSTHROUGH
);
7058 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7063 if (show_url
== 0 || t
->focus_wv
)
7066 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7068 return (XT_CB_PASSTHROUGH
);
7072 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7075 const gchar
*c
= gtk_entry_get_text(entry
);
7078 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7082 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7089 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7095 if (c
[0] == '/' || c
[0] == '?') {
7096 if (t
->search_text
) {
7097 g_free(t
->search_text
);
7098 t
->search_text
= NULL
;
7101 t
->search_text
= g_strdup(s
);
7103 g_free(global_search
);
7104 global_search
= g_strdup(s
);
7105 t
->search_forward
= c
[0] == '/';
7117 backward_cb(GtkWidget
*w
, struct tab
*t
)
7122 show_oops(NULL
, "backward_cb invalid parameters");
7126 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7133 forward_cb(GtkWidget
*w
, struct tab
*t
)
7138 show_oops(NULL
, "forward_cb invalid parameters");
7142 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7144 a
.i
= XT_NAV_FORWARD
;
7149 home_cb(GtkWidget
*w
, struct tab
*t
)
7152 show_oops(NULL
, "home_cb invalid parameters");
7156 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7162 stop_cb(GtkWidget
*w
, struct tab
*t
)
7164 WebKitWebFrame
*frame
;
7167 show_oops(NULL
, "stop_cb invalid parameters");
7171 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7173 frame
= webkit_web_view_get_main_frame(t
->wv
);
7174 if (frame
== NULL
) {
7175 show_oops(t
, "stop_cb: no frame");
7179 webkit_web_frame_stop_loading(frame
);
7180 abort_favicon_download(t
);
7184 setup_webkit(struct tab
*t
)
7186 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7187 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7188 FALSE
, (char *)NULL
);
7190 warnx("webkit does not have \"enable-dns-prefetching\" property");
7191 g_object_set(G_OBJECT(t
->settings
),
7192 "user-agent", t
->user_agent
, (char *)NULL
);
7193 g_object_set(G_OBJECT(t
->settings
),
7194 "enable-scripts", enable_scripts
, (char *)NULL
);
7195 g_object_set(G_OBJECT(t
->settings
),
7196 "enable-plugins", enable_plugins
, (char *)NULL
);
7197 g_object_set(G_OBJECT(t
->settings
),
7198 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
7199 g_object_set(G_OBJECT(t
->settings
),
7200 "enable-html5-database", FALSE
, (char *)NULL
);
7201 g_object_set(G_OBJECT(t
->settings
),
7202 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7203 g_object_set(G_OBJECT(t
->settings
),
7204 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7205 g_object_set(G_OBJECT(t
->settings
),
7206 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7207 g_object_set(G_OBJECT(t
->wv
),
7208 "full-content-zoom", TRUE
, (char *)NULL
);
7209 adjustfont_webkit(t
, XT_FONT_SET
);
7211 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7215 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
7217 struct tab
*ti
, *t
= NULL
;
7218 gdouble view_size
, value
, max
;
7221 TAILQ_FOREACH(ti
, &tabs
, entry
)
7222 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
7230 if (adjustment
== NULL
)
7231 adjustment
= gtk_scrolled_window_get_vadjustment(
7232 GTK_SCROLLED_WINDOW(t
->browser_win
));
7234 view_size
= gtk_adjustment_get_page_size(adjustment
);
7235 value
= gtk_adjustment_get_value(adjustment
);
7236 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
7239 position
= g_strdup("All");
7240 else if (value
== max
)
7241 position
= g_strdup("Bot");
7242 else if (value
== 0)
7243 position
= g_strdup("Top");
7245 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
7247 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
7254 create_browser(struct tab
*t
)
7258 GtkAdjustment
*adjustment
;
7261 show_oops(NULL
, "create_browser invalid parameters");
7265 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7266 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7267 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7268 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7270 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7271 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7272 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7274 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7275 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7278 t
->settings
= webkit_web_settings_new();
7280 if (user_agent
== NULL
) {
7281 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7283 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7286 t
->user_agent
= g_strdup(user_agent
);
7288 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7291 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
7292 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
7293 G_CALLBACK(update_statusbar_position
), NULL
);
7305 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7306 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7307 gtk_widget_set_name(w
, "xxxterm");
7308 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7309 g_signal_connect(G_OBJECT(w
), "delete_event",
7310 G_CALLBACK (gtk_main_quit
), NULL
);
7316 create_kiosk_toolbar(struct tab
*t
)
7318 GtkWidget
*toolbar
= NULL
, *b
;
7320 b
= gtk_hbox_new(FALSE
, 0);
7322 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7324 /* backward button */
7325 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7326 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7327 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7328 G_CALLBACK(backward_cb
), t
);
7329 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7331 /* forward button */
7332 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7333 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7334 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7335 G_CALLBACK(forward_cb
), t
);
7336 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7339 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7340 gtk_widget_set_sensitive(t
->gohome
, true);
7341 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7342 G_CALLBACK(home_cb
), t
);
7343 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7345 /* create widgets but don't use them */
7346 t
->uri_entry
= gtk_entry_new();
7347 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7348 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7349 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7355 create_toolbar(struct tab
*t
)
7357 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7359 b
= gtk_hbox_new(FALSE
, 0);
7361 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7364 /* backward button */
7365 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7366 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7367 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7368 G_CALLBACK(backward_cb
), t
);
7369 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7371 /* forward button */
7372 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7373 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7374 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7375 G_CALLBACK(forward_cb
), t
);
7376 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7380 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7381 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7382 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7383 G_CALLBACK(stop_cb
), t
);
7384 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7388 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7389 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7390 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7391 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7392 G_CALLBACK(js_toggle_cb
), t
);
7393 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7396 t
->uri_entry
= gtk_entry_new();
7397 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7398 G_CALLBACK(activate_uri_entry_cb
), t
);
7399 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7400 G_CALLBACK(entry_key_cb
), t
);
7402 eb1
= gtk_hbox_new(FALSE
, 0);
7403 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7404 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7405 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7408 if (fancy_bar
&& search_string
) {
7410 t
->search_entry
= gtk_entry_new();
7411 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7412 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7413 G_CALLBACK(activate_search_entry_cb
), t
);
7414 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7415 G_CALLBACK(entry_key_cb
), t
);
7416 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7417 eb2
= gtk_hbox_new(FALSE
, 0);
7418 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7419 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7421 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7427 create_buffers(struct tab
*t
)
7429 GtkCellRenderer
*renderer
;
7432 view
= gtk_tree_view_new();
7434 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7436 renderer
= gtk_cell_renderer_text_new();
7437 gtk_tree_view_insert_column_with_attributes
7438 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7440 renderer
= gtk_cell_renderer_text_new();
7441 gtk_tree_view_insert_column_with_attributes
7442 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
, NULL
);
7444 gtk_tree_view_set_model
7445 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7451 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7452 GtkTreeViewColumn
*col
, struct tab
*t
)
7457 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7459 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
, path
)) {
7461 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7462 set_current_tab(id
- 1);
7468 /* after tab reordering/creation/removal */
7475 TAILQ_FOREACH(t
, &tabs
, entry
) {
7476 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7477 if (t
->tab_id
> maxid
)
7480 gtk_widget_show(t
->tab_elems
.sep
);
7483 TAILQ_FOREACH(t
, &tabs
, entry
) {
7484 if (t
->tab_id
== maxid
) {
7485 gtk_widget_hide(t
->tab_elems
.sep
);
7491 /* after active tab change */
7493 recolor_compact_tabs(void)
7499 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7500 TAILQ_FOREACH(t
, &tabs
, entry
)
7501 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7503 curid
= gtk_notebook_get_current_page(notebook
);
7504 TAILQ_FOREACH(t
, &tabs
, entry
)
7505 if (t
->tab_id
== curid
) {
7506 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
7507 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7513 set_current_tab(int page_num
)
7515 gtk_notebook_set_current_page(notebook
, page_num
);
7516 recolor_compact_tabs();
7520 undo_close_tab_save(struct tab
*t
)
7524 struct undo
*u1
, *u2
;
7526 WebKitWebHistoryItem
*item
;
7528 if ((uri
= get_uri(t
)) == NULL
)
7531 u1
= g_malloc0(sizeof(struct undo
));
7532 u1
->uri
= g_strdup(uri
);
7534 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7536 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7537 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7540 /* forward history */
7541 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7545 u1
->history
= g_list_prepend(u1
->history
,
7546 webkit_web_history_item_copy(item
));
7547 items
= g_list_next(items
);
7552 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7553 u1
->history
= g_list_prepend(u1
->history
,
7554 webkit_web_history_item_copy(item
));
7558 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7562 u1
->history
= g_list_prepend(u1
->history
,
7563 webkit_web_history_item_copy(item
));
7564 items
= g_list_next(items
);
7567 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7569 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7570 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7571 TAILQ_REMOVE(&undos
, u2
, entry
);
7573 g_list_free(u2
->history
);
7582 delete_tab(struct tab
*t
)
7586 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7591 TAILQ_REMOVE(&tabs
, t
, entry
);
7593 /* Halt all webkit activity. */
7594 abort_favicon_download(t
);
7595 webkit_web_view_stop_loading(t
->wv
);
7597 /* Save the tab, so we can undo the close. */
7598 undo_close_tab_save(t
);
7600 if (browser_mode
== XT_BM_KIOSK
) {
7601 gtk_widget_destroy(t
->uri_entry
);
7602 gtk_widget_destroy(t
->stop
);
7603 gtk_widget_destroy(t
->js_toggle
);
7606 gtk_widget_destroy(t
->tab_elems
.eventbox
);
7607 gtk_widget_destroy(t
->vbox
);
7609 g_free(t
->user_agent
);
7610 g_free(t
->stylesheet
);
7613 if (TAILQ_EMPTY(&tabs
)) {
7614 if (browser_mode
== XT_BM_KIOSK
)
7615 create_new_tab(home
, NULL
, 1, -1);
7617 create_new_tab(NULL
, NULL
, 1, -1);
7620 /* recreate session */
7621 if (session_autosave
) {
7627 recolor_compact_tabs();
7631 adjustfont_webkit(struct tab
*t
, int adjust
)
7636 show_oops(NULL
, "adjustfont_webkit invalid parameters");
7640 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7641 if (adjust
== XT_FONT_SET
) {
7642 t
->font_size
= default_font_size
;
7643 zoom
= default_zoom_level
;
7644 t
->font_size
+= adjust
;
7645 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7646 t
->font_size
, (char *)NULL
);
7647 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7648 &t
->font_size
, (char *)NULL
);
7650 t
->font_size
+= adjust
;
7651 zoom
+= adjust
/25.0;
7656 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7657 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7661 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
7663 struct tab
*t
= (struct tab
*) data
;
7665 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
7667 switch (event
->button
) {
7669 set_current_tab(t
->tab_id
);
7680 append_tab(struct tab
*t
)
7685 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7686 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7690 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
7695 WebKitWebHistoryItem
*item
;
7699 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7701 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7702 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7706 t
= g_malloc0(sizeof *t
);
7708 if (title
== NULL
) {
7709 title
= "(untitled)";
7713 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7715 /* label + button for tab */
7716 b
= gtk_hbox_new(FALSE
, 0);
7719 #if GTK_CHECK_VERSION(2, 20, 0)
7720 t
->spinner
= gtk_spinner_new();
7722 t
->label
= gtk_label_new(title
);
7723 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7724 gtk_widget_set_size_request(t
->label
, 100, 0);
7725 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
7726 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
7727 gtk_widget_set_size_request(b
, 130, 0);
7729 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7730 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7731 #if GTK_CHECK_VERSION(2, 20, 0)
7732 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7736 if (browser_mode
== XT_BM_KIOSK
)
7737 t
->toolbar
= create_kiosk_toolbar(t
);
7739 t
->toolbar
= create_toolbar(t
);
7741 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7744 t
->browser_win
= create_browser(t
);
7745 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7747 /* oops message for user feedback */
7748 t
->oops
= gtk_entry_new();
7749 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7750 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7751 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7752 gdk_color_parse(XT_COLOR_RED
, &color
);
7753 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7754 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7755 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
7758 t
->cmd
= gtk_entry_new();
7759 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7760 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7761 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7762 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
7765 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
7767 t
->sbe
.statusbar
= gtk_entry_new();
7768 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
7769 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
7770 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
7771 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
7773 t
->sbe
.position
= gtk_entry_new();
7774 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.position
), NULL
);
7775 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.position
), FALSE
);
7776 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.position
), FALSE
);
7777 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.position
), statusbar_font
);
7778 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.position
), 1.0);
7779 gtk_widget_set_size_request(t
->sbe
.position
, 40, -1);
7781 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7782 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
7783 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
7784 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7785 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
7786 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
7788 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
7790 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.position
, FALSE
,
7793 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
7796 t
->buffers
= create_buffers(t
);
7797 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
7799 /* xtp meaning is normal by default */
7800 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7802 /* set empty favicon */
7803 xt_icon_from_name(t
, "text-html");
7805 /* and show it all */
7806 gtk_widget_show_all(b
);
7807 gtk_widget_show_all(t
->vbox
);
7809 /* compact tab bar */
7810 t
->tab_elems
.label
= gtk_label_new(title
);
7811 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
7812 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
7813 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
7814 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
7816 t
->tab_elems
.eventbox
= gtk_event_box_new();
7817 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
7818 t
->tab_elems
.sep
= gtk_vseparator_new();
7820 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
7821 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
7822 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7823 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7824 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
7825 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
7827 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
7829 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
7831 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
7834 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
7836 gtk_widget_show_all(t
->tab_elems
.eventbox
);
7838 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7841 id
= position
>= 0 ? position
: gtk_notebook_get_current_page(notebook
) + 1;
7842 if (id
> gtk_notebook_get_n_pages(notebook
))
7845 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7846 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
7847 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, id
);
7852 #if GTK_CHECK_VERSION(2, 20, 0)
7853 /* turn spinner off if we are a new tab without uri */
7855 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7856 gtk_widget_hide(t
->spinner
);
7859 /* make notebook tabs reorderable */
7860 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7862 /* compact tabs clickable */
7863 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
7864 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
7866 g_object_connect(G_OBJECT(t
->cmd
),
7867 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7868 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7869 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7870 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7873 /* reuse wv_button_cb to hide oops */
7874 g_object_connect(G_OBJECT(t
->oops
),
7875 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7878 g_signal_connect(t
->buffers
,
7879 "row-activated", G_CALLBACK(row_activated_cb
), t
);
7880 g_object_connect(G_OBJECT(t
->buffers
),
7881 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
7883 g_object_connect(G_OBJECT(t
->wv
),
7884 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7885 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7886 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7887 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7888 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7889 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7890 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7891 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7892 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7893 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7894 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7895 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7896 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7897 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7898 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
7900 g_signal_connect(t
->wv
,
7901 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7902 g_signal_connect(t
->wv
,
7903 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7905 /* hijack the unused keys as if we were the browser */
7906 g_object_connect(G_OBJECT(t
->toolbar
),
7907 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7910 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7911 G_CALLBACK(tab_close_cb
), t
);
7917 url_set_visibility();
7918 statusbar_set_visibility();
7921 set_current_tab(t
->tab_id
);
7922 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7927 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7931 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7936 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7937 /* restore the tab's history */
7938 if (u
&& u
->history
) {
7942 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7943 items
= g_list_next(items
);
7946 item
= g_list_nth_data(u
->history
, u
->back
);
7948 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7951 g_list_free(u
->history
);
7953 webkit_web_back_forward_list_clear(t
->bfl
);
7955 recolor_compact_tabs();
7960 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7966 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7968 if (gtk_notebook_get_current_page(notebook
) == -1)
7971 TAILQ_FOREACH(t
, &tabs
, entry
) {
7972 if (t
->tab_id
== pn
) {
7973 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7976 uri
= get_title(t
, TRUE
);
7977 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7983 /* can't use focus_webview here */
7984 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7991 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7994 struct tab
*t
= NULL
, *tt
;
7998 TAILQ_FOREACH(tt
, &tabs
, entry
)
7999 if (tt
->tab_id
== pn
) {
8004 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
8006 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
8011 menuitem_response(struct tab
*t
)
8013 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
8017 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
8019 GtkWidget
*menu
, *menu_items
;
8020 GdkEventButton
*bevent
;
8024 if (event
->type
== GDK_BUTTON_PRESS
) {
8025 bevent
= (GdkEventButton
*) event
;
8026 menu
= gtk_menu_new();
8028 TAILQ_FOREACH(ti
, &tabs
, entry
) {
8029 if ((uri
= get_uri(ti
)) == NULL
)
8030 /* XXX make sure there is something to print */
8031 /* XXX add gui pages in here to look purdy */
8033 menu_items
= gtk_menu_item_new_with_label(uri
);
8034 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
8035 gtk_widget_show(menu_items
);
8037 g_signal_connect_swapped((menu_items
),
8038 "activate", G_CALLBACK(menuitem_response
),
8042 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
8043 bevent
->button
, bevent
->time
);
8045 /* unref object so it'll free itself when popped down */
8046 #if !GTK_CHECK_VERSION(3, 0, 0)
8047 /* XXX does not need unref with gtk+3? */
8048 g_object_ref_sink(menu
);
8049 g_object_unref(menu
);
8052 return (TRUE
/* eat event */);
8055 return (FALSE
/* propagate */);
8059 icon_size_map(int icon_size
)
8061 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
8062 icon_size
> GTK_ICON_SIZE_DIALOG
)
8063 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
8069 create_button(char *name
, char *stockid
, int size
)
8071 GtkWidget
*button
, *image
;
8075 rcstring
= g_strdup_printf(
8076 "style \"%s-style\"\n"
8078 " GtkWidget::focus-padding = 0\n"
8079 " GtkWidget::focus-line-width = 0\n"
8083 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8084 gtk_rc_parse_string(rcstring
);
8086 button
= gtk_button_new();
8087 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8088 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8090 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8091 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8092 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8093 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8094 gtk_widget_set_name(button
, name
);
8095 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8101 button_set_stockid(GtkWidget
*button
, char *stockid
)
8105 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8106 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8107 gtk_button_set_image(GTK_BUTTON(button
), image
);
8111 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8113 GtkClipboard
*clipboard
;
8114 gchar
*p
= NULL
, *s
= NULL
;
8117 * This code is very aggressive!
8118 * It basically ensures that the primary and regular clipboard are
8119 * always set the same. This obviously messes with standard X protocol
8120 * but those clowns should have come up with something better.
8126 clipboard
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
8127 p
= gtk_clipboard_wait_for_text(primary
);
8129 DNPRINTF(XT_D_CLIP
, "primary cleaned\n");
8130 p
= gtk_clipboard_wait_for_text(clipboard
);
8132 gtk_clipboard_set_text(primary
, p
, -1);
8134 DNPRINTF(XT_D_CLIP
, "primary got selection\n");
8135 s
= gtk_clipboard_wait_for_text(clipboard
);
8138 * if s and p are the same the string was set by
8139 * clipb_clipboard_cb so do nothing in that case
8140 * to prevent endless loop
8145 gtk_clipboard_set_text(clipboard
, p
, -1);
8155 clipb_clipboard_cb(GtkClipboard
*clipboard
, GdkEvent
*event
, gpointer notused
)
8157 GtkClipboard
*primary
;
8158 gchar
*p
= NULL
, *s
= NULL
;
8163 DNPRINTF(XT_D_CLIP
, "clipboard got content\n");
8165 primary
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8166 p
= gtk_clipboard_wait_for_text(clipboard
);
8168 s
= gtk_clipboard_wait_for_text(primary
);
8171 * if s and p are the same the string was set by
8172 * clipb_primary_cb so do nothing in that case
8173 * to prevent endless loop and deselection of text
8178 gtk_clipboard_set_text(primary
, p
, -1);
8193 char file
[PATH_MAX
];
8196 vbox
= gtk_vbox_new(FALSE
, 0);
8197 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8198 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8199 #if !GTK_CHECK_VERSION(3, 0, 0)
8200 /* XXX seems to be needed with gtk+2 */
8201 gtk_notebook_set_tab_hborder(notebook
, 0);
8202 gtk_notebook_set_tab_vborder(notebook
, 0);
8204 gtk_notebook_set_scrollable(notebook
, TRUE
);
8205 gtk_notebook_set_show_border(notebook
, FALSE
);
8206 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8208 abtn
= gtk_button_new();
8209 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8210 gtk_widget_set_size_request(arrow
, -1, -1);
8211 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8212 gtk_widget_set_size_request(abtn
, -1, 20);
8214 #if GTK_CHECK_VERSION(2, 20, 0)
8215 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8217 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8219 /* compact tab bar */
8220 tab_bar
= gtk_hbox_new(TRUE
, 0);
8222 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8223 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8224 gtk_widget_set_size_request(vbox
, -1, -1);
8226 g_object_connect(G_OBJECT(notebook
),
8227 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8229 g_object_connect(G_OBJECT(notebook
),
8230 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
), NULL
,
8232 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8233 G_CALLBACK(arrow_cb
), NULL
);
8235 main_window
= create_window();
8236 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8239 for (i
= 0; i
< LENGTH(icons
); i
++) {
8240 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8241 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8242 l
= g_list_append(l
, pb
);
8244 gtk_window_set_default_icon_list(l
);
8247 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8248 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8249 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
)),
8250 "owner-change", G_CALLBACK(clipb_clipboard_cb
), NULL
);
8252 gtk_widget_show_all(abtn
);
8253 gtk_widget_show_all(main_window
);
8254 notebook_tab_set_visibility();
8258 set_hook(void **hook
, char *name
)
8261 errx(1, "set_hook");
8263 if (*hook
== NULL
) {
8264 *hook
= dlsym(RTLD_NEXT
, name
);
8266 errx(1, "can't hook %s", name
);
8270 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8272 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8274 g_return_val_if_fail(cookie1
, FALSE
);
8275 g_return_val_if_fail(cookie2
, FALSE
);
8277 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8278 !strcmp (cookie1
->value
, cookie2
->value
) &&
8279 !strcmp (cookie1
->path
, cookie2
->path
) &&
8280 !strcmp (cookie1
->domain
, cookie2
->domain
));
8284 transfer_cookies(void)
8287 SoupCookie
*sc
, *pc
;
8289 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8291 for (;cf
; cf
= cf
->next
) {
8293 sc
= soup_cookie_copy(pc
);
8294 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8297 soup_cookies_free(cf
);
8301 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8306 print_cookie("soup_cookie_jar_delete_cookie", c
);
8308 if (cookies_enabled
== 0)
8311 if (jar
== NULL
|| c
== NULL
)
8314 /* find and remove from persistent jar */
8315 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8317 for (;cf
; cf
= cf
->next
) {
8319 if (soup_cookie_equal(ci
, c
)) {
8320 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8325 soup_cookies_free(cf
);
8327 /* delete from session jar */
8328 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8332 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8334 struct domain
*d
= NULL
;
8338 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8339 jar
, p_cookiejar
, s_cookiejar
);
8341 if (cookies_enabled
== 0)
8344 /* see if we are up and running */
8345 if (p_cookiejar
== NULL
) {
8346 _soup_cookie_jar_add_cookie(jar
, cookie
);
8349 /* disallow p_cookiejar adds, shouldn't happen */
8350 if (jar
== p_cookiejar
)
8354 if (jar
== NULL
|| cookie
== NULL
)
8357 if (enable_cookie_whitelist
&&
8358 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8360 DNPRINTF(XT_D_COOKIE
,
8361 "soup_cookie_jar_add_cookie: reject %s\n",
8363 if (save_rejected_cookies
) {
8364 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8365 show_oops(NULL
, "can't open reject cookie file");
8368 fseek(r_cookie_f
, 0, SEEK_END
);
8369 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8370 cookie
->http_only
? "#HttpOnly_" : "",
8372 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8374 cookie
->secure
? "TRUE" : "FALSE",
8376 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8383 if (!allow_volatile_cookies
)
8387 if (cookie
->expires
== NULL
&& session_timeout
) {
8388 soup_cookie_set_expires(cookie
,
8389 soup_date_new_from_now(session_timeout
));
8390 print_cookie("modified add cookie", cookie
);
8393 /* see if we are white listed for persistence */
8394 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8395 /* add to persistent jar */
8396 c
= soup_cookie_copy(cookie
);
8397 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8398 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8401 /* add to session jar */
8402 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8403 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8409 char file
[PATH_MAX
];
8411 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8412 "soup_cookie_jar_add_cookie");
8413 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8414 "soup_cookie_jar_delete_cookie");
8416 if (cookies_enabled
== 0)
8420 * the following code is intricate due to overriding several libsoup
8422 * do not alter order of these operations.
8425 /* rejected cookies */
8426 if (save_rejected_cookies
)
8427 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
, XT_REJECT_FILE
);
8429 /* persistent cookies */
8430 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8431 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8433 /* session cookies */
8434 s_cookiejar
= soup_cookie_jar_new();
8435 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8436 cookie_policy
, (void *)NULL
);
8439 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8443 setup_proxy(char *uri
)
8446 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8447 soup_uri_free(proxy_uri
);
8451 if (http_proxy
!= uri
) {
8458 http_proxy
= g_strdup(uri
);
8459 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8460 proxy_uri
= soup_uri_new(http_proxy
);
8461 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
8466 send_cmd_to_socket(char *cmd
)
8469 struct sockaddr_un sa
;
8471 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8472 warnx("%s: socket", __func__
);
8476 sa
.sun_family
= AF_UNIX
;
8477 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8478 work_dir
, XT_SOCKET_FILE
);
8481 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8482 warnx("%s: connect", __func__
);
8486 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
8487 warnx("%s: send", __func__
);
8498 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
8501 char str
[XT_MAX_URL_LENGTH
];
8502 socklen_t t
= sizeof(struct sockaddr_un
);
8503 struct sockaddr_un sa
;
8508 gint fd
= g_io_channel_unix_get_fd(source
);
8510 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
8515 if (getpeereid(s
, &uid
, &gid
) == -1) {
8519 if (uid
!= getuid() || gid
!= getgid()) {
8520 warnx("unauthorized user");
8526 warnx("not a valid user");
8530 n
= recv(s
, str
, sizeof(str
), 0);
8534 tt
= TAILQ_LAST(&tabs
, tab_list
);
8535 cmd_execute(tt
, str
);
8543 struct sockaddr_un sa
;
8545 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8546 warn("is_running: socket");
8550 sa
.sun_family
= AF_UNIX
;
8551 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8552 work_dir
, XT_SOCKET_FILE
);
8555 /* connect to see if there is a listener */
8556 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
8557 rv
= 0; /* not running */
8559 rv
= 1; /* already running */
8570 struct sockaddr_un sa
;
8572 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8573 warn("build_socket: socket");
8577 sa
.sun_family
= AF_UNIX
;
8578 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8579 work_dir
, XT_SOCKET_FILE
);
8582 /* connect to see if there is a listener */
8583 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8584 /* no listener so we will */
8585 unlink(sa
.sun_path
);
8587 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8588 warn("build_socket: bind");
8592 if (listen(s
, 1) == -1) {
8593 warn("build_socket: listen");
8606 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8607 GtkTreeIter
*iter
, struct tab
*t
)
8611 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8619 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8620 GtkTreeIter
*iter
, struct tab
*t
)
8624 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8625 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
8626 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
8633 completion_add_uri(const gchar
*uri
)
8637 /* add uri to list_store */
8638 gtk_list_store_append(completion_model
, &iter
);
8639 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8643 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8644 GtkTreeIter
*iter
, gpointer user_data
)
8647 gboolean match
= FALSE
;
8649 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8655 match
= match_uri(value
, key
);
8662 completion_add(struct tab
*t
)
8664 /* enable completion for tab */
8665 t
->completion
= gtk_entry_completion_new();
8666 gtk_entry_completion_set_text_column(t
->completion
, 0);
8667 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8668 gtk_entry_completion_set_model(t
->completion
,
8669 GTK_TREE_MODEL(completion_model
));
8670 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8672 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8673 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
8674 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8675 G_CALLBACK(completion_select_cb
), t
);
8676 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
8677 G_CALLBACK(completion_hover_cb
), t
);
8685 if (stat(dir
, &sb
)) {
8686 if (mkdir(dir
, S_IRWXU
) == -1)
8687 err(1, "mkdir %s", dir
);
8689 err(1, "stat %s", dir
);
8691 if (S_ISDIR(sb
.st_mode
) == 0)
8692 errx(1, "%s not a dir", dir
);
8693 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8694 warnx("fixing invalid permissions on %s", dir
);
8695 if (chmod(dir
, S_IRWXU
) == -1)
8696 err(1, "chmod %s", dir
);
8704 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8710 main(int argc
, char *argv
[])
8713 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8714 char conf
[PATH_MAX
] = { '\0' };
8715 char file
[PATH_MAX
];
8716 char *env_proxy
= NULL
;
8719 struct sigaction sact
;
8720 GIOChannel
*channel
;
8725 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8727 /* fiddle with ulimits */
8728 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8731 /* just use them all */
8732 rlp
.rlim_cur
= rlp
.rlim_max
;
8733 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8735 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8737 else if (rlp
.rlim_cur
<= 256)
8738 warnx("%s requires at least 256 file descriptors",
8742 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8751 errx(0 , "Version: %s", version
);
8754 strlcpy(conf
, optarg
, sizeof(conf
));
8757 strlcpy(named_session
, optarg
, sizeof(named_session
));
8778 RB_INIT(&downloads
);
8782 TAILQ_INIT(&aliases
);
8788 gnutls_global_init();
8790 /* generate session keys for xtp pages */
8791 generate_xtp_session_key(&dl_session_key
);
8792 generate_xtp_session_key(&hl_session_key
);
8793 generate_xtp_session_key(&cl_session_key
);
8794 generate_xtp_session_key(&fl_session_key
);
8797 gtk_init(&argc
, &argv
);
8798 if (!g_thread_supported())
8799 g_thread_init(NULL
);
8802 bzero(&sact
, sizeof(sact
));
8803 sigemptyset(&sact
.sa_mask
);
8804 sact
.sa_handler
= sigchild
;
8805 sact
.sa_flags
= SA_NOCLDSTOP
;
8806 sigaction(SIGCHLD
, &sact
, NULL
);
8808 /* set download dir */
8809 pwd
= getpwuid(getuid());
8811 errx(1, "invalid user %d", getuid());
8812 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8814 /* set default string settings */
8815 home
= g_strdup("https://www.cyphertite.com");
8816 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8817 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8818 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
8819 cmd_font_name
= g_strdup("monospace normal 9");
8820 oops_font_name
= g_strdup("monospace normal 9");
8821 statusbar_font_name
= g_strdup("monospace normal 9");
8822 tabbar_font_name
= g_strdup("monospace normal 9");
8825 /* read config file */
8826 if (strlen(conf
) == 0)
8827 snprintf(conf
, sizeof conf
, "%s/.%s",
8828 pwd
->pw_dir
, XT_CONF_FILE
);
8829 config_parse(conf
, 0);
8832 cmd_font
= pango_font_description_from_string(cmd_font_name
);
8833 oops_font
= pango_font_description_from_string(oops_font_name
);
8834 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
8835 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
8837 /* working directory */
8838 if (strlen(work_dir
) == 0)
8839 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8840 pwd
->pw_dir
, XT_DIR
);
8843 /* icon cache dir */
8844 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8848 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8852 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8853 work_dir
, XT_SESSIONS_DIR
);
8854 xxx_dir(sessions_dir
);
8856 /* runtime settings that can override config file */
8857 if (runtime_settings
[0] != '\0')
8858 config_parse(runtime_settings
, 1);
8861 if (!strcmp(download_dir
, pwd
->pw_dir
))
8862 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8863 xxx_dir(download_dir
);
8865 /* favorites file */
8866 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8867 if (stat(file
, &sb
)) {
8868 warnx("favorites file doesn't exist, creating it");
8869 if ((f
= fopen(file
, "w")) == NULL
)
8870 err(1, "favorites");
8875 session
= webkit_get_default_session();
8880 if (stat(ssl_ca_file
, &sb
)) {
8881 warnx("no CA file: %s", ssl_ca_file
);
8882 g_free(ssl_ca_file
);
8885 g_object_set(session
,
8886 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8887 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8892 env_proxy
= getenv("http_proxy");
8894 setup_proxy(env_proxy
);
8896 setup_proxy(http_proxy
);
8899 send_cmd_to_socket(argv
[0]);
8903 /* set some connection parameters */
8904 /* XXX webkit 1.4.X overwrites these values! */
8905 /* https://bugs.webkit.org/show_bug.cgi?id=64355 */
8906 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8907 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8910 /* see if there is already an xxxterm running */
8911 if (single_instance
&& is_running()) {
8913 warnx("already running");
8919 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8920 send_cmd_to_socket(cmd
);
8930 /* uri completion */
8931 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8934 buffers_store
= gtk_list_store_new
8935 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
8939 notebook_tab_set_visibility();
8941 if (save_global_history
)
8942 restore_global_history();
8944 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8945 restore_saved_tabs();
8947 a
.s
= named_session
;
8948 a
.i
= XT_SES_DONOTHING
;
8949 open_tabs(NULL
, &a
);
8953 create_new_tab(argv
[0], NULL
, focus
, -1);
8960 if (TAILQ_EMPTY(&tabs
))
8961 create_new_tab(home
, NULL
, 1, -1);
8964 if ((s
= build_socket()) != -1) {
8965 channel
= g_io_channel_unix_new(s
);
8966 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
8971 gnutls_global_deinit();