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 *statusbar_font_name
= NULL
;
544 PangoFontDescription
*cmd_font
;
545 PangoFontDescription
*statusbar_font
;
547 int btn_down
; /* M1 down in any wv */
551 int set_browser_mode(struct settings
*, char *);
552 int set_cookie_policy(struct settings
*, char *);
553 int set_download_dir(struct settings
*, char *);
554 int set_runtime_dir(struct settings
*, char *);
555 int set_tab_style(struct settings
*, char *);
556 int set_work_dir(struct settings
*, char *);
557 int add_alias(struct settings
*, char *);
558 int add_mime_type(struct settings
*, char *);
559 int add_cookie_wl(struct settings
*, char *);
560 int add_js_wl(struct settings
*, char *);
561 int add_kb(struct settings
*, char *);
562 void button_set_stockid(GtkWidget
*, char *);
563 GtkWidget
* create_button(char *, char *, int);
565 char *get_browser_mode(struct settings
*);
566 char *get_cookie_policy(struct settings
*);
567 char *get_download_dir(struct settings
*);
568 char *get_runtime_dir(struct settings
*);
569 char *get_tab_style(struct settings
*);
570 char *get_work_dir(struct settings
*);
572 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
573 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
574 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
575 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
576 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
578 void recalc_tabs(void);
579 void recolor_compact_tabs(void);
580 void set_current_tab(int page_num
);
581 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
584 int (*set
)(struct settings
*, char *);
585 char *(*get
)(struct settings
*);
586 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
589 struct special s_browser_mode
= {
595 struct special s_cookie
= {
601 struct special s_alias
= {
607 struct special s_mime
= {
613 struct special s_js
= {
619 struct special s_kb
= {
625 struct special s_cookie_wl
= {
631 struct special s_download_dir
= {
637 struct special s_work_dir
= {
643 struct special s_tab_style
= {
652 #define XT_S_INVALID (0)
655 #define XT_S_FLOAT (3)
657 #define XT_SF_RESTART (1<<0)
658 #define XT_SF_RUNTIME (1<<1)
664 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
665 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
666 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
667 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
668 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
669 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
670 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
671 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
672 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
673 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
674 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
675 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
676 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
677 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
678 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
679 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
680 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
681 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
682 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
683 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
684 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
685 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
686 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
687 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
688 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
689 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
690 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
691 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
692 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
693 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
694 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
695 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
696 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
697 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
698 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
699 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
700 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
701 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
702 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
703 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
704 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
705 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
706 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
709 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
710 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
712 /* runtime settings */
713 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
714 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
715 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
716 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
717 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
720 int about(struct tab
*, struct karg
*);
721 int blank(struct tab
*, struct karg
*);
722 int ca_cmd(struct tab
*, struct karg
*);
723 int cookie_show_wl(struct tab
*, struct karg
*);
724 int js_show_wl(struct tab
*, struct karg
*);
725 int help(struct tab
*, struct karg
*);
726 int set(struct tab
*, struct karg
*);
727 int stats(struct tab
*, struct karg
*);
728 int marco(struct tab
*, struct karg
*);
729 const char * marco_message(int *);
730 int xtp_page_cl(struct tab
*, struct karg
*);
731 int xtp_page_dl(struct tab
*, struct karg
*);
732 int xtp_page_fl(struct tab
*, struct karg
*);
733 int xtp_page_hl(struct tab
*, struct karg
*);
734 void xt_icon_from_file(struct tab
*, char *);
735 const gchar
*get_uri(struct tab
*);
736 const gchar
*get_title(struct tab
*);
738 #define XT_URI_ABOUT ("about:")
739 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
740 #define XT_URI_ABOUT_ABOUT ("about")
741 #define XT_URI_ABOUT_BLANK ("blank")
742 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
743 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
744 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
745 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
746 #define XT_URI_ABOUT_FAVORITES ("favorites")
747 #define XT_URI_ABOUT_HELP ("help")
748 #define XT_URI_ABOUT_HISTORY ("history")
749 #define XT_URI_ABOUT_JSWL ("jswl")
750 #define XT_URI_ABOUT_SET ("set")
751 #define XT_URI_ABOUT_STATS ("stats")
752 #define XT_URI_ABOUT_MARCO ("marco")
756 int (*func
)(struct tab
*, struct karg
*);
758 { XT_URI_ABOUT_ABOUT
, about
},
759 { XT_URI_ABOUT_BLANK
, blank
},
760 { XT_URI_ABOUT_CERTS
, ca_cmd
},
761 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
762 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
763 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
764 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
765 { XT_URI_ABOUT_HELP
, help
},
766 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
767 { XT_URI_ABOUT_JSWL
, js_show_wl
},
768 { XT_URI_ABOUT_SET
, set
},
769 { XT_URI_ABOUT_STATS
, stats
},
770 { XT_URI_ABOUT_MARCO
, marco
},
773 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
774 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
775 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
776 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
777 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
778 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
779 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
782 extern char *__progname
;
785 GtkWidget
*main_window
;
786 GtkNotebook
*notebook
;
788 GtkWidget
*arrow
, *abtn
;
789 struct tab_list tabs
;
790 struct history_list hl
;
791 struct download_list downloads
;
792 struct domain_list c_wl
;
793 struct domain_list js_wl
;
794 struct undo_tailq undos
;
795 struct keybinding_list kbl
;
797 int updating_dl_tabs
= 0;
798 int updating_hl_tabs
= 0;
799 int updating_cl_tabs
= 0;
800 int updating_fl_tabs
= 0;
802 uint64_t blocked_cookies
= 0;
803 char named_session
[PATH_MAX
];
804 int icon_size_map(int);
806 GtkListStore
*completion_model
;
807 void completion_add(struct tab
*);
808 void completion_add_uri(const gchar
*);
809 GtkListStore
*buffers_store
;
810 void xxx_dir(char *);
815 int saved_errno
, status
;
820 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
824 if (errno
!= ECHILD
) {
826 clog_warn("sigchild: waitpid:");
832 if (WIFEXITED(status
)) {
833 if (WEXITSTATUS(status
) != 0) {
835 clog_warnx("sigchild: child exit status: %d",
836 WEXITSTATUS(status));
841 clog_warnx("sigchild: child is terminated abnormally");
850 is_g_object_setting(GObject
*o
, char *str
)
852 guint n_props
= 0, i
;
853 GParamSpec
**proplist
;
855 if (! G_IS_OBJECT(o
))
858 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
861 for (i
=0; i
< n_props
; i
++) {
862 if (! strcmp(proplist
[i
]->name
, str
))
869 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
873 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
875 "<title>%s</title>\n"
884 addstyles
? XT_PAGE_STYLE
: "",
893 * Display a web page from a HTML string in memory, rather than from a URL
896 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
901 /* we set this to indicate we want to manually do navaction */
903 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
905 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
907 /* set t->xtp_meaning */
908 for (i
= 0; i
< LENGTH(about_list
); i
++)
909 if (!strcmp(title
, about_list
[i
].name
)) {
914 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
915 #if GTK_CHECK_VERSION(2, 20, 0)
916 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
917 gtk_widget_hide(t
->spinner
);
919 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
920 xt_icon_from_file(t
, file
);
925 get_current_tab(void)
929 TAILQ_FOREACH(t
, &tabs
, entry
) {
930 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
934 warnx("%s: no current tab", __func__
);
940 set_status(struct tab
*t
, gchar
*s
, int status
)
948 case XT_STATUS_LOADING
:
949 type
= g_strdup_printf("Loading: %s", s
);
953 type
= g_strdup_printf("Link: %s", s
);
955 t
->status
= g_strdup(gtk_entry_get_text(
956 GTK_ENTRY(t
->sbe
.statusbar
)));
960 type
= g_strdup_printf("%s", s
);
962 t
->status
= g_strdup(type
);
966 t
->status
= g_strdup(s
);
968 case XT_STATUS_NOTHING
:
973 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
979 hide_cmd(struct tab
*t
)
981 gtk_widget_hide(t
->cmd
);
985 show_cmd(struct tab
*t
)
987 gtk_widget_hide(t
->oops
);
988 gtk_widget_show(t
->cmd
);
992 hide_buffers(struct tab
*t
)
994 gtk_widget_hide(t
->buffers
);
995 gtk_list_store_clear(buffers_store
);
1005 sort_tabs_by_page_num(struct tab
***stabs
)
1010 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1012 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1014 TAILQ_FOREACH(t
, &tabs
, entry
)
1015 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1021 buffers_make_list(void)
1024 const gchar
*title
= NULL
;
1026 struct tab
**stabs
= NULL
;
1028 num_tabs
= sort_tabs_by_page_num(&stabs
);
1030 for (i
= 0; i
< num_tabs
; i
++)
1032 gtk_list_store_append(buffers_store
, &iter
);
1033 title
= get_title(stabs
[i
]);
1034 gtk_list_store_set(buffers_store
, &iter
,
1035 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1045 show_buffers(struct tab
*t
)
1047 buffers_make_list();
1048 gtk_widget_show(t
->buffers
);
1049 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1053 toggle_buffers(struct tab
*t
)
1055 if (gtk_widget_get_visible(t
->buffers
))
1062 buffers(struct tab
*t
, struct karg
*args
)
1070 hide_oops(struct tab
*t
)
1072 gtk_widget_hide(t
->oops
);
1076 show_oops(struct tab
*at
, const char *fmt
, ...)
1080 struct tab
*t
= NULL
;
1086 if ((t
= get_current_tab()) == NULL
)
1092 if (vasprintf(&msg
, fmt
, ap
) == -1)
1093 errx(1, "show_oops failed");
1096 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1097 gtk_widget_hide(t
->cmd
);
1098 gtk_widget_show(t
->oops
);
1102 get_as_string(struct settings
*s
)
1113 warnx("get_as_string skip %s\n", s
->name
);
1114 } else if (s
->type
== XT_S_INT
)
1115 r
= g_strdup_printf("%d", *s
->ival
);
1116 else if (s
->type
== XT_S_STR
)
1117 r
= g_strdup(*s
->sval
);
1118 else if (s
->type
== XT_S_FLOAT
)
1119 r
= g_strdup_printf("%f", *s
->fval
);
1121 r
= g_strdup_printf("INVALID TYPE");
1127 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1132 for (i
= 0; i
< LENGTH(rs
); i
++) {
1133 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1134 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1136 s
= get_as_string(&rs
[i
]);
1137 cb(&rs
[i
], s
, cb_args
);
1144 set_browser_mode(struct settings
*s
, char *val
)
1146 if (!strcmp(val
, "whitelist")) {
1147 browser_mode
= XT_BM_WHITELIST
;
1148 allow_volatile_cookies
= 0;
1149 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1150 cookies_enabled
= 1;
1151 enable_cookie_whitelist
= 1;
1152 read_only_cookies
= 0;
1153 save_rejected_cookies
= 0;
1154 session_timeout
= 3600;
1156 enable_js_whitelist
= 1;
1157 enable_localstorage
= 0;
1158 } else if (!strcmp(val
, "normal")) {
1159 browser_mode
= XT_BM_NORMAL
;
1160 allow_volatile_cookies
= 0;
1161 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1162 cookies_enabled
= 1;
1163 enable_cookie_whitelist
= 0;
1164 read_only_cookies
= 0;
1165 save_rejected_cookies
= 0;
1166 session_timeout
= 3600;
1168 enable_js_whitelist
= 0;
1169 enable_localstorage
= 1;
1170 } else if (!strcmp(val
, "kiosk")) {
1171 browser_mode
= XT_BM_KIOSK
;
1172 allow_volatile_cookies
= 0;
1173 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1174 cookies_enabled
= 1;
1175 enable_cookie_whitelist
= 0;
1176 read_only_cookies
= 0;
1177 save_rejected_cookies
= 0;
1178 session_timeout
= 3600;
1180 enable_js_whitelist
= 0;
1181 enable_localstorage
= 1;
1191 get_browser_mode(struct settings
*s
)
1195 if (browser_mode
== XT_BM_WHITELIST
)
1196 r
= g_strdup("whitelist");
1197 else if (browser_mode
== XT_BM_NORMAL
)
1198 r
= g_strdup("normal");
1199 else if (browser_mode
== XT_BM_KIOSK
)
1200 r
= g_strdup("kiosk");
1208 set_cookie_policy(struct settings
*s
, char *val
)
1210 if (!strcmp(val
, "no3rdparty"))
1211 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1212 else if (!strcmp(val
, "accept"))
1213 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1214 else if (!strcmp(val
, "reject"))
1215 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1223 get_cookie_policy(struct settings
*s
)
1227 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1228 r
= g_strdup("no3rdparty");
1229 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1230 r
= g_strdup("accept");
1231 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1232 r
= g_strdup("reject");
1240 get_download_dir(struct settings
*s
)
1242 if (download_dir
[0] == '\0')
1244 return (g_strdup(download_dir
));
1248 set_download_dir(struct settings
*s
, char *val
)
1251 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1252 pwd
->pw_dir
, &val
[1]);
1254 strlcpy(download_dir
, val
, sizeof download_dir
);
1261 * We use these to prevent people putting xxxt:// URLs on
1262 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1264 #define XT_XTP_SES_KEY_SZ 8
1265 #define XT_XTP_SES_KEY_HEX_FMT \
1266 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1267 char *dl_session_key
; /* downloads */
1268 char *hl_session_key
; /* history list */
1269 char *cl_session_key
; /* cookie list */
1270 char *fl_session_key
; /* favorites list */
1272 char work_dir
[PATH_MAX
];
1273 char certs_dir
[PATH_MAX
];
1274 char cache_dir
[PATH_MAX
];
1275 char sessions_dir
[PATH_MAX
];
1276 char cookie_file
[PATH_MAX
];
1277 SoupURI
*proxy_uri
= NULL
;
1278 SoupSession
*session
;
1279 SoupCookieJar
*s_cookiejar
;
1280 SoupCookieJar
*p_cookiejar
;
1281 char rc_fname
[PATH_MAX
];
1283 struct mime_type_list mtl
;
1284 struct alias_list aliases
;
1287 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1288 void delete_tab(struct tab
*);
1289 void adjustfont_webkit(struct tab
*, int);
1290 int run_script(struct tab
*, char *);
1291 int download_rb_cmp(struct download
*, struct download
*);
1292 gboolean
cmd_execute(struct tab
*t
, char *str
);
1295 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1297 return (strcmp(h1
->uri
, h2
->uri
));
1299 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1302 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1304 return (strcmp(d1
->d
, d2
->d
));
1306 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1309 get_work_dir(struct settings
*s
)
1311 if (work_dir
[0] == '\0')
1313 return (g_strdup(work_dir
));
1317 set_work_dir(struct settings
*s
, char *val
)
1320 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1321 pwd
->pw_dir
, &val
[1]);
1323 strlcpy(work_dir
, val
, sizeof work_dir
);
1329 get_tab_style(struct settings
*s
)
1331 if (tab_style
== XT_TABS_NORMAL
)
1332 return (g_strdup("normal"));
1334 return (g_strdup("compact"));
1338 set_tab_style(struct settings
*s
, char *val
)
1340 if (!strcmp(val
, "normal"))
1341 tab_style
= XT_TABS_NORMAL
;
1342 else if (!strcmp(val
, "compact"))
1343 tab_style
= XT_TABS_COMPACT
;
1351 * generate a session key to secure xtp commands.
1352 * pass in a ptr to the key in question and it will
1353 * be modified in place.
1356 generate_xtp_session_key(char **key
)
1358 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1364 /* make a new one */
1365 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1366 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1367 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1368 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1370 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1374 * validate a xtp session key.
1378 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1380 if (strcmp(trusted
, untrusted
) != 0) {
1381 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1390 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1392 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1394 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1396 struct valid_url_types
{
1407 valid_url_type(char *url
)
1411 for (i
= 0; i
< LENGTH(vut
); i
++)
1412 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1419 print_cookie(char *msg
, SoupCookie
*c
)
1425 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1426 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1427 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1428 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1429 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1430 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1431 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1432 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1433 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1434 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1438 walk_alias(struct settings
*s
,
1439 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1444 if (s
== NULL
|| cb
== NULL
) {
1445 show_oops(NULL
, "walk_alias invalid parameters");
1449 TAILQ_FOREACH(a
, &aliases
, entry
) {
1450 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1451 cb(s
, str
, cb_args
);
1457 match_alias(char *url_in
)
1461 char *url_out
= NULL
, *search
, *enc_arg
;
1463 search
= g_strdup(url_in
);
1465 if (strsep(&arg
, " \t") == NULL
) {
1466 show_oops(NULL
, "match_alias: NULL URL");
1470 TAILQ_FOREACH(a
, &aliases
, entry
) {
1471 if (!strcmp(search
, a
->a_name
))
1476 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1479 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1480 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1483 url_out
= g_strdup_printf(a
->a_uri
, "");
1491 guess_url_type(char *url_in
)
1494 char *url_out
= NULL
, *enc_search
= NULL
;
1496 url_out
= match_alias(url_in
);
1497 if (url_out
!= NULL
)
1502 * If there is no dot nor slash in the string and it isn't a
1503 * path to a local file and doesn't resolves to an IP, assume
1504 * that the user wants to search for the string.
1507 if (strchr(url_in
, '.') == NULL
&&
1508 strchr(url_in
, '/') == NULL
&&
1509 stat(url_in
, &sb
) != 0 &&
1510 gethostbyname(url_in
) == NULL
) {
1512 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1513 url_out
= g_strdup_printf(search_string
, enc_search
);
1519 /* XXX not sure about this heuristic */
1520 if (stat(url_in
, &sb
) == 0)
1521 url_out
= g_strdup_printf("file://%s", url_in
);
1523 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1525 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1531 load_uri(struct tab
*t
, gchar
*uri
)
1534 gchar
*newuri
= NULL
;
1540 /* Strip leading spaces. */
1541 while (*uri
&& isspace(*uri
))
1544 if (strlen(uri
) == 0) {
1549 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1551 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1552 for (i
= 0; i
< LENGTH(about_list
); i
++)
1553 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1554 bzero(&args
, sizeof args
);
1555 about_list
[i
].func(t
, &args
);
1556 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1560 show_oops(t
, "invalid about page");
1564 if (valid_url_type(uri
)) {
1565 newuri
= guess_url_type(uri
);
1569 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1570 webkit_web_view_load_uri(t
->wv
, uri
);
1577 get_uri(struct tab
*t
)
1579 const gchar
*uri
= NULL
;
1581 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
)
1582 uri
= webkit_web_view_get_uri(t
->wv
);
1584 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, about_list
[t
->xtp_meaning
].name
);
1590 get_title(struct tab
*t
)
1592 const gchar
*set
= NULL
, *title
= NULL
;
1594 title
= webkit_web_view_get_title(t
->wv
);
1595 set
= title
? title
: get_uri(t
);
1596 if (!set
|| t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
) {
1603 add_alias(struct settings
*s
, char *line
)
1606 struct alias
*a
= NULL
;
1608 if (s
== NULL
|| line
== NULL
) {
1609 show_oops(NULL
, "add_alias invalid parameters");
1614 a
= g_malloc(sizeof(*a
));
1616 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1617 show_oops(NULL
, "add_alias: incomplete alias definition");
1620 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1621 show_oops(NULL
, "add_alias: invalid alias definition");
1625 a
->a_name
= g_strdup(alias
);
1626 a
->a_uri
= g_strdup(l
);
1628 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1630 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1640 add_mime_type(struct settings
*s
, char *line
)
1644 struct mime_type
*m
= NULL
;
1645 int downloadfirst
= 0;
1647 /* XXX this could be smarter */
1649 if (line
== NULL
|| strlen(line
) == 0) {
1650 show_oops(NULL
, "add_mime_type invalid parameters");
1659 m
= g_malloc(sizeof(*m
));
1661 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1662 show_oops(NULL
, "add_mime_type: invalid mime_type");
1665 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1666 mime_type
[strlen(mime_type
) - 1] = '\0';
1671 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1672 show_oops(NULL
, "add_mime_type: invalid mime_type");
1676 m
->mt_type
= g_strdup(mime_type
);
1677 m
->mt_action
= g_strdup(l
);
1678 m
->mt_download
= downloadfirst
;
1680 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1681 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1683 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1693 find_mime_type(char *mime_type
)
1695 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1697 TAILQ_FOREACH(m
, &mtl
, entry
) {
1698 if (m
->mt_default
&&
1699 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1702 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1715 walk_mime_type(struct settings
*s
,
1716 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1718 struct mime_type
*m
;
1721 if (s
== NULL
|| cb
== NULL
) {
1722 show_oops(NULL
, "walk_mime_type invalid parameters");
1726 TAILQ_FOREACH(m
, &mtl
, entry
) {
1727 str
= g_strdup_printf("%s%s --> %s",
1729 m
->mt_default
? "*" : "",
1731 cb(s
, str
, cb_args
);
1737 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1742 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1745 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1747 /* treat *.moo.com the same as .moo.com */
1748 if (str
[0] == '*' && str
[1] == '.')
1750 else if (str
[0] == '.')
1755 d
= g_malloc(sizeof *d
);
1757 d
->d
= g_strdup_printf(".%s", str
);
1759 d
->d
= g_strdup(str
);
1762 if (RB_INSERT(domain_list
, wl
, d
))
1765 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1776 add_cookie_wl(struct settings
*s
, char *entry
)
1778 wl_add(entry
, &c_wl
, 1);
1783 walk_cookie_wl(struct settings
*s
,
1784 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1788 if (s
== NULL
|| cb
== NULL
) {
1789 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1793 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1794 cb(s
, d
->d
, cb_args
);
1798 walk_js_wl(struct settings
*s
,
1799 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1803 if (s
== NULL
|| cb
== NULL
) {
1804 show_oops(NULL
, "walk_js_wl invalid parameters");
1808 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1809 cb(s
, d
->d
, cb_args
);
1813 add_js_wl(struct settings
*s
, char *entry
)
1815 wl_add(entry
, &js_wl
, 1 /* persistent */);
1820 wl_find(const gchar
*search
, struct domain_list
*wl
)
1823 struct domain
*d
= NULL
, dfind
;
1826 if (search
== NULL
|| wl
== NULL
)
1828 if (strlen(search
) < 2)
1831 if (search
[0] != '.')
1832 s
= g_strdup_printf(".%s", search
);
1834 s
= g_strdup(search
);
1836 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1839 d
= RB_FIND(domain_list
, wl
, &dfind
);
1853 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1859 if (s
== NULL
|| wl
== NULL
)
1862 if (!strncmp(s
, "http://", strlen("http://")))
1863 s
= &s
[strlen("http://")];
1864 else if (!strncmp(s
, "https://", strlen("https://")))
1865 s
= &s
[strlen("https://")];
1870 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1871 /* chop string at first slash */
1872 if (s
[i
] == '/' || s
[i
] == '\0') {
1875 r
= wl_find(ss
, wl
);
1884 get_toplevel_domain(char *domain
)
1891 if (strlen(domain
) < 2)
1894 s
= &domain
[strlen(domain
) - 1];
1895 while (s
!= domain
) {
1911 settings_add(char *var
, char *val
)
1918 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1919 if (strcmp(var
, rs
[i
].name
))
1923 if (rs
[i
].s
->set(&rs
[i
], val
))
1924 errx(1, "invalid value for %s: %s", var
, val
);
1928 switch (rs
[i
].type
) {
1937 errx(1, "invalid sval for %s",
1951 errx(1, "invalid type for %s", var
);
1960 config_parse(char *filename
, int runtime
)
1963 char *line
, *cp
, *var
, *val
;
1964 size_t len
, lineno
= 0;
1966 char file
[PATH_MAX
];
1969 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1971 if (filename
== NULL
)
1974 if (runtime
&& runtime_settings
[0] != '\0') {
1975 snprintf(file
, sizeof file
, "%s/%s",
1976 work_dir
, runtime_settings
);
1977 if (stat(file
, &sb
)) {
1978 warnx("runtime file doesn't exist, creating it");
1979 if ((f
= fopen(file
, "w")) == NULL
)
1981 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1985 strlcpy(file
, filename
, sizeof file
);
1987 if ((config
= fopen(file
, "r")) == NULL
) {
1988 warn("config_parse: cannot open %s", filename
);
1993 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1994 if (feof(config
) || ferror(config
))
1998 cp
+= (long)strspn(cp
, WS
);
1999 if (cp
[0] == '\0') {
2005 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2006 errx(1, "invalid config file entry: %s", line
);
2008 cp
+= (long)strspn(cp
, WS
);
2010 if ((val
= strsep(&cp
, "\0")) == NULL
)
2013 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2014 handled
= settings_add(var
, val
);
2016 errx(1, "invalid conf file entry: %s=%s", var
, val
);
2025 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2031 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2035 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2038 JSStringGetUTF8CString(jsref
, s
, l
);
2039 JSStringRelease(jsref
);
2045 disable_hints(struct tab
*t
)
2047 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2048 bzero(t
->hint_num
, sizeof t
->hint_num
);
2049 run_script(t
, "vimprobable_clear()");
2051 t
->hint_mode
= XT_HINT_NONE
;
2055 enable_hints(struct tab
*t
)
2057 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2058 run_script(t
, "vimprobable_show_hints()");
2060 t
->hint_mode
= XT_HINT_NONE
;
2063 #define XT_JS_OPEN ("open;")
2064 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2065 #define XT_JS_FIRE ("fire;")
2066 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2067 #define XT_JS_FOUND ("found;")
2068 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2071 run_script(struct tab
*t
, char *s
)
2073 JSGlobalContextRef ctx
;
2074 WebKitWebFrame
*frame
;
2076 JSValueRef val
, exception
;
2079 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2080 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2082 frame
= webkit_web_view_get_main_frame(t
->wv
);
2083 ctx
= webkit_web_frame_get_global_context(frame
);
2085 str
= JSStringCreateWithUTF8CString(s
);
2086 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2087 NULL
, 0, &exception
);
2088 JSStringRelease(str
);
2090 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2092 es
= js_ref_to_string(ctx
, exception
);
2093 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2097 es
= js_ref_to_string(ctx
, val
);
2098 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2100 /* handle return value right here */
2101 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2103 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
2106 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2107 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2108 &es
[XT_JS_FIRE_LEN
]);
2113 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2114 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2125 hint(struct tab
*t
, struct karg
*args
)
2128 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2130 if (t
->hints_on
== 0)
2139 apply_style(struct tab
*t
)
2141 g_object_set(G_OBJECT(t
->settings
),
2142 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2146 userstyle(struct tab
*t
, struct karg
*args
)
2148 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2152 g_object_set(G_OBJECT(t
->settings
),
2153 "user-stylesheet-uri", NULL
, (char *)NULL
);
2162 * Doesn't work fully, due to the following bug:
2163 * https://bugs.webkit.org/show_bug.cgi?id=51747
2166 restore_global_history(void)
2168 char file
[PATH_MAX
];
2173 const char delim
[3] = {'\\', '\\', '\0'};
2175 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2177 if ((f
= fopen(file
, "r")) == NULL
) {
2178 warnx("%s: fopen", __func__
);
2183 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2184 if (feof(f
) || ferror(f
))
2187 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2188 if (feof(f
) || ferror(f
)) {
2190 warnx("%s: broken history file\n", __func__
);
2194 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2195 webkit_web_history_item_new_with_data(uri
, title
);
2196 h
= g_malloc(sizeof(struct history
));
2197 h
->uri
= g_strdup(uri
);
2198 h
->title
= g_strdup(title
);
2199 RB_INSERT(history_list
, &hl
, h
);
2200 completion_add_uri(h
->uri
);
2202 warnx("%s: failed to restore history\n", __func__
);
2218 save_global_history_to_disk(struct tab
*t
)
2220 char file
[PATH_MAX
];
2224 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2226 if ((f
= fopen(file
, "w")) == NULL
) {
2227 show_oops(t
, "%s: global history file: %s",
2228 __func__
, strerror(errno
));
2232 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2233 if (h
->uri
&& h
->title
)
2234 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2243 quit(struct tab
*t
, struct karg
*args
)
2245 if (save_global_history
)
2246 save_global_history_to_disk(t
);
2254 open_tabs(struct tab
*t
, struct karg
*a
)
2256 char file
[PATH_MAX
];
2260 struct tab
*ti
, *tt
;
2265 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2266 if ((f
= fopen(file
, "r")) == NULL
)
2269 ti
= TAILQ_LAST(&tabs
, tab_list
);
2272 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2273 if (feof(f
) || ferror(f
))
2276 /* retrieve session name */
2277 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2278 strlcpy(named_session
,
2279 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2280 sizeof named_session
);
2284 if (uri
&& strlen(uri
))
2285 create_new_tab(uri
, NULL
, 1, -1);
2291 /* close open tabs */
2292 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2294 tt
= TAILQ_FIRST(&tabs
);
2314 restore_saved_tabs(void)
2316 char file
[PATH_MAX
];
2317 int unlink_file
= 0;
2322 snprintf(file
, sizeof file
, "%s/%s",
2323 sessions_dir
, XT_RESTART_TABS_FILE
);
2324 if (stat(file
, &sb
) == -1)
2325 a
.s
= XT_SAVED_TABS_FILE
;
2328 a
.s
= XT_RESTART_TABS_FILE
;
2331 a
.i
= XT_SES_DONOTHING
;
2332 rv
= open_tabs(NULL
, &a
);
2341 save_tabs(struct tab
*t
, struct karg
*a
)
2343 char file
[PATH_MAX
];
2345 int num_tabs
= 0, i
;
2346 struct tab
**stabs
= NULL
;
2351 snprintf(file
, sizeof file
, "%s/%s",
2352 sessions_dir
, named_session
);
2354 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2356 if ((f
= fopen(file
, "w")) == NULL
) {
2357 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2361 /* save session name */
2362 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2364 /* Save tabs, in the order they are arranged in the notebook. */
2365 num_tabs
= sort_tabs_by_page_num(&stabs
);
2367 for (i
= 0; i
< num_tabs
; i
++)
2368 if (stabs
[i
] && get_uri(stabs
[i
]) != NULL
)
2369 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2373 /* try and make sure this gets to disk NOW. XXX Backup first? */
2374 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2375 show_oops(t
, "May not have managed to save session: %s",
2385 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2397 yank_uri(struct tab
*t
, struct karg
*args
)
2400 GtkClipboard
*clipboard
;
2402 if ((uri
= get_uri(t
)) == NULL
)
2405 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2406 gtk_clipboard_set_text(clipboard
, uri
, -1);
2412 paste_uri(struct tab
*t
, struct karg
*args
)
2414 GtkClipboard
*clipboard
;
2415 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2417 gchar
*p
= NULL
, *uri
;
2419 /* try primary clipboard first */
2420 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2421 p
= gtk_clipboard_wait_for_text(clipboard
);
2423 /* if it failed get whatever text is in cut_buffer0 */
2425 if (gdk_property_get(gdk_get_default_root_window(),
2427 gdk_atom_intern("STRING", FALSE
),
2429 65536 /* picked out of my butt */,
2435 /* yes sir, we need to NUL the string */
2441 while (*uri
&& isspace(*uri
))
2443 if (strlen(uri
) == 0) {
2444 show_oops(t
, "empty paste buffer");
2447 if (guess_search
== 0 && valid_url_type(uri
)) {
2448 /* we can be clever and paste this in search box */
2449 show_oops(t
, "not a valid URL");
2453 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2455 else if (args
->i
== XT_PASTE_NEW_TAB
)
2456 create_new_tab(uri
, NULL
, 1, -1);
2467 find_domain(const gchar
*s
, int add_dot
)
2470 char *r
= NULL
, *ss
= NULL
;
2475 if (!strncmp(s
, "http://", strlen("http://")))
2476 s
= &s
[strlen("http://")];
2477 else if (!strncmp(s
, "https://", strlen("https://")))
2478 s
= &s
[strlen("https://")];
2484 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2485 /* chop string at first slash */
2486 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2489 r
= g_strdup_printf(".%s", ss
);
2500 toggle_cwl(struct tab
*t
, struct karg
*args
)
2504 char *dom
= NULL
, *dom_toggle
= NULL
;
2511 dom
= find_domain(uri
, 1);
2512 d
= wl_find(dom
, &c_wl
);
2519 if (args
->i
& XT_WL_TOGGLE
)
2521 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2523 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2526 if (args
->i
& XT_WL_TOPLEVEL
)
2527 dom_toggle
= get_toplevel_domain(dom
);
2532 /* enable cookies for domain */
2533 wl_add(dom_toggle
, &c_wl
, 0);
2535 /* disable cookies for domain */
2536 RB_REMOVE(domain_list
, &c_wl
, d
);
2538 if (args
->i
& XT_WL_RELOAD
)
2539 webkit_web_view_reload(t
->wv
);
2546 toggle_js(struct tab
*t
, struct karg
*args
)
2551 char *dom
= NULL
, *dom_toggle
= NULL
;
2556 g_object_get(G_OBJECT(t
->settings
),
2557 "enable-scripts", &es
, (char *)NULL
);
2558 if (args
->i
& XT_WL_TOGGLE
)
2560 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2562 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2568 dom
= find_domain(uri
, 1);
2570 if (uri
== NULL
|| dom
== NULL
) {
2571 show_oops(t
, "Can't toggle domain in JavaScript white list");
2575 if (args
->i
& XT_WL_TOPLEVEL
)
2576 dom_toggle
= get_toplevel_domain(dom
);
2581 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2582 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2584 d
= wl_find(dom_toggle
, &js_wl
);
2586 RB_REMOVE(domain_list
, &js_wl
, d
);
2587 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2589 g_object_set(G_OBJECT(t
->settings
),
2590 "enable-scripts", es
, (char *)NULL
);
2591 g_object_set(G_OBJECT(t
->settings
),
2592 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2593 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2595 if (args
->i
& XT_WL_RELOAD
)
2596 webkit_web_view_reload(t
->wv
);
2604 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2608 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2611 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2616 toggle_src(struct tab
*t
, struct karg
*args
)
2623 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2624 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2625 webkit_web_view_reload(t
->wv
);
2631 focus_webview(struct tab
*t
)
2636 /* only grab focus if we are visible */
2637 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2638 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2642 focus(struct tab
*t
, struct karg
*args
)
2644 if (t
== NULL
|| args
== NULL
)
2650 if (args
->i
== XT_FOCUS_URI
)
2651 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2652 else if (args
->i
== XT_FOCUS_SEARCH
)
2653 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2659 stats(struct tab
*t
, struct karg
*args
)
2661 char *page
, *body
, *s
, line
[64 * 1024];
2662 uint64_t line_count
= 0;
2666 show_oops(NULL
, "stats invalid parameters");
2669 if (save_rejected_cookies
) {
2670 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2672 s
= fgets(line
, sizeof line
, r_cookie_f
);
2673 if (s
== NULL
|| feof(r_cookie_f
) ||
2679 snprintf(line
, sizeof line
,
2680 "<br/>Cookies blocked(*) total: %llu", line_count
);
2682 show_oops(t
, "Can't open blocked cookies file: %s",
2686 body
= g_strdup_printf(
2687 "Cookies blocked(*) this session: %llu"
2689 "<p><small><b>*</b> results vary based on settings</small></p>",
2693 page
= get_html_page("Statistics", body
, "", 0);
2696 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2703 marco(struct tab
*t
, struct karg
*args
)
2705 char *page
, line
[64 * 1024];
2709 show_oops(NULL
, "marco invalid parameters");
2712 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2714 page
= get_html_page("Marco Sez...", line
, "", 0);
2716 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2723 blank(struct tab
*t
, struct karg
*args
)
2726 show_oops(NULL
, "blank invalid parameters");
2728 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2733 about(struct tab
*t
, struct karg
*args
)
2738 show_oops(NULL
, "about invalid parameters");
2740 body
= g_strdup_printf("<b>Version: %s</b><p>"
2743 "<li>Marco Peereboom <marco@peereboom.us></li>"
2744 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2745 "<li>Edd Barrett <vext01@gmail.com> </li>"
2746 "<li>Todd T. Fries <todd@fries.net> </li>"
2747 "<li>Raphael Graf <r@undefined.ch> </li>"
2749 "Copyrights and licenses can be found on the XXXterm "
2750 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2754 page
= get_html_page("About", body
, "", 0);
2757 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2764 help(struct tab
*t
, struct karg
*args
)
2766 char *page
, *head
, *body
;
2769 show_oops(NULL
, "help invalid parameters");
2771 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2772 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2774 body
= "XXXterm man page <a href=\"http://opensource.conformal.com/"
2775 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2776 "cgi-bin/man-cgi?xxxterm</a>";
2778 page
= get_html_page("XXXterm", body
, head
, FALSE
);
2780 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2787 * update all favorite tabs apart from one. Pass NULL if
2788 * you want to update all.
2791 update_favorite_tabs(struct tab
*apart_from
)
2794 if (!updating_fl_tabs
) {
2795 updating_fl_tabs
= 1; /* stop infinite recursion */
2796 TAILQ_FOREACH(t
, &tabs
, entry
)
2797 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2798 && (t
!= apart_from
))
2799 xtp_page_fl(t
, NULL
);
2800 updating_fl_tabs
= 0;
2804 /* show a list of favorites (bookmarks) */
2806 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2808 char file
[PATH_MAX
];
2810 char *uri
= NULL
, *title
= NULL
;
2811 size_t len
, lineno
= 0;
2813 char *body
, *tmp
, *page
= NULL
;
2814 const char delim
[3] = {'\\', '\\', '\0'};
2816 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2819 warn("%s: bad param", __func__
);
2821 /* new session key */
2822 if (!updating_fl_tabs
)
2823 generate_xtp_session_key(&fl_session_key
);
2825 /* open favorites */
2826 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2827 if ((f
= fopen(file
, "r")) == NULL
) {
2828 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2833 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2834 "<th style='width: 40px'>#</th><th>Link</th>"
2835 "<th style='width: 40px'>Rm</th></tr>\n");
2838 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2839 if (feof(f
) || ferror(f
))
2847 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2848 if (feof(f
) || ferror(f
)) {
2849 show_oops(t
, "favorites file corrupt");
2855 body
= g_strdup_printf("%s<tr>"
2857 "<td><a href='%s'>%s</a></td>"
2858 "<td style='text-align: center'>"
2859 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2861 body
, i
, uri
, title
,
2862 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2874 /* if none, say so */
2877 body
= g_strdup_printf("%s<tr>"
2878 "<td colspan='3' style='text-align: center'>"
2879 "No favorites - To add one use the 'favadd' command."
2880 "</td></tr>", body
);
2885 body
= g_strdup_printf("%s</table>", body
);
2895 page
= get_html_page("Favorites", body
, "", 1);
2896 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
2900 update_favorite_tabs(t
);
2909 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2910 size_t cert_count
, char *title
)
2912 gnutls_datum_t cinfo
;
2916 body
= g_strdup("");
2918 for (i
= 0; i
< cert_count
; i
++) {
2919 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2924 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2925 body
, i
, cinfo
.data
);
2926 gnutls_free(cinfo
.data
);
2930 tmp
= get_html_page(title
, body
, "", 0);
2933 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2938 ca_cmd(struct tab
*t
, struct karg
*args
)
2941 int rv
= 1, certs
= 0, certs_read
;
2944 gnutls_x509_crt_t
*c
= NULL
;
2945 char *certs_buf
= NULL
, *s
;
2947 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2948 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
2952 if (fstat(fileno(f
), &sb
) == -1) {
2953 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
2957 certs_buf
= g_malloc(sb
.st_size
+ 1);
2958 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2959 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2962 certs_buf
[sb
.st_size
] = '\0';
2965 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2967 s
+= strlen("BEGIN CERTIFICATE");
2970 bzero(&dt
, sizeof dt
);
2971 dt
.data
= (unsigned char *)certs_buf
;
2972 dt
.size
= sb
.st_size
;
2973 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2974 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
2975 GNUTLS_X509_FMT_PEM
, 0);
2976 if (certs_read
<= 0) {
2977 show_oops(t
, "No cert(s) available");
2980 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2993 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2996 struct addrinfo hints
, *res
= NULL
, *ai
;
3000 if (uri
&& !g_str_has_prefix(uri
, "https://"))
3003 su
= soup_uri_new(uri
);
3006 if (!SOUP_URI_VALID_FOR_HTTP(su
))
3009 snprintf(port
, sizeof port
, "%d", su
->port
);
3010 bzero(&hints
, sizeof(struct addrinfo
));
3011 hints
.ai_flags
= AI_CANONNAME
;
3012 hints
.ai_family
= AF_UNSPEC
;
3013 hints
.ai_socktype
= SOCK_STREAM
;
3015 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
3018 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3019 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3022 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3025 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3029 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
3034 strlcpy(domain
, su
->host
, domain_sz
);
3045 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3048 gnutls_deinit(gsession
);
3050 gnutls_certificate_free_credentials(xcred
);
3056 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3057 gnutls_certificate_credentials_t
*xc
)
3059 gnutls_certificate_credentials_t xcred
;
3060 gnutls_session_t gsession
;
3063 if (gs
== NULL
|| xc
== NULL
)
3069 gnutls_certificate_allocate_credentials(&xcred
);
3070 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3071 GNUTLS_X509_FMT_PEM
);
3072 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3073 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3074 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3075 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3076 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3077 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3079 gnutls_error_is_fatal(rv
),
3080 gnutls_strerror_name(rv
));
3081 stop_tls(gsession
, xcred
);
3085 gnutls_credentials_type_t cred
;
3086 cred
= gnutls_auth_get_type(gsession
);
3087 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3088 stop_tls(gsession
, xcred
);
3100 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3104 const gnutls_datum_t
*cl
;
3105 gnutls_x509_crt_t
*all_certs
;
3108 if (certs
== NULL
|| cert_count
== NULL
)
3110 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3112 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3116 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3117 for (i
= 0; i
< len
; i
++) {
3118 gnutls_x509_crt_init(&all_certs
[i
]);
3119 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3120 GNUTLS_X509_FMT_PEM
< 0)) {
3134 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3138 for (i
= 0; i
< cert_count
; i
++)
3139 gnutls_x509_crt_deinit(certs
[i
]);
3144 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3145 size_t cert_count
, char *domain
)
3148 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3153 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3156 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3157 if ((f
= fopen(file
, "w")) == NULL
) {
3158 show_oops(t
, "Can't create cert file %s %s",
3159 file
, strerror(errno
));
3163 for (i
= 0; i
< cert_count
; i
++) {
3164 cert_buf_sz
= sizeof cert_buf
;
3165 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3166 cert_buf
, &cert_buf_sz
)) {
3167 show_oops(t
, "gnutls_x509_crt_export failed");
3170 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3171 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3176 /* not the best spot but oh well */
3177 gdk_color_parse("lightblue", &color
);
3178 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3179 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
3180 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
3181 gdk_color_parse(XT_COLOR_BLACK
, &color
);
3182 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
3183 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
3189 load_compare_cert(struct tab
*t
, struct karg
*args
)
3192 char domain
[8182], file
[PATH_MAX
];
3193 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3194 int s
= -1, rv
= 1, i
;
3198 gnutls_session_t gsession
;
3199 gnutls_x509_crt_t
*certs
;
3200 gnutls_certificate_credentials_t xcred
;
3205 if ((uri
= get_uri(t
)) == NULL
)
3208 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3212 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3213 show_oops(t
, "Start TLS failed");
3218 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3219 show_oops(t
, "Can't get connection certificates");
3223 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3224 if ((f
= fopen(file
, "r")) == NULL
)
3227 for (i
= 0; i
< cert_count
; i
++) {
3228 cert_buf_sz
= sizeof cert_buf
;
3229 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3230 cert_buf
, &cert_buf_sz
)) {
3233 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3234 rv
= -1; /* critical */
3237 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3238 rv
= -1; /* critical */
3247 free_connection_certs(certs
, cert_count
);
3249 /* we close the socket first for speed */
3252 stop_tls(gsession
, xcred
);
3258 cert_cmd(struct tab
*t
, struct karg
*args
)
3264 gnutls_session_t gsession
;
3265 gnutls_x509_crt_t
*certs
;
3266 gnutls_certificate_credentials_t xcred
;
3271 if (ssl_ca_file
== NULL
) {
3272 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3276 if ((uri
= get_uri(t
)) == NULL
) {
3277 show_oops(t
, "Invalid URI");
3281 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3282 show_oops(t
, "Invalid certificate URI: %s", uri
);
3287 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3288 show_oops(t
, "Start TLS failed");
3293 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3294 show_oops(t
, "get_connection_certs failed");
3298 if (args
->i
& XT_SHOW
)
3299 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3300 else if (args
->i
& XT_SAVE
)
3301 save_certs(t
, certs
, cert_count
, domain
);
3303 free_connection_certs(certs
, cert_count
);
3305 /* we close the socket first for speed */
3308 stop_tls(gsession
, xcred
);
3314 remove_cookie(int index
)
3320 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3322 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3324 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3328 print_cookie("remove cookie", c
);
3329 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3334 soup_cookies_free(cf
);
3340 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3345 body
= g_strdup("");
3348 if (args
->i
& XT_WL_PERSISTENT
) {
3350 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3352 RB_FOREACH(d
, domain_list
, wl
) {
3356 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3362 if (args
->i
& XT_WL_SESSION
) {
3364 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3366 RB_FOREACH(d
, domain_list
, wl
) {
3370 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3375 tmp
= get_html_page(title
, body
, "", 0);
3378 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3380 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3386 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3388 char file
[PATH_MAX
];
3390 char *line
= NULL
, *lt
= NULL
;
3393 char *dom
= NULL
, *dom_save
= NULL
;
3399 if (t
== NULL
|| args
== NULL
)
3402 if (runtime_settings
[0] == '\0')
3405 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3406 if ((f
= fopen(file
, "r+")) == NULL
)
3410 dom
= find_domain(uri
, 1);
3411 if (uri
== NULL
|| dom
== NULL
) {
3412 show_oops(t
, "Can't add domain to %s white list",
3413 js
? "JavaScript" : "cookie");
3417 if (args
->i
& XT_WL_TOPLEVEL
) {
3419 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3420 show_oops(t
, "invalid domain: %s", dom
);
3423 } else if (args
->i
& XT_WL_FQDN
) {
3429 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3432 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3435 if (!strcmp(line
, lt
))
3441 fprintf(f
, "%s\n", lt
);
3446 d
= wl_find(dom_save
, &js_wl
);
3448 settings_add("js_wl", dom_save
);
3449 d
= wl_find(dom_save
, &js_wl
);
3453 d
= wl_find(dom_save
, &c_wl
);
3455 settings_add("cookie_wl", dom_save
);
3456 d
= wl_find(dom_save
, &c_wl
);
3460 /* find and add to persistent jar */
3461 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3462 for (;cf
; cf
= cf
->next
) {
3464 if (!strcmp(dom_save
, ci
->domain
) ||
3465 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3466 c
= soup_cookie_copy(ci
);
3467 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3470 soup_cookies_free(cf
);
3488 js_show_wl(struct tab
*t
, struct karg
*args
)
3490 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3491 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3497 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3499 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3500 wl_show(t
, args
, "Cookie White List", &c_wl
);
3506 cookie_cmd(struct tab
*t
, struct karg
*args
)
3508 if (args
->i
& XT_SHOW
)
3509 wl_show(t
, args
, "Cookie White List", &c_wl
);
3510 else if (args
->i
& XT_WL_TOGGLE
) {
3511 args
->i
|= XT_WL_RELOAD
;
3512 toggle_cwl(t
, args
);
3513 } else if (args
->i
& XT_SAVE
) {
3514 args
->i
|= XT_WL_RELOAD
;
3515 wl_save(t
, args
, 0);
3516 } else if (args
->i
& XT_DELETE
)
3517 show_oops(t
, "'cookie delete' currently unimplemented");
3523 js_cmd(struct tab
*t
, struct karg
*args
)
3525 if (args
->i
& XT_SHOW
)
3526 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3527 else if (args
->i
& XT_SAVE
) {
3528 args
->i
|= XT_WL_RELOAD
;
3529 wl_save(t
, args
, 1);
3530 } else if (args
->i
& XT_WL_TOGGLE
) {
3531 args
->i
|= XT_WL_RELOAD
;
3533 } else if (args
->i
& XT_DELETE
)
3534 show_oops(t
, "'js delete' currently unimplemented");
3540 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3542 js_toggle_cb(t
->js_toggle
, t
);
3548 add_favorite(struct tab
*t
, struct karg
*args
)
3550 char file
[PATH_MAX
];
3553 size_t urilen
, linelen
;
3554 const gchar
*uri
, *title
;
3559 /* don't allow adding of xtp pages to favorites */
3560 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3561 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3565 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3566 if ((f
= fopen(file
, "r+")) == NULL
) {
3567 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3571 title
= webkit_web_view_get_title(t
->wv
);
3577 if (title
== NULL
|| uri
== NULL
) {
3578 show_oops(t
, "can't add page to favorites");
3582 urilen
= strlen(uri
);
3585 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3586 if (feof(f
) || ferror(f
))
3589 if (linelen
== urilen
&& !strcmp(line
, uri
))
3596 fprintf(f
, "\n%s\n%s", title
, uri
);
3602 update_favorite_tabs(NULL
);
3608 navaction(struct tab
*t
, struct karg
*args
)
3610 WebKitWebHistoryItem
*item
;
3612 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3613 t
->tab_id
, args
->i
);
3616 if (args
->i
== XT_NAV_BACK
)
3617 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3619 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3621 return (XT_CB_PASSTHROUGH
);
3622 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3624 return (XT_CB_PASSTHROUGH
);
3629 webkit_web_view_go_back(t
->wv
);
3631 case XT_NAV_FORWARD
:
3632 webkit_web_view_go_forward(t
->wv
);
3635 webkit_web_view_reload(t
->wv
);
3637 case XT_NAV_RELOAD_CACHE
:
3638 webkit_web_view_reload_bypass_cache(t
->wv
);
3641 return (XT_CB_PASSTHROUGH
);
3645 move(struct tab
*t
, struct karg
*args
)
3647 GtkAdjustment
*adjust
;
3648 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3653 case XT_MOVE_BOTTOM
:
3655 case XT_MOVE_PAGEDOWN
:
3656 case XT_MOVE_PAGEUP
:
3657 case XT_MOVE_HALFDOWN
:
3658 case XT_MOVE_HALFUP
:
3659 adjust
= t
->adjust_v
;
3662 adjust
= t
->adjust_h
;
3666 pos
= gtk_adjustment_get_value(adjust
);
3667 ps
= gtk_adjustment_get_page_size(adjust
);
3668 upper
= gtk_adjustment_get_upper(adjust
);
3669 lower
= gtk_adjustment_get_lower(adjust
);
3670 si
= gtk_adjustment_get_step_increment(adjust
);
3671 pi
= gtk_adjustment_get_page_increment(adjust
);
3674 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3675 "max %f si %f pi %f\n",
3676 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3677 pos
, ps
, upper
, lower
, max
, si
, pi
);
3683 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3688 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3690 case XT_MOVE_BOTTOM
:
3691 case XT_MOVE_FARRIGHT
:
3692 gtk_adjustment_set_value(adjust
, max
);
3695 case XT_MOVE_FARLEFT
:
3696 gtk_adjustment_set_value(adjust
, lower
);
3698 case XT_MOVE_PAGEDOWN
:
3700 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3702 case XT_MOVE_PAGEUP
:
3704 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3706 case XT_MOVE_HALFDOWN
:
3708 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3710 case XT_MOVE_HALFUP
:
3712 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3715 return (XT_CB_PASSTHROUGH
);
3718 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3720 return (XT_CB_HANDLED
);
3724 url_set_visibility(void)
3728 TAILQ_FOREACH(t
, &tabs
, entry
) {
3729 if (show_url
== 0) {
3730 gtk_widget_hide(t
->toolbar
);
3733 gtk_widget_show(t
->toolbar
);
3738 notebook_tab_set_visibility()
3740 if (show_tabs
== 0) {
3741 gtk_widget_hide(tab_bar
);
3742 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3744 if (tab_style
== XT_TABS_NORMAL
) {
3745 gtk_widget_hide(tab_bar
);
3746 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3747 } else if (tab_style
== XT_TABS_COMPACT
) {
3748 gtk_widget_show(tab_bar
);
3749 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3755 statusbar_set_visibility(void)
3759 TAILQ_FOREACH(t
, &tabs
, entry
) {
3760 if (show_statusbar
== 0) {
3761 gtk_widget_hide(t
->statusbar_box
);
3764 gtk_widget_show(t
->statusbar_box
);
3769 url_set(struct tab
*t
, int enable_url_entry
)
3774 show_url
= enable_url_entry
;
3776 if (enable_url_entry
) {
3777 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3778 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3779 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
3781 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3782 GTK_ENTRY_ICON_PRIMARY
);
3784 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3785 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
3786 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3787 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
3793 fullscreen(struct tab
*t
, struct karg
*args
)
3795 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3798 return (XT_CB_PASSTHROUGH
);
3800 if (show_url
== 0) {
3808 url_set_visibility();
3809 notebook_tab_set_visibility();
3811 return (XT_CB_HANDLED
);
3815 statustoggle(struct tab
*t
, struct karg
*args
)
3817 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3819 if (show_statusbar
== 1) {
3821 statusbar_set_visibility();
3822 } else if (show_statusbar
== 0) {
3824 statusbar_set_visibility();
3826 return (XT_CB_HANDLED
);
3830 urlaction(struct tab
*t
, struct karg
*args
)
3832 int rv
= XT_CB_HANDLED
;
3834 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3837 return (XT_CB_PASSTHROUGH
);
3841 if (show_url
== 0) {
3843 url_set_visibility();
3847 if (show_url
== 1) {
3849 url_set_visibility();
3857 tabaction(struct tab
*t
, struct karg
*args
)
3859 int rv
= XT_CB_HANDLED
;
3860 char *url
= args
->s
;
3864 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3867 return (XT_CB_PASSTHROUGH
);
3871 if (strlen(url
) > 0)
3872 create_new_tab(url
, NULL
, 1, args
->p
);
3874 create_new_tab(NULL
, NULL
, 1, args
->p
);
3880 TAILQ_FOREACH(tt
, &tabs
, entry
)
3881 if (tt
->tab_id
== args
->p
- 1) {
3887 case XT_TAB_DELQUIT
:
3888 if (gtk_notebook_get_n_pages(notebook
) > 1)
3894 if (strlen(url
) > 0)
3897 rv
= XT_CB_PASSTHROUGH
;
3903 if (show_tabs
== 0) {
3905 notebook_tab_set_visibility();
3909 if (show_tabs
== 1) {
3911 notebook_tab_set_visibility();
3914 case XT_TAB_NEXTSTYLE
:
3915 if (tab_style
== XT_TABS_NORMAL
)
3916 tab_style
= XT_TABS_COMPACT
;
3918 tab_style
= XT_TABS_NORMAL
;
3919 notebook_tab_set_visibility();
3921 case XT_TAB_UNDO_CLOSE
:
3922 if (undo_count
== 0) {
3923 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3927 u
= TAILQ_FIRST(&undos
);
3928 create_new_tab(u
->uri
, u
, 1, -1);
3930 TAILQ_REMOVE(&undos
, u
, entry
);
3932 /* u->history is freed in create_new_tab() */
3937 rv
= XT_CB_PASSTHROUGH
;
3951 resizetab(struct tab
*t
, struct karg
*args
)
3953 if (t
== NULL
|| args
== NULL
) {
3954 show_oops(NULL
, "resizetab invalid parameters");
3955 return (XT_CB_PASSTHROUGH
);
3958 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3959 t
->tab_id
, args
->i
);
3961 adjustfont_webkit(t
, args
->i
);
3963 return (XT_CB_HANDLED
);
3967 movetab(struct tab
*t
, struct karg
*args
)
3971 if (t
== NULL
|| args
== NULL
) {
3972 show_oops(NULL
, "movetab invalid parameters");
3973 return (XT_CB_PASSTHROUGH
);
3976 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3977 t
->tab_id
, args
->i
);
3979 if (args
->i
>= XT_TAB_INVALID
)
3980 return (XT_CB_PASSTHROUGH
);
3982 if (TAILQ_EMPTY(&tabs
))
3983 return (XT_CB_PASSTHROUGH
);
3985 n
= gtk_notebook_get_n_pages(notebook
);
3986 dest
= gtk_notebook_get_current_page(notebook
);
3991 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4000 dest
-= args
->p
% n
;
4013 return (XT_CB_PASSTHROUGH
);
4016 if (dest
< 0 || dest
>= n
)
4017 return (XT_CB_PASSTHROUGH
);
4018 if (t
->tab_id
== dest
) {
4019 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4020 return (XT_CB_HANDLED
);
4023 set_current_tab(dest
);
4025 return (XT_CB_HANDLED
);
4031 command(struct tab
*t
, struct karg
*args
)
4033 char *s
= NULL
, *ss
= NULL
;
4037 if (t
== NULL
|| args
== NULL
) {
4038 show_oops(NULL
, "command invalid parameters");
4039 return (XT_CB_PASSTHROUGH
);
4050 if (cmd_prefix
== 0)
4053 ss
= g_strdup_printf(":%d", cmd_prefix
);
4064 case XT_CMD_OPEN_CURRENT
:
4067 case XT_CMD_TABNEW_CURRENT
:
4068 if (!s
) /* FALL THROUGH? */
4070 if ((uri
= get_uri(t
)) != NULL
) {
4071 ss
= g_strdup_printf("%s%s", s
, uri
);
4076 show_oops(t
, "command: invalid opcode %d", args
->i
);
4077 return (XT_CB_PASSTHROUGH
);
4080 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4082 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4083 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4084 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4086 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4087 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4092 return (XT_CB_HANDLED
);
4096 * Return a new string with a download row (in html)
4097 * appended. Old string is freed.
4100 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4103 WebKitDownloadStatus stat
;
4104 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4106 char cur_sz
[FMT_SCALED_STRSIZE
];
4107 char tot_sz
[FMT_SCALED_STRSIZE
];
4110 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4112 /* All actions wil take this form:
4113 * xxxt://class/seskey
4115 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4116 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4118 stat
= webkit_download_get_status(dl
->download
);
4121 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4122 status_html
= g_strdup_printf("Finished");
4123 cmd_html
= g_strdup_printf(
4124 "<a href='%s%d/%d'>Remove</a>",
4125 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4127 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4128 /* gather size info */
4129 progress
= 100 * webkit_download_get_progress(dl
->download
);
4132 webkit_download_get_current_size(dl
->download
), cur_sz
);
4134 webkit_download_get_total_size(dl
->download
), tot_sz
);
4136 status_html
= g_strdup_printf(
4137 "<div style='width: 100%%' align='center'>"
4138 "<div class='progress-outer'>"
4139 "<div class='progress-inner' style='width: %.2f%%'>"
4140 "</div></div></div>"
4141 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4142 progress
, cur_sz
, tot_sz
, progress
);
4144 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4145 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4149 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4150 status_html
= g_strdup_printf("Cancelled");
4151 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4152 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4154 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4155 status_html
= g_strdup_printf("Error!");
4156 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4157 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4159 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4160 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4161 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4162 status_html
= g_strdup_printf("Starting");
4165 show_oops(t
, "%s: unknown download status", __func__
);
4168 new_html
= g_strdup_printf(
4169 "%s\n<tr><td>%s</td><td>%s</td>"
4170 "<td style='text-align:center'>%s</td></tr>\n",
4171 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4172 status_html
, cmd_html
);
4176 g_free(status_html
);
4187 * update all download tabs apart from one. Pass NULL if
4188 * you want to update all.
4191 update_download_tabs(struct tab
*apart_from
)
4194 if (!updating_dl_tabs
) {
4195 updating_dl_tabs
= 1; /* stop infinite recursion */
4196 TAILQ_FOREACH(t
, &tabs
, entry
)
4197 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4198 && (t
!= apart_from
))
4199 xtp_page_dl(t
, NULL
);
4200 updating_dl_tabs
= 0;
4205 * update all cookie tabs apart from one. Pass NULL if
4206 * you want to update all.
4209 update_cookie_tabs(struct tab
*apart_from
)
4212 if (!updating_cl_tabs
) {
4213 updating_cl_tabs
= 1; /* stop infinite recursion */
4214 TAILQ_FOREACH(t
, &tabs
, entry
)
4215 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4216 && (t
!= apart_from
))
4217 xtp_page_cl(t
, NULL
);
4218 updating_cl_tabs
= 0;
4223 * update all history tabs apart from one. Pass NULL if
4224 * you want to update all.
4227 update_history_tabs(struct tab
*apart_from
)
4231 if (!updating_hl_tabs
) {
4232 updating_hl_tabs
= 1; /* stop infinite recursion */
4233 TAILQ_FOREACH(t
, &tabs
, entry
)
4234 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4235 && (t
!= apart_from
))
4236 xtp_page_hl(t
, NULL
);
4237 updating_hl_tabs
= 0;
4241 /* cookie management XTP page */
4243 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4245 char *body
, *page
, *tmp
;
4246 int i
= 1; /* all ids start 1 */
4247 GSList
*sc
, *pc
, *pc_start
;
4249 char *type
, *table_headers
, *last_domain
;
4251 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4254 show_oops(NULL
, "%s invalid parameters", __func__
);
4258 /* Generate a new session key */
4259 if (!updating_cl_tabs
)
4260 generate_xtp_session_key(&cl_session_key
);
4263 table_headers
= g_strdup_printf("<table><tr>"
4266 "<th style='width:200px'>Value</th>"
4270 "<th>HTTP<br />only</th>"
4271 "<th style='width:40px'>Rm</th></tr>\n");
4273 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4274 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4278 last_domain
= strdup("");
4279 for (; sc
; sc
= sc
->next
) {
4282 if (strcmp(last_domain
, c
->domain
) != 0) {
4285 last_domain
= strdup(c
->domain
);
4289 body
= g_strdup_printf("%s</table>"
4291 body
, c
->domain
, table_headers
);
4295 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4296 c
->domain
, table_headers
);
4301 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4302 if (soup_cookie_equal(pc
->data
, c
)) {
4303 type
= "Session + Persistent";
4308 body
= g_strdup_printf(
4311 "<td style='word-wrap:normal'>%s</td>"
4313 " <textarea rows='4'>%s</textarea>"
4319 "<td style='text-align:center'>"
4320 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4327 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4342 soup_cookies_free(sc
);
4343 soup_cookies_free(pc
);
4345 /* small message if there are none */
4347 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4348 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4351 body
= g_strdup_printf("%s</table>", body
);
4354 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4356 g_free(table_headers
);
4357 g_free(last_domain
);
4359 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4360 update_cookie_tabs(t
);
4368 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4370 char *body
, *page
, *tmp
;
4372 int i
= 1; /* all ids start 1 */
4374 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4377 show_oops(NULL
, "%s invalid parameters", __func__
);
4381 /* Generate a new session key */
4382 if (!updating_hl_tabs
)
4383 generate_xtp_session_key(&hl_session_key
);
4386 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4387 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4389 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4391 body
= g_strdup_printf(
4393 "<td><a href='%s'>%s</a></td>"
4395 "<td style='text-align: center'>"
4396 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4397 body
, h
->uri
, h
->uri
, h
->title
,
4398 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4399 XT_XTP_HL_REMOVE
, i
);
4405 /* small message if there are none */
4408 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4409 "colspan='3'>No History</td></tr>\n", body
);
4414 body
= g_strdup_printf("%s</table>", body
);
4417 page
= get_html_page("History", body
, "", TRUE
);
4421 * update all history manager tabs as the xtp session
4422 * key has now changed. No need to update the current tab.
4423 * Already did that above.
4425 update_history_tabs(t
);
4427 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4434 * Generate a web page detailing the status of any downloads
4437 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4439 struct download
*dl
;
4440 char *body
, *page
, *tmp
;
4444 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4447 show_oops(NULL
, "%s invalid parameters", __func__
);
4452 * Generate a new session key for next page instance.
4453 * This only happens for the top level call to xtp_page_dl()
4454 * in which case updating_dl_tabs is 0.
4456 if (!updating_dl_tabs
)
4457 generate_xtp_session_key(&dl_session_key
);
4459 /* header - with refresh so as to update */
4460 if (refresh_interval
>= 1)
4461 ref
= g_strdup_printf(
4462 "<meta http-equiv='refresh' content='%u"
4463 ";url=%s%d/%s/%d' />\n",
4472 body
= g_strdup_printf("<div align='center'>"
4473 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4474 "</p><table><tr><th style='width: 60%%'>"
4475 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4476 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4478 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4479 body
= xtp_page_dl_row(t
, body
, dl
);
4483 /* message if no downloads in list */
4486 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4487 " style='text-align: center'>"
4488 "No downloads</td></tr>\n", body
);
4493 body
= g_strdup_printf("%s</table></div>", body
);
4496 page
= get_html_page("Downloads", body
, ref
, 1);
4501 * update all download manager tabs as the xtp session
4502 * key has now changed. No need to update the current tab.
4503 * Already did that above.
4505 update_download_tabs(t
);
4507 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4514 search(struct tab
*t
, struct karg
*args
)
4518 if (t
== NULL
|| args
== NULL
) {
4519 show_oops(NULL
, "search invalid parameters");
4522 if (t
->search_text
== NULL
) {
4523 if (global_search
== NULL
)
4524 return (XT_CB_PASSTHROUGH
);
4526 t
->search_text
= g_strdup(global_search
);
4527 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4528 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4532 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4533 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4536 case XT_SEARCH_NEXT
:
4537 d
= t
->search_forward
;
4539 case XT_SEARCH_PREV
:
4540 d
= !t
->search_forward
;
4543 return (XT_CB_PASSTHROUGH
);
4546 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4548 return (XT_CB_HANDLED
);
4551 struct settings_args
{
4557 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4560 struct settings_args
*sa
= cb_args
;
4565 if (s
->flags
& XT_SF_RUNTIME
)
4571 *sa
->body
= g_strdup_printf(
4573 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4574 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4586 set(struct tab
*t
, struct karg
*args
)
4588 char *body
, *page
, *tmp
;
4590 struct settings_args sa
;
4592 bzero(&sa
, sizeof sa
);
4596 body
= g_strdup_printf("<div align='center'><table><tr>"
4597 "<th align='left'>Setting</th>"
4598 "<th align='left'>Value</th></tr>\n");
4600 settings_walk(print_setting
, &sa
);
4603 /* small message if there are none */
4606 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4607 "colspan='2'>No settings</td></tr>\n", body
);
4612 body
= g_strdup_printf("%s</table></div>", body
);
4615 page
= get_html_page("Settings", body
, "", 0);
4619 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4623 return (XT_CB_PASSTHROUGH
);
4627 session_save(struct tab
*t
, char *filename
)
4632 if (strlen(filename
) == 0)
4635 if (filename
[0] == '.' || filename
[0] == '/')
4639 if (save_tabs(t
, &a
))
4641 strlcpy(named_session
, filename
, sizeof named_session
);
4649 session_open(struct tab
*t
, char *filename
)
4654 if (strlen(filename
) == 0)
4657 if (filename
[0] == '.' || filename
[0] == '/')
4661 a
.i
= XT_SES_CLOSETABS
;
4662 if (open_tabs(t
, &a
))
4665 strlcpy(named_session
, filename
, sizeof named_session
);
4673 session_delete(struct tab
*t
, char *filename
)
4675 char file
[PATH_MAX
];
4678 if (strlen(filename
) == 0)
4681 if (filename
[0] == '.' || filename
[0] == '/')
4684 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4688 if (!strcmp(filename
, named_session
))
4689 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4690 sizeof named_session
);
4698 session_cmd(struct tab
*t
, struct karg
*args
)
4700 char *filename
= args
->s
;
4705 if (args
->i
& XT_SHOW
)
4706 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4707 XT_SAVED_TABS_FILE
: named_session
);
4708 else if (args
->i
& XT_SAVE
) {
4709 if (session_save(t
, filename
)) {
4710 show_oops(t
, "Can't save session: %s",
4711 filename
? filename
: "INVALID");
4714 } else if (args
->i
& XT_OPEN
) {
4715 if (session_open(t
, filename
)) {
4716 show_oops(t
, "Can't open session: %s",
4717 filename
? filename
: "INVALID");
4720 } else if (args
->i
& XT_DELETE
) {
4721 if (session_delete(t
, filename
)) {
4722 show_oops(t
, "Can't delete session: %s",
4723 filename
? filename
: "INVALID");
4728 return (XT_CB_PASSTHROUGH
);
4732 * Make a hardcopy of the page
4735 print_page(struct tab
*t
, struct karg
*args
)
4737 WebKitWebFrame
*frame
;
4739 GtkPrintOperation
*op
;
4740 GtkPrintOperationAction action
;
4741 GtkPrintOperationResult print_res
;
4742 GError
*g_err
= NULL
;
4743 int marg_l
, marg_r
, marg_t
, marg_b
;
4745 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4747 ps
= gtk_page_setup_new();
4748 op
= gtk_print_operation_new();
4749 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4750 frame
= webkit_web_view_get_main_frame(t
->wv
);
4752 /* the default margins are too small, so we will bump them */
4753 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4754 XT_PRINT_EXTRA_MARGIN
;
4755 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4756 XT_PRINT_EXTRA_MARGIN
;
4757 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4758 XT_PRINT_EXTRA_MARGIN
;
4759 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4760 XT_PRINT_EXTRA_MARGIN
;
4763 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4764 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4765 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4766 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4768 gtk_print_operation_set_default_page_setup(op
, ps
);
4770 /* this appears to free 'op' and 'ps' */
4771 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4773 /* check it worked */
4774 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4775 show_oops(NULL
, "can't print: %s", g_err
->message
);
4776 g_error_free (g_err
);
4784 go_home(struct tab
*t
, struct karg
*args
)
4791 restart(struct tab
*t
, struct karg
*args
)
4795 a
.s
= XT_RESTART_TABS_FILE
;
4797 execvp(start_argv
[0], start_argv
);
4803 #define CTRL GDK_CONTROL_MASK
4804 #define MOD1 GDK_MOD1_MASK
4805 #define SHFT GDK_SHIFT_MASK
4807 /* inherent to GTK not all keys will be caught at all times */
4808 /* XXX sort key bindings */
4809 struct key_binding
{
4814 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4816 { "cookiejar", MOD1
, 0, GDK_j
},
4817 { "downloadmgr", MOD1
, 0, GDK_d
},
4818 { "history", MOD1
, 0, GDK_h
},
4819 { "print", CTRL
, 0, GDK_p
},
4820 { "search", 0, 0, GDK_slash
},
4821 { "searchb", 0, 0, GDK_question
},
4822 { "statustoggle", CTRL
, 0, GDK_n
},
4823 { "command", 0, 0, GDK_colon
},
4824 { "qa", CTRL
, 0, GDK_q
},
4825 { "restart", MOD1
, 0, GDK_q
},
4826 { "js toggle", CTRL
, 0, GDK_j
},
4827 { "cookie toggle", MOD1
, 0, GDK_c
},
4828 { "togglesrc", CTRL
, 0, GDK_s
},
4829 { "yankuri", 0, 0, GDK_y
},
4830 { "pasteuricur", 0, 0, GDK_p
},
4831 { "pasteurinew", 0, 0, GDK_P
},
4832 { "toplevel toggle", 0, 0, GDK_F4
},
4833 { "help", 0, 0, GDK_F1
},
4836 { "searchnext", 0, 0, GDK_n
},
4837 { "searchprevious", 0, 0, GDK_N
},
4840 { "focusaddress", 0, 0, GDK_F6
},
4841 { "focussearch", 0, 0, GDK_F7
},
4844 { "hinting", 0, 0, GDK_f
},
4846 /* custom stylesheet */
4847 { "userstyle", 0, 0, GDK_i
},
4850 { "goback", 0, 0, GDK_BackSpace
},
4851 { "goback", MOD1
, 0, GDK_Left
},
4852 { "goforward", SHFT
, 0, GDK_BackSpace
},
4853 { "goforward", MOD1
, 0, GDK_Right
},
4854 { "reload", 0, 0, GDK_F5
},
4855 { "reload", CTRL
, 0, GDK_r
},
4856 { "reloadforce", CTRL
, 0, GDK_R
},
4857 { "reload", CTRL
, 0, GDK_l
},
4858 { "favorites", MOD1
, 1, GDK_f
},
4860 /* vertical movement */
4861 { "scrolldown", 0, 0, GDK_j
},
4862 { "scrolldown", 0, 0, GDK_Down
},
4863 { "scrollup", 0, 0, GDK_Up
},
4864 { "scrollup", 0, 0, GDK_k
},
4865 { "scrollbottom", 0, 0, GDK_G
},
4866 { "scrollbottom", 0, 0, GDK_End
},
4867 { "scrolltop", 0, 0, GDK_Home
},
4868 { "scrolltop", 0, 0, GDK_g
},
4869 { "scrollpagedown", 0, 0, GDK_space
},
4870 { "scrollpagedown", CTRL
, 0, GDK_f
},
4871 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4872 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4873 { "scrollpageup", 0, 0, GDK_Page_Up
},
4874 { "scrollpageup", CTRL
, 0, GDK_b
},
4875 { "scrollhalfup", CTRL
, 0, GDK_u
},
4876 /* horizontal movement */
4877 { "scrollright", 0, 0, GDK_l
},
4878 { "scrollright", 0, 0, GDK_Right
},
4879 { "scrollleft", 0, 0, GDK_Left
},
4880 { "scrollleft", 0, 0, GDK_h
},
4881 { "scrollfarright", 0, 0, GDK_dollar
},
4882 { "scrollfarleft", 0, 0, GDK_0
},
4885 { "tabnew", CTRL
, 0, GDK_t
},
4886 { "999tabnew", CTRL
, 0, GDK_T
},
4887 { "tabclose", CTRL
, 1, GDK_w
},
4888 { "tabundoclose", 0, 0, GDK_U
},
4889 { "tabnext 1", CTRL
, 0, GDK_1
},
4890 { "tabnext 2", CTRL
, 0, GDK_2
},
4891 { "tabnext 3", CTRL
, 0, GDK_3
},
4892 { "tabnext 4", CTRL
, 0, GDK_4
},
4893 { "tabnext 5", CTRL
, 0, GDK_5
},
4894 { "tabnext 6", CTRL
, 0, GDK_6
},
4895 { "tabnext 7", CTRL
, 0, GDK_7
},
4896 { "tabnext 8", CTRL
, 0, GDK_8
},
4897 { "tabnext 9", CTRL
, 0, GDK_9
},
4898 { "tabnext 10", CTRL
, 0, GDK_0
},
4899 { "tabfirst", CTRL
, 0, GDK_less
},
4900 { "tablast", CTRL
, 0, GDK_greater
},
4901 { "tabprevious", CTRL
, 0, GDK_Left
},
4902 { "tabnext", CTRL
, 0, GDK_Right
},
4903 { "focusout", CTRL
, 0, GDK_minus
},
4904 { "focusin", CTRL
, 0, GDK_plus
},
4905 { "focusin", CTRL
, 0, GDK_equal
},
4907 /* command aliases (handy when -S flag is used) */
4908 { "promptopen", 0, 0, GDK_F9
},
4909 { "promptopencurrent", 0, 0, GDK_F10
},
4910 { "prompttabnew", 0, 0, GDK_F11
},
4911 { "prompttabnewcurrent",0, 0, GDK_F12
},
4913 TAILQ_HEAD(keybinding_list
, key_binding
);
4916 walk_kb(struct settings
*s
,
4917 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4919 struct key_binding
*k
;
4922 if (s
== NULL
|| cb
== NULL
) {
4923 show_oops(NULL
, "walk_kb invalid parameters");
4927 TAILQ_FOREACH(k
, &kbl
, entry
) {
4933 if (gdk_keyval_name(k
->key
) == NULL
)
4936 strlcat(str
, k
->cmd
, sizeof str
);
4937 strlcat(str
, ",", sizeof str
);
4939 if (k
->mask
& GDK_SHIFT_MASK
)
4940 strlcat(str
, "S-", sizeof str
);
4941 if (k
->mask
& GDK_CONTROL_MASK
)
4942 strlcat(str
, "C-", sizeof str
);
4943 if (k
->mask
& GDK_MOD1_MASK
)
4944 strlcat(str
, "M1-", sizeof str
);
4945 if (k
->mask
& GDK_MOD2_MASK
)
4946 strlcat(str
, "M2-", sizeof str
);
4947 if (k
->mask
& GDK_MOD3_MASK
)
4948 strlcat(str
, "M3-", sizeof str
);
4949 if (k
->mask
& GDK_MOD4_MASK
)
4950 strlcat(str
, "M4-", sizeof str
);
4951 if (k
->mask
& GDK_MOD5_MASK
)
4952 strlcat(str
, "M5-", sizeof str
);
4954 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4955 cb(s
, str
, cb_args
);
4960 init_keybindings(void)
4963 struct key_binding
*k
;
4965 for (i
= 0; i
< LENGTH(keys
); i
++) {
4966 k
= g_malloc0(sizeof *k
);
4967 k
->cmd
= keys
[i
].cmd
;
4968 k
->mask
= keys
[i
].mask
;
4969 k
->use_in_entry
= keys
[i
].use_in_entry
;
4970 k
->key
= keys
[i
].key
;
4971 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4973 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4974 k
->cmd
? k
->cmd
: "unnamed key");
4979 keybinding_clearall(void)
4981 struct key_binding
*k
, *next
;
4983 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4984 next
= TAILQ_NEXT(k
, entry
);
4988 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4989 k
->cmd
? k
->cmd
: "unnamed key");
4990 TAILQ_REMOVE(&kbl
, k
, entry
);
4996 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
4998 struct key_binding
*k
;
4999 guint keyval
, mask
= 0;
5002 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5004 /* Keys which are to be used in entry have been prefixed with an
5005 * exclamation mark. */
5009 /* find modifier keys */
5010 if (strstr(key
, "S-"))
5011 mask
|= GDK_SHIFT_MASK
;
5012 if (strstr(key
, "C-"))
5013 mask
|= GDK_CONTROL_MASK
;
5014 if (strstr(key
, "M1-"))
5015 mask
|= GDK_MOD1_MASK
;
5016 if (strstr(key
, "M2-"))
5017 mask
|= GDK_MOD2_MASK
;
5018 if (strstr(key
, "M3-"))
5019 mask
|= GDK_MOD3_MASK
;
5020 if (strstr(key
, "M4-"))
5021 mask
|= GDK_MOD4_MASK
;
5022 if (strstr(key
, "M5-"))
5023 mask
|= GDK_MOD5_MASK
;
5026 for (i
= strlen(key
) - 1; i
> 0; i
--)
5030 /* validate keyname */
5031 keyval
= gdk_keyval_from_name(key
);
5032 if (keyval
== GDK_VoidSymbol
) {
5033 warnx("invalid keybinding name %s", key
);
5036 /* must run this test too, gtk+ doesn't handle 10 for example */
5037 if (gdk_keyval_name(keyval
) == NULL
) {
5038 warnx("invalid keybinding name %s", key
);
5042 /* Remove eventual dupes. */
5043 TAILQ_FOREACH(k
, &kbl
, entry
)
5044 if (k
->key
== keyval
&& k
->mask
== mask
) {
5045 TAILQ_REMOVE(&kbl
, k
, entry
);
5051 k
= g_malloc0(sizeof *k
);
5052 k
->cmd
= g_strdup(cmd
);
5054 k
->use_in_entry
= use_in_entry
;
5057 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5062 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5063 k
->cmd
, gdk_keyval_name(keyval
));
5065 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5071 add_kb(struct settings
*s
, char *entry
)
5075 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5077 /* clearall is special */
5078 if (!strcmp(entry
, "clearall")) {
5079 keybinding_clearall();
5083 kb
= strstr(entry
, ",");
5089 return (keybinding_add(entry
, key
, key
[0] == '!'));
5095 int (*func
)(struct tab
*, struct karg
*);
5099 { "command", 0, command
, ':', 0 },
5100 { "search", 0, command
, '/', 0 },
5101 { "searchb", 0, command
, '?', 0 },
5102 { "togglesrc", 0, toggle_src
, 0, 0 },
5104 /* yanking and pasting */
5105 { "yankuri", 0, yank_uri
, 0, 0 },
5106 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5107 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5108 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5111 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5112 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5115 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5116 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5119 { "hinting", 0, hint
, 0, 0 },
5121 /* custom stylesheet */
5122 { "userstyle", 0, userstyle
, 0, 0 },
5125 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5126 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5127 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5128 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
5130 /* vertical movement */
5131 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5132 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5133 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5134 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5135 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5136 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5137 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5138 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5139 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5140 /* horizontal movement */
5141 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5142 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5143 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5144 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5147 { "favorites", 0, xtp_page_fl
, 0, 0 },
5148 { "fav", 0, xtp_page_fl
, 0, 0 },
5149 { "favadd", 0, add_favorite
, 0, 0 },
5151 { "qall", 0, quit
, 0, 0 },
5152 { "quitall", 0, quit
, 0, 0 },
5153 { "w", 0, save_tabs
, 0, 0 },
5154 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5155 { "help", 0, help
, 0, 0 },
5156 { "about", 0, about
, 0, 0 },
5157 { "stats", 0, stats
, 0, 0 },
5158 { "version", 0, about
, 0, 0 },
5161 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5162 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5163 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5164 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5165 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5166 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5167 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5168 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5169 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5170 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5171 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5173 /* cookie command */
5174 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5175 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5176 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5177 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5178 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5179 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5180 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5181 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5182 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5183 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5184 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5186 /* toplevel (domain) command */
5187 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5188 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5191 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5194 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5195 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5196 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5198 { "ca", 0, ca_cmd
, 0, 0 },
5199 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5200 { "dl", 0, xtp_page_dl
, 0, 0 },
5201 { "h", 0, xtp_page_hl
, 0, 0 },
5202 { "history", 0, xtp_page_hl
, 0, 0 },
5203 { "home", 0, go_home
, 0, 0 },
5204 { "restart", 0, restart
, 0, 0 },
5205 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5206 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5207 { "statustoggle", 0, statustoggle
, 0, 0 },
5209 { "print", 0, print_page
, 0, 0 },
5212 { "focusin", 0, resizetab
, 1, 0 },
5213 { "focusout", 0, resizetab
, -1, 0 },
5214 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5215 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5216 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5217 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5218 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5219 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5220 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5221 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5222 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5223 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5224 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5225 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5226 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5227 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5228 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5229 { "buffers", 0, buffers
, 0, 0 },
5230 { "ls", 0, buffers
, 0, 0 },
5231 { "tabs", 0, buffers
, 0, 0 },
5233 /* command aliases (handy when -S flag is used) */
5234 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5235 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5236 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5237 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5240 { "set", 0, set
, 0, 0 },
5241 { "fullscreen", 0, fullscreen
, 0, 0 },
5242 { "f", 0, fullscreen
, 0, 0 },
5245 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5246 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5247 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5248 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5249 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5256 } cmd_status
= {-1, 0};
5259 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5262 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5269 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5276 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5278 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5284 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5286 a
.i
= XT_NAV_FORWARD
;
5296 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5298 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5300 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5307 * cancel, remove, etc. downloads
5310 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5312 struct download find
, *d
= NULL
;
5314 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5316 /* some commands require a valid download id */
5317 if (cmd
!= XT_XTP_DL_LIST
) {
5318 /* lookup download in question */
5320 d
= RB_FIND(download_list
, &downloads
, &find
);
5323 show_oops(t
, "%s: no such download", __func__
);
5328 /* decide what to do */
5330 case XT_XTP_DL_CANCEL
:
5331 webkit_download_cancel(d
->download
);
5333 case XT_XTP_DL_REMOVE
:
5334 webkit_download_cancel(d
->download
); /* just incase */
5335 g_object_unref(d
->download
);
5336 RB_REMOVE(download_list
, &downloads
, d
);
5338 case XT_XTP_DL_LIST
:
5342 show_oops(t
, "%s: unknown command", __func__
);
5345 xtp_page_dl(t
, NULL
);
5349 * Actions on history, only does one thing for now, but
5350 * we provide the function for future actions
5353 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5355 struct history
*h
, *next
;
5359 case XT_XTP_HL_REMOVE
:
5360 /* walk backwards, as listed in reverse */
5361 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5362 next
= RB_PREV(history_list
, &hl
, h
);
5364 RB_REMOVE(history_list
, &hl
, h
);
5365 g_free((gpointer
) h
->title
);
5366 g_free((gpointer
) h
->uri
);
5373 case XT_XTP_HL_LIST
:
5374 /* Nothing - just xtp_page_hl() below */
5377 show_oops(t
, "%s: unknown command", __func__
);
5381 xtp_page_hl(t
, NULL
);
5384 /* remove a favorite */
5386 remove_favorite(struct tab
*t
, int index
)
5388 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5389 char *new_favs
, *tmp
;
5394 /* open favorites */
5395 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5397 if ((f
= fopen(file
, "r")) == NULL
) {
5398 show_oops(t
, "%s: can't open favorites: %s",
5399 __func__
, strerror(errno
));
5403 /* build a string which will become the new favroites file */
5404 new_favs
= g_strdup("");
5407 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5408 if (feof(f
) || ferror(f
))
5410 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5417 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5418 if (feof(f
) || ferror(f
)) {
5419 show_oops(t
, "%s: can't parse favorites %s",
5420 __func__
, strerror(errno
));
5425 /* as long as this isn't the one we are deleting add to file */
5428 new_favs
= g_strdup_printf("%s%s\n%s\n",
5429 new_favs
, title
, uri
);
5441 /* write back new favorites file */
5442 if ((f
= fopen(file
, "w")) == NULL
) {
5443 show_oops(t
, "%s: can't open favorites: %s",
5444 __func__
, strerror(errno
));
5448 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5461 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5464 case XT_XTP_FL_LIST
:
5465 /* nothing, just the below call to xtp_page_fl() */
5467 case XT_XTP_FL_REMOVE
:
5468 remove_favorite(t
, arg
);
5471 show_oops(t
, "%s: invalid favorites command", __func__
);
5475 xtp_page_fl(t
, NULL
);
5479 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5482 case XT_XTP_CL_LIST
:
5483 /* nothing, just xtp_page_cl() */
5485 case XT_XTP_CL_REMOVE
:
5489 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5493 xtp_page_cl(t
, NULL
);
5496 /* link an XTP class to it's session key and handler function */
5497 struct xtp_despatch
{
5500 void (*handle_func
)(struct tab
*, uint8_t, int);
5503 struct xtp_despatch xtp_despatches
[] = {
5504 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5505 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5506 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5507 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5508 { XT_XTP_INVALID
, NULL
, NULL
}
5512 * is the url xtp protocol? (xxxt://)
5513 * if so, parse and despatch correct bahvior
5516 parse_xtp_url(struct tab
*t
, const char *url
)
5518 char *dup
= NULL
, *p
, *last
;
5519 uint8_t n_tokens
= 0;
5520 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5521 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5526 * tokens array meaning:
5528 * tokens[1] = session key
5529 * tokens[2] = action
5530 * tokens[3] = optional argument
5533 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5535 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5538 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5540 /* split out the url */
5541 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5542 (p
= strtok_r(NULL
, "/", &last
))) {
5544 tokens
[n_tokens
++] = p
;
5547 /* should be atleast three fields 'class/seskey/command/arg' */
5551 dsp
= xtp_despatches
;
5552 req_class
= atoi(tokens
[0]);
5553 while (dsp
->xtp_class
) {
5554 if (dsp
->xtp_class
== req_class
) {
5561 /* did we find one atall? */
5562 if (dsp_match
== NULL
) {
5563 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5567 /* check session key and call despatch function */
5568 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5569 ret
= TRUE
; /* all is well, this was a valid xtp request */
5570 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5583 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5585 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5587 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5590 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5595 show_oops(t
, "activate_uri_entry_cb no uri");
5599 uri
+= strspn(uri
, "\t ");
5601 /* if xxxt:// treat specially */
5602 if (parse_xtp_url(t
, uri
))
5605 /* otherwise continue to load page normally */
5606 load_uri(t
, (gchar
*)uri
);
5611 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5613 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5614 char *newuri
= NULL
;
5617 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5620 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5624 if (search_string
== NULL
) {
5625 show_oops(t
, "no search_string");
5629 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5630 newuri
= g_strdup_printf(search_string
, enc_search
);
5633 webkit_web_view_load_uri(t
->wv
, newuri
);
5641 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5643 struct domain
*d
= NULL
;
5646 if (uri
== NULL
|| t
== NULL
)
5649 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5654 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5655 es
? "enable" : "disable", uri
);
5657 g_object_set(G_OBJECT(t
->settings
),
5658 "enable-scripts", es
, (char *)NULL
);
5659 g_object_set(G_OBJECT(t
->settings
),
5660 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5661 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5663 button_set_stockid(t
->js_toggle
,
5664 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5668 show_ca_status(struct tab
*t
, const char *uri
)
5670 WebKitWebFrame
*frame
;
5671 WebKitWebDataSource
*source
;
5672 WebKitNetworkRequest
*request
;
5673 SoupMessage
*message
;
5675 gchar
*col_str
= XT_COLOR_WHITE
;
5678 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5679 ssl_strict_certs
, ssl_ca_file
, uri
);
5683 if (ssl_ca_file
== NULL
) {
5684 if (g_str_has_prefix(uri
, "http://"))
5686 if (g_str_has_prefix(uri
, "https://")) {
5687 col_str
= XT_COLOR_RED
;
5692 if (g_str_has_prefix(uri
, "http://") ||
5693 !g_str_has_prefix(uri
, "https://"))
5696 frame
= webkit_web_view_get_main_frame(t
->wv
);
5697 source
= webkit_web_frame_get_data_source(frame
);
5698 request
= webkit_web_data_source_get_request(source
);
5699 message
= webkit_network_request_get_message(request
);
5701 if (message
&& (soup_message_get_flags(message
) &
5702 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5703 col_str
= XT_COLOR_GREEN
;
5706 r
= load_compare_cert(t
, NULL
);
5708 col_str
= XT_COLOR_BLUE
;
5710 col_str
= XT_COLOR_YELLOW
;
5712 col_str
= XT_COLOR_RED
;
5717 gdk_color_parse(col_str
, &color
);
5718 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5720 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5721 gtk_widget_modify_text(t
->sbe
.statusbar
,
5722 GTK_STATE_NORMAL
, &color
);
5723 gtk_widget_modify_text(t
->sbe
.position
,
5724 GTK_STATE_NORMAL
, &color
);
5725 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5726 gtk_widget_modify_base(t
->sbe
.statusbar
,
5727 GTK_STATE_NORMAL
, &color
);
5728 gtk_widget_modify_base(t
->sbe
.position
,
5729 GTK_STATE_NORMAL
, &color
);
5731 gtk_widget_modify_base(t
->sbe
.statusbar
,
5732 GTK_STATE_NORMAL
, &color
);
5733 gtk_widget_modify_base(t
->sbe
.position
,
5734 GTK_STATE_NORMAL
, &color
);
5735 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5736 gtk_widget_modify_text(t
->sbe
.statusbar
,
5737 GTK_STATE_NORMAL
, &color
);
5738 gtk_widget_modify_text(t
->sbe
.position
,
5739 GTK_STATE_NORMAL
, &color
);
5745 free_favicon(struct tab
*t
)
5747 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5748 __func__
, t
->icon_download
, t
->icon_request
);
5750 if (t
->icon_request
)
5751 g_object_unref(t
->icon_request
);
5752 if (t
->icon_dest_uri
)
5753 g_free(t
->icon_dest_uri
);
5755 t
->icon_request
= NULL
;
5756 t
->icon_dest_uri
= NULL
;
5760 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5762 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5763 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5765 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5766 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5768 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5769 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5773 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5775 GdkPixbuf
*pb_scaled
;
5777 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5778 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
5779 GDK_INTERP_BILINEAR
);
5783 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5784 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5786 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
5787 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5789 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5790 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5792 if (pb_scaled
!= pb
)
5793 g_object_unref(pb_scaled
);
5797 xt_icon_from_file(struct tab
*t
, char *file
)
5801 if (g_str_has_prefix(file
, "file://"))
5802 file
+= strlen("file://");
5804 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5806 xt_icon_from_pixbuf(t
, pb
);
5809 xt_icon_from_name(t
, "text-html");
5813 is_valid_icon(char *file
)
5816 const char *mime_type
;
5820 gf
= g_file_new_for_path(file
);
5821 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5823 mime_type
= g_file_info_get_content_type(fi
);
5824 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5825 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5826 g_strcmp0(mime_type
, "image/png") == 0 ||
5827 g_strcmp0(mime_type
, "image/gif") == 0 ||
5828 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5836 set_favicon_from_file(struct tab
*t
, char *file
)
5840 if (t
== NULL
|| file
== NULL
)
5843 if (g_str_has_prefix(file
, "file://"))
5844 file
+= strlen("file://");
5845 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5847 if (!stat(file
, &sb
)) {
5848 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5849 /* corrupt icon so trash it */
5850 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5853 /* no need to set icon to default here */
5857 xt_icon_from_file(t
, file
);
5861 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5864 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5865 struct tab
*tt
= NULL
, *t
= NULL
;
5868 * find the webview instead of passing in the tab as it could have been
5869 * deleted from underneath us.
5871 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5880 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5881 __func__
, t
->tab_id
, status
);
5884 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5886 t
->icon_download
= NULL
;
5889 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5892 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5895 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5897 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5898 __func__
, t
->tab_id
);
5899 t
->icon_download
= NULL
;
5902 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5905 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5906 __func__
, t
->icon_dest_uri
);
5907 set_favicon_from_file(t
, t
->icon_dest_uri
);
5908 /* these will be freed post callback */
5909 t
->icon_request
= NULL
;
5910 t
->icon_download
= NULL
;
5918 abort_favicon_download(struct tab
*t
)
5920 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5922 if (!WEBKIT_CHECK_VERSION(1, 4, 0)) {
5923 if (t
->icon_download
) {
5924 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5925 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5926 webkit_download_cancel(t
->icon_download
);
5927 t
->icon_download
= NULL
;
5932 xt_icon_from_name(t
, "text-html");
5936 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5939 gchar
*name_hash
, file
[PATH_MAX
];
5942 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
5944 if (uri
== NULL
|| t
== NULL
)
5947 if (WEBKIT_CHECK_VERSION(1, 4, 0)) {
5948 /* take icon from WebKitIconDatabase */
5949 pb
= webkit_web_view_get_icon_pixbuf(wv
);
5951 xt_icon_from_pixbuf(t
, pb
);
5954 xt_icon_from_name(t
, "text-html");
5956 } else if (WEBKIT_CHECK_VERSION(1, 1, 18)) {
5957 /* download icon to cache dir */
5958 if (t
->icon_request
) {
5959 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5963 /* check to see if we got the icon in cache */
5964 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5965 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5968 if (!stat(file
, &sb
)) {
5969 if (sb
.st_size
> 0) {
5970 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5972 set_favicon_from_file(t
, file
);
5976 /* corrupt icon so trash it */
5977 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5982 /* create download for icon */
5983 t
->icon_request
= webkit_network_request_new(uri
);
5984 if (t
->icon_request
== NULL
) {
5985 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5990 t
->icon_download
= webkit_download_new(t
->icon_request
);
5991 if (t
->icon_download
== NULL
)
5994 /* we have to free icon_dest_uri later */
5995 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5996 webkit_download_set_destination_uri(t
->icon_download
,
5999 if (webkit_download_get_status(t
->icon_download
) ==
6000 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6001 g_object_unref(t
->icon_request
);
6002 g_free(t
->icon_dest_uri
);
6003 t
->icon_request
= NULL
;
6004 t
->icon_dest_uri
= NULL
;
6008 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6009 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6011 webkit_download_start(t
->icon_download
);
6016 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6018 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
6019 struct history
*h
, find
;
6020 const gchar
*s_loading
;
6023 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6024 webkit_web_view_get_load_status(wview
), get_uri(t
) ? get_uri(t
) : "NOTHING");
6027 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6031 switch (webkit_web_view_get_load_status(wview
)) {
6032 case WEBKIT_LOAD_PROVISIONAL
:
6034 abort_favicon_download(t
);
6035 #if GTK_CHECK_VERSION(2, 20, 0)
6036 gtk_widget_show(t
->spinner
);
6037 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6039 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6041 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6043 /* take focus if we are visible */
6049 case WEBKIT_LOAD_COMMITTED
:
6051 if ((uri
= get_uri(t
)) != NULL
) {
6052 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6058 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6061 /* check if js white listing is enabled */
6062 if (enable_js_whitelist
) {
6064 check_and_set_js(uri
, t
);
6070 show_ca_status(t
, uri
);
6072 /* we know enough to autosave the session */
6073 if (session_autosave
) {
6079 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6083 case WEBKIT_LOAD_FINISHED
:
6089 if (!strncmp(uri
, "http://", strlen("http://")) ||
6090 !strncmp(uri
, "https://", strlen("https://")) ||
6091 !strncmp(uri
, "file://", strlen("file://"))) {
6093 h
= RB_FIND(history_list
, &hl
, &find
);
6095 title
= webkit_web_view_get_title(wview
);
6096 set
= title
? title
: uri
;
6097 h
= g_malloc(sizeof *h
);
6098 h
->uri
= g_strdup(uri
);
6099 h
->title
= g_strdup(set
);
6100 RB_INSERT(history_list
, &hl
, h
);
6101 completion_add_uri(h
->uri
);
6102 update_history_tabs(NULL
);
6106 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6107 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6108 case WEBKIT_LOAD_FAILED
:
6111 #if GTK_CHECK_VERSION(2, 20, 0)
6112 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6113 gtk_widget_hide(t
->spinner
);
6115 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
6116 if (s_loading
&& !strcmp(s_loading
, "Loading"))
6117 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6119 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6124 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6126 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6127 webkit_web_view_can_go_back(wview
));
6129 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6130 webkit_web_view_can_go_forward(wview
));
6134 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6136 const gchar
*set
= NULL
, *title
= NULL
;
6138 title
= webkit_web_view_get_title(wview
);
6139 set
= title
? title
: get_uri(t
);
6141 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
6142 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), set
);
6143 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
6145 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6146 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), "(untitled)");
6147 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
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
;
6543 /* don't use w directly; use t->whatever instead */
6546 show_oops(NULL
, "wv_keypress_after_cb");
6547 return (XT_CB_PASSTHROUGH
);
6550 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6551 e
->keyval
, e
->state
, t
);
6555 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6557 return (XT_CB_HANDLED
);
6561 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6562 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6564 /* we have a string */
6566 /* we have a number */
6567 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6575 /* XXX unfuck this */
6576 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6577 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6578 /* last input was numerical */
6580 l
= strlen(t
->hint_num
);
6587 t
->hint_num
[l
] = '\0';
6591 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6592 /* last input was alphanumerical */
6594 l
= strlen(t
->hint_buf
);
6601 t
->hint_buf
[l
] = '\0';
6611 /* numerical input */
6612 if (CLEAN(e
->state
) == 0 &&
6613 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6614 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6615 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6616 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6619 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6621 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6624 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6626 t
->hint_mode
= XT_HINT_NUMERICAL
;
6630 /* empty the counter buffer */
6631 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6632 return (XT_CB_HANDLED
);
6635 /* alphanumerical input */
6637 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6638 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6639 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6640 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6641 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6642 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6643 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6646 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6649 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6651 t
->hint_mode
= XT_HINT_ALPHANUM
;
6654 /* empty the counter buffer */
6655 bzero(t
->hint_num
, sizeof t
->hint_num
);
6656 return (XT_CB_HANDLED
);
6659 return (XT_CB_HANDLED
);
6662 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6663 if (CLEAN(e
->state
) == 0 && isdigit(s
[0])) {
6664 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
6668 return (handle_keypress(t
, e
, 0));
6672 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6676 /* Hide buffers, if they are visible, with escape. */
6677 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
6678 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6679 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6681 return (XT_CB_HANDLED
);
6684 return (XT_CB_PASSTHROUGH
);
6688 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6690 const gchar
*c
= gtk_entry_get_text(w
);
6694 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6695 e
->keyval
, e
->state
, t
);
6698 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
6699 return (XT_CB_PASSTHROUGH
);
6702 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6703 e
->keyval
, e
->state
, t
);
6707 if (strlen(c
) == 1) {
6708 webkit_web_view_unmark_text_matches(t
->wv
);
6714 else if (c
[0] == '?')
6720 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6722 /* not found, mark red */
6723 gdk_color_parse(XT_COLOR_RED
, &color
);
6724 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6725 /* unmark and remove selection */
6726 webkit_web_view_unmark_text_matches(t
->wv
);
6727 /* my kingdom for a way to unselect text in webview */
6729 /* found, highlight all */
6730 webkit_web_view_unmark_text_matches(t
->wv
);
6731 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6732 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6733 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6734 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6737 return (XT_CB_PASSTHROUGH
);
6741 match_uri(const gchar
*uri
, const gchar
*key
) {
6744 gboolean match
= FALSE
;
6748 if (!strncmp(key
, uri
, len
))
6751 voffset
= strstr(uri
, "/") + 2;
6752 if (!strncmp(key
, voffset
, len
))
6754 else if (g_str_has_prefix(voffset
, "www.")) {
6755 voffset
= voffset
+ strlen("www.");
6756 if (!strncmp(key
, voffset
, len
))
6765 cmd_getlist(int id
, char *key
)
6770 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
6771 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6772 if (match_uri(h
->uri
, key
)) {
6773 cmd_status
.list
[c
] = (char *)h
->uri
;
6782 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6784 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6785 if(cmds
[i
].level
< dep
)
6787 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6788 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6796 cmd_getnext(int dir
)
6798 cmd_status
.index
+= dir
;
6800 if (cmd_status
.index
< 0)
6801 cmd_status
.index
= cmd_status
.len
- 1;
6802 else if (cmd_status
.index
>= cmd_status
.len
)
6803 cmd_status
.index
= 0;
6805 return cmd_status
.list
[cmd_status
.index
];
6809 cmd_tokenize(char *s
, char *tokens
[])
6813 size_t len
= strlen(s
);
6814 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6816 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6826 cmd_complete(struct tab
*t
, char *str
, int dir
)
6828 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6829 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1, matchcount
= 0;
6830 char *tok
, *match
, *s
= g_strdup(str
);
6832 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6835 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
6838 for (i
= 0; isdigit(s
[i
]); i
++)
6841 for (; isspace(s
[i
]); i
++)
6846 levels
= cmd_tokenize(s
, tokens
);
6848 for (i
= 0; i
< levels
- 1; i
++) {
6851 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6852 if (cmds
[j
].level
< dep
)
6854 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, strlen(tok
))) {
6857 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
6864 if (matchcount
== 1) {
6865 strlcat(res
, tok
, sizeof res
);
6866 strlcat(res
, " ", sizeof res
);
6876 if (cmd_status
.index
== -1)
6877 cmd_getlist(parent
, tokens
[i
]);
6879 if (cmd_status
.len
> 0) {
6880 match
= cmd_getnext(dir
);
6881 strlcat(res
, match
, sizeof res
);
6882 gtk_entry_set_text(w
, res
);
6883 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6890 cmd_execute(struct tab
*t
, char *str
)
6892 struct cmd
*cmd
= NULL
;
6893 char *tok
, *last
, *s
= g_strdup(str
), *sc
, prefixstr
[4];
6894 int j
, len
, c
= 0, dep
= 0, matchcount
= 0, prefix
= -1;
6895 struct karg arg
= {0, NULL
, -1};
6896 int rv
= XT_CB_PASSTHROUGH
;
6901 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
6907 while (isspace(s
[0]))
6910 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
6911 prefix
= atoi(prefixstr
);
6915 for (tok
= strtok_r(s
, " ", &last
); tok
;
6916 tok
= strtok_r(NULL
, " ", &last
)) {
6918 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6919 if (cmds
[j
].level
< dep
)
6921 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1: strlen(tok
);
6922 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, len
)) {
6926 if (len
== strlen(cmds
[j
].cmd
)) {
6932 if (matchcount
== 1) {
6937 show_oops(t
, "Invalid command: %s", str
);
6946 else if (cmd_prefix
> 0)
6949 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
6950 show_oops(t
, "No prefix allowed: %s", str
);
6954 arg
.s
= last
? g_strdup(last
) : g_strdup("");
6955 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
6956 arg
.p
= atoi(arg
.s
);
6959 show_oops(t
, "Zero count");
6961 show_oops(t
, "Trailing characters");
6966 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
6982 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6985 show_oops(NULL
, "entry_key_cb invalid parameters");
6986 return (XT_CB_PASSTHROUGH
);
6989 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6990 e
->keyval
, e
->state
, t
);
6994 if (e
->keyval
== GDK_Escape
) {
6995 /* don't use focus_webview(t) because we want to type :cmds */
6996 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6999 return (handle_keypress(t
, e
, 1));
7003 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7005 int rv
= XT_CB_HANDLED
;
7006 const gchar
*c
= gtk_entry_get_text(w
);
7009 show_oops(NULL
, "cmd_keypress_cb parameters");
7010 return (XT_CB_PASSTHROUGH
);
7013 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7014 e
->keyval
, e
->state
, t
);
7018 e
->keyval
= GDK_Escape
;
7019 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7020 e
->keyval
= GDK_Escape
;
7022 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
7023 cmd_status
.index
= -1;
7025 switch (e
->keyval
) {
7028 cmd_complete(t
, (char *)&c
[1], 1);
7030 case GDK_ISO_Left_Tab
:
7032 cmd_complete(t
, (char *)&c
[1], -1);
7036 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7044 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7045 webkit_web_view_unmark_text_matches(t
->wv
);
7049 rv
= XT_CB_PASSTHROUGH
;
7055 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7058 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7059 return (XT_CB_PASSTHROUGH
);
7061 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7066 if (show_url
== 0 || t
->focus_wv
)
7069 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7071 return (XT_CB_PASSTHROUGH
);
7075 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7078 const gchar
*c
= gtk_entry_get_text(entry
);
7081 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7085 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7092 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7098 if (c
[0] == '/' || c
[0] == '?') {
7099 if (t
->search_text
) {
7100 g_free(t
->search_text
);
7101 t
->search_text
= NULL
;
7104 t
->search_text
= g_strdup(s
);
7106 g_free(global_search
);
7107 global_search
= g_strdup(s
);
7108 t
->search_forward
= c
[0] == '/';
7120 backward_cb(GtkWidget
*w
, struct tab
*t
)
7125 show_oops(NULL
, "backward_cb invalid parameters");
7129 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7136 forward_cb(GtkWidget
*w
, struct tab
*t
)
7141 show_oops(NULL
, "forward_cb invalid parameters");
7145 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7147 a
.i
= XT_NAV_FORWARD
;
7152 home_cb(GtkWidget
*w
, struct tab
*t
)
7155 show_oops(NULL
, "home_cb invalid parameters");
7159 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7165 stop_cb(GtkWidget
*w
, struct tab
*t
)
7167 WebKitWebFrame
*frame
;
7170 show_oops(NULL
, "stop_cb invalid parameters");
7174 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7176 frame
= webkit_web_view_get_main_frame(t
->wv
);
7177 if (frame
== NULL
) {
7178 show_oops(t
, "stop_cb: no frame");
7182 webkit_web_frame_stop_loading(frame
);
7183 abort_favicon_download(t
);
7187 setup_webkit(struct tab
*t
)
7189 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7190 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7191 FALSE
, (char *)NULL
);
7193 warnx("webkit does not have \"enable-dns-prefetching\" property");
7194 g_object_set(G_OBJECT(t
->settings
),
7195 "user-agent", t
->user_agent
, (char *)NULL
);
7196 g_object_set(G_OBJECT(t
->settings
),
7197 "enable-scripts", enable_scripts
, (char *)NULL
);
7198 g_object_set(G_OBJECT(t
->settings
),
7199 "enable-plugins", enable_plugins
, (char *)NULL
);
7200 g_object_set(G_OBJECT(t
->settings
),
7201 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
7202 g_object_set(G_OBJECT(t
->settings
),
7203 "enable-html5-database", FALSE
, (char *)NULL
);
7204 g_object_set(G_OBJECT(t
->settings
),
7205 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7206 g_object_set(G_OBJECT(t
->settings
),
7207 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7208 g_object_set(G_OBJECT(t
->settings
),
7209 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7210 g_object_set(G_OBJECT(t
->wv
),
7211 "full-content-zoom", TRUE
, (char *)NULL
);
7212 adjustfont_webkit(t
, XT_FONT_SET
);
7214 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7218 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
7220 struct tab
*ti
, *t
= NULL
;
7221 gdouble view_size
, value
, max
;
7224 TAILQ_FOREACH(ti
, &tabs
, entry
)
7225 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
7233 if (adjustment
== NULL
)
7234 adjustment
= gtk_scrolled_window_get_vadjustment(
7235 GTK_SCROLLED_WINDOW(t
->browser_win
));
7237 view_size
= gtk_adjustment_get_page_size(adjustment
);
7238 value
= gtk_adjustment_get_value(adjustment
);
7239 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
7242 position
= g_strdup("All");
7243 else if (value
== max
)
7244 position
= g_strdup("Bot");
7245 else if (value
== 0)
7246 position
= g_strdup("Top");
7248 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
7250 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
7257 create_browser(struct tab
*t
)
7261 GtkAdjustment
*adjustment
;
7264 show_oops(NULL
, "create_browser invalid parameters");
7268 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7269 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7270 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7271 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7273 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7274 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7275 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7277 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7278 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7281 t
->settings
= webkit_web_settings_new();
7283 if (user_agent
== NULL
) {
7284 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7286 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7289 t
->user_agent
= g_strdup(user_agent
);
7291 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7294 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
7295 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
7296 G_CALLBACK(update_statusbar_position
), NULL
);
7308 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7309 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7310 gtk_widget_set_name(w
, "xxxterm");
7311 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7312 g_signal_connect(G_OBJECT(w
), "delete_event",
7313 G_CALLBACK (gtk_main_quit
), NULL
);
7319 create_kiosk_toolbar(struct tab
*t
)
7321 GtkWidget
*toolbar
= NULL
, *b
;
7323 b
= gtk_hbox_new(FALSE
, 0);
7325 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7327 /* backward button */
7328 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7329 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7330 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7331 G_CALLBACK(backward_cb
), t
);
7332 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7334 /* forward button */
7335 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7336 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7337 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7338 G_CALLBACK(forward_cb
), t
);
7339 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7342 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7343 gtk_widget_set_sensitive(t
->gohome
, true);
7344 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7345 G_CALLBACK(home_cb
), t
);
7346 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7348 /* create widgets but don't use them */
7349 t
->uri_entry
= gtk_entry_new();
7350 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7351 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7352 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7358 create_toolbar(struct tab
*t
)
7360 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7362 b
= gtk_hbox_new(FALSE
, 0);
7364 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7367 /* backward button */
7368 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7369 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7370 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7371 G_CALLBACK(backward_cb
), t
);
7372 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7374 /* forward button */
7375 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7376 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7377 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7378 G_CALLBACK(forward_cb
), t
);
7379 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7383 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7384 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7385 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7386 G_CALLBACK(stop_cb
), t
);
7387 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7391 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7392 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7393 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7394 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7395 G_CALLBACK(js_toggle_cb
), t
);
7396 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7399 t
->uri_entry
= gtk_entry_new();
7400 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7401 G_CALLBACK(activate_uri_entry_cb
), t
);
7402 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7403 G_CALLBACK(entry_key_cb
), t
);
7405 eb1
= gtk_hbox_new(FALSE
, 0);
7406 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7407 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7408 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7411 if (fancy_bar
&& search_string
) {
7413 t
->search_entry
= gtk_entry_new();
7414 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7415 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7416 G_CALLBACK(activate_search_entry_cb
), t
);
7417 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7418 G_CALLBACK(entry_key_cb
), t
);
7419 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7420 eb2
= gtk_hbox_new(FALSE
, 0);
7421 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7422 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7424 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7430 create_buffers(struct tab
*t
)
7432 GtkCellRenderer
*renderer
;
7435 view
= gtk_tree_view_new();
7437 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7439 renderer
= gtk_cell_renderer_text_new();
7440 gtk_tree_view_insert_column_with_attributes
7441 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7443 renderer
= gtk_cell_renderer_text_new();
7444 gtk_tree_view_insert_column_with_attributes
7445 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
, NULL
);
7447 gtk_tree_view_set_model
7448 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7454 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7455 GtkTreeViewColumn
*col
, struct tab
*t
)
7460 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7462 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
, path
)) {
7464 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7465 set_current_tab(id
- 1);
7471 /* after tab reordering/creation/removal */
7479 TAILQ_FOREACH(t
, &tabs
, entry
) {
7480 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7481 if (t
->tab_id
> maxid
)
7484 gtk_widget_show(t
->tab_elems
.sep
);
7487 curid
= gtk_notebook_get_current_page(notebook
);
7488 TAILQ_FOREACH(t
, &tabs
, entry
) {
7489 if (t
->tab_id
== maxid
) {
7490 gtk_widget_hide(t
->tab_elems
.sep
);
7496 /* after active tab change */
7498 recolor_compact_tabs(void)
7504 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7505 TAILQ_FOREACH(t
, &tabs
, entry
)
7506 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7508 curid
= gtk_notebook_get_current_page(notebook
);
7509 TAILQ_FOREACH(t
, &tabs
, entry
)
7510 if (t
->tab_id
== curid
) {
7511 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
7512 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7518 set_current_tab(int page_num
)
7520 gtk_notebook_set_current_page(notebook
, page_num
);
7521 recolor_compact_tabs();
7525 undo_close_tab_save(struct tab
*t
)
7529 struct undo
*u1
, *u2
;
7531 WebKitWebHistoryItem
*item
;
7533 if ((uri
= get_uri(t
)) == NULL
)
7536 u1
= g_malloc0(sizeof(struct undo
));
7537 u1
->uri
= g_strdup(uri
);
7539 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7541 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7542 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7545 /* forward history */
7546 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7550 u1
->history
= g_list_prepend(u1
->history
,
7551 webkit_web_history_item_copy(item
));
7552 items
= g_list_next(items
);
7557 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7558 u1
->history
= g_list_prepend(u1
->history
,
7559 webkit_web_history_item_copy(item
));
7563 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7567 u1
->history
= g_list_prepend(u1
->history
,
7568 webkit_web_history_item_copy(item
));
7569 items
= g_list_next(items
);
7572 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7574 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7575 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7576 TAILQ_REMOVE(&undos
, u2
, entry
);
7578 g_list_free(u2
->history
);
7587 delete_tab(struct tab
*t
)
7591 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7596 TAILQ_REMOVE(&tabs
, t
, entry
);
7598 /* Halt all webkit activity. */
7599 abort_favicon_download(t
);
7600 webkit_web_view_stop_loading(t
->wv
);
7602 /* Save the tab, so we can undo the close. */
7603 undo_close_tab_save(t
);
7605 if (browser_mode
== XT_BM_KIOSK
) {
7606 gtk_widget_destroy(t
->uri_entry
);
7607 gtk_widget_destroy(t
->stop
);
7608 gtk_widget_destroy(t
->js_toggle
);
7611 gtk_widget_destroy(t
->tab_elems
.eventbox
);
7612 gtk_widget_destroy(t
->vbox
);
7614 g_free(t
->user_agent
);
7615 g_free(t
->stylesheet
);
7618 if (TAILQ_EMPTY(&tabs
)) {
7619 if (browser_mode
== XT_BM_KIOSK
)
7620 create_new_tab(home
, NULL
, 1, -1);
7622 create_new_tab(NULL
, NULL
, 1, -1);
7625 /* recreate session */
7626 if (session_autosave
) {
7632 recolor_compact_tabs();
7636 adjustfont_webkit(struct tab
*t
, int adjust
)
7641 show_oops(NULL
, "adjustfont_webkit invalid parameters");
7645 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7646 if (adjust
== XT_FONT_SET
) {
7647 t
->font_size
= default_font_size
;
7648 zoom
= default_zoom_level
;
7649 t
->font_size
+= adjust
;
7650 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7651 t
->font_size
, (char *)NULL
);
7652 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7653 &t
->font_size
, (char *)NULL
);
7655 t
->font_size
+= adjust
;
7656 zoom
+= adjust
/25.0;
7661 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7662 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7666 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
7668 struct tab
*t
= (struct tab
*) data
;
7670 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
7672 switch (event
->button
) {
7674 set_current_tab(t
->tab_id
);
7685 page_reordered_cb(GtkWidget
*nb
, GtkWidget
*eventbox
, guint pn
, gpointer data
)
7687 struct tab
*t
= (struct tab
*) data
;
7689 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
7692 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
7699 append_tab(struct tab
*t
)
7704 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7705 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7709 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
7714 WebKitWebHistoryItem
*item
;
7718 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7720 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7721 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7725 t
= g_malloc0(sizeof *t
);
7727 if (title
== NULL
) {
7728 title
= "(untitled)";
7732 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7734 /* label + button for tab */
7735 b
= gtk_hbox_new(FALSE
, 0);
7738 #if GTK_CHECK_VERSION(2, 20, 0)
7739 t
->spinner
= gtk_spinner_new();
7741 t
->label
= gtk_label_new(title
);
7742 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7743 gtk_widget_set_size_request(t
->label
, 100, 0);
7744 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
7745 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
7746 gtk_widget_set_size_request(b
, 130, 0);
7748 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7749 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7750 #if GTK_CHECK_VERSION(2, 20, 0)
7751 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7755 if (browser_mode
== XT_BM_KIOSK
)
7756 t
->toolbar
= create_kiosk_toolbar(t
);
7758 t
->toolbar
= create_toolbar(t
);
7760 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7763 t
->browser_win
= create_browser(t
);
7764 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7766 /* oops message for user feedback */
7767 t
->oops
= gtk_entry_new();
7768 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7769 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7770 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7771 gdk_color_parse(XT_COLOR_RED
, &color
);
7772 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7773 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7776 t
->cmd
= gtk_entry_new();
7777 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7778 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7779 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7780 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
7783 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
7785 t
->sbe
.statusbar
= gtk_entry_new();
7786 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
7787 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
7788 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
7790 t
->sbe
.position
= gtk_entry_new();
7791 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.position
), NULL
);
7792 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.position
), FALSE
);
7793 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.position
), FALSE
);
7795 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.position
), 1.0);
7796 gtk_widget_set_size_request(t
->sbe
.position
, 40, -1);
7798 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7799 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
7800 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
7801 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7802 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &color
);
7803 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &color
);
7805 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
7807 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.position
, FALSE
,
7810 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
7813 t
->buffers
= create_buffers(t
);
7814 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
7816 /* xtp meaning is normal by default */
7817 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7819 /* set empty favicon */
7820 xt_icon_from_name(t
, "text-html");
7822 /* and show it all */
7823 gtk_widget_show_all(b
);
7824 gtk_widget_show_all(t
->vbox
);
7826 /* compact tab bar */
7827 t
->tab_elems
.label
= gtk_label_new(title
);
7828 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
7829 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
7830 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
7832 t
->tab_elems
.eventbox
= gtk_event_box_new();
7833 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
7834 t
->tab_elems
.sep
= gtk_vseparator_new();
7836 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
7837 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
7838 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7839 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7840 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
7841 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
7843 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
7845 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
7847 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
7850 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
7852 gtk_widget_show_all(t
->tab_elems
.eventbox
);
7854 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7857 id
= position
>= 0 ? position
: gtk_notebook_get_current_page(notebook
) + 1;
7858 if (id
> gtk_notebook_get_n_pages(notebook
))
7861 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7862 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
7863 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, id
);
7868 #if GTK_CHECK_VERSION(2, 20, 0)
7869 /* turn spinner off if we are a new tab without uri */
7871 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7872 gtk_widget_hide(t
->spinner
);
7875 /* make notebook tabs reorderable */
7876 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7878 /* compact tabs clickable */
7879 g_signal_connect(GTK_OBJECT(t
->tab_elems
.eventbox
),
7880 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
7882 g_signal_connect(GTK_OBJECT(notebook
),
7883 "page_reordered", G_CALLBACK(page_reordered_cb
), t
);
7885 g_object_connect(G_OBJECT(t
->cmd
),
7886 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7887 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7888 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7889 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7892 /* reuse wv_button_cb to hide oops */
7893 g_object_connect(G_OBJECT(t
->oops
),
7894 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7897 g_signal_connect(t
->buffers
,
7898 "row-activated", G_CALLBACK(row_activated_cb
), t
);
7899 g_object_connect(G_OBJECT(t
->buffers
),
7900 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
7902 g_object_connect(G_OBJECT(t
->wv
),
7903 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7904 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7905 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7906 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7907 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7908 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7909 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7910 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7911 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7912 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7913 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7914 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7915 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7916 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7917 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
7919 g_signal_connect(t
->wv
,
7920 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7921 g_signal_connect(t
->wv
,
7922 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7924 /* hijack the unused keys as if we were the browser */
7925 g_object_connect(G_OBJECT(t
->toolbar
),
7926 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7929 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7930 G_CALLBACK(tab_close_cb
), t
);
7936 url_set_visibility();
7937 statusbar_set_visibility();
7940 set_current_tab(t
->tab_id
);
7941 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7946 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7950 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7955 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7956 /* restore the tab's history */
7957 if (u
&& u
->history
) {
7961 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7962 items
= g_list_next(items
);
7965 item
= g_list_nth_data(u
->history
, u
->back
);
7967 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7970 g_list_free(u
->history
);
7972 webkit_web_back_forward_list_clear(t
->bfl
);
7975 recolor_compact_tabs();
7980 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7986 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7988 if (gtk_notebook_get_current_page(notebook
) == -1)
7991 TAILQ_FOREACH(t
, &tabs
, entry
) {
7992 if (t
->tab_id
== pn
) {
7993 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7996 uri
= webkit_web_view_get_title(t
->wv
);
7999 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8005 /* can't use focus_webview here */
8006 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8013 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8020 menuitem_response(struct tab
*t
)
8022 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
8026 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
8028 GtkWidget
*menu
, *menu_items
;
8029 GdkEventButton
*bevent
;
8033 if (event
->type
== GDK_BUTTON_PRESS
) {
8034 bevent
= (GdkEventButton
*) event
;
8035 menu
= gtk_menu_new();
8037 TAILQ_FOREACH(ti
, &tabs
, entry
) {
8038 if ((uri
= get_uri(ti
)) == NULL
)
8039 /* XXX make sure there is something to print */
8040 /* XXX add gui pages in here to look purdy */
8042 menu_items
= gtk_menu_item_new_with_label(uri
);
8043 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
8044 gtk_widget_show(menu_items
);
8046 g_signal_connect_swapped((menu_items
),
8047 "activate", G_CALLBACK(menuitem_response
),
8051 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
8052 bevent
->button
, bevent
->time
);
8054 /* unref object so it'll free itself when popped down */
8055 #if !GTK_CHECK_VERSION(3, 0, 0)
8056 /* XXX does not need unref with gtk+3? */
8057 g_object_ref_sink(menu
);
8058 g_object_unref(menu
);
8061 return (TRUE
/* eat event */);
8064 return (FALSE
/* propagate */);
8068 icon_size_map(int icon_size
)
8070 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
8071 icon_size
> GTK_ICON_SIZE_DIALOG
)
8072 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
8078 create_button(char *name
, char *stockid
, int size
)
8080 GtkWidget
*button
, *image
;
8084 rcstring
= g_strdup_printf(
8085 "style \"%s-style\"\n"
8087 " GtkWidget::focus-padding = 0\n"
8088 " GtkWidget::focus-line-width = 0\n"
8092 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8093 gtk_rc_parse_string(rcstring
);
8095 button
= gtk_button_new();
8096 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8097 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8099 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8100 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8101 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8102 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8103 gtk_widget_set_name(button
, name
);
8104 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8110 button_set_stockid(GtkWidget
*button
, char *stockid
)
8114 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8115 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8116 gtk_button_set_image(GTK_BUTTON(button
), image
);
8120 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8122 GtkClipboard
*clipboard
;
8123 gchar
*p
= NULL
, *s
= NULL
;
8126 * This code is very aggressive!
8127 * It basically ensures that the primary and regular clipboard are
8128 * always set the same. This obviously messes with standard X protocol
8129 * but those clowns should have come up with something better.
8135 clipboard
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
8136 p
= gtk_clipboard_wait_for_text(primary
);
8138 DNPRINTF(XT_D_CLIP
, "primary cleaned\n");
8139 p
= gtk_clipboard_wait_for_text(clipboard
);
8141 gtk_clipboard_set_text(primary
, p
, -1);
8143 DNPRINTF(XT_D_CLIP
, "primary got selection\n");
8144 s
= gtk_clipboard_wait_for_text(clipboard
);
8147 * if s and p are the same the string was set by
8148 * clipb_clipboard_cb so do nothing in that case
8149 * to prevent endless loop
8154 gtk_clipboard_set_text(clipboard
, p
, -1);
8164 clipb_clipboard_cb(GtkClipboard
*clipboard
, GdkEvent
*event
, gpointer notused
)
8166 GtkClipboard
*primary
;
8167 gchar
*p
= NULL
, *s
= NULL
;
8172 DNPRINTF(XT_D_CLIP
, "clipboard got content\n");
8174 primary
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8175 p
= gtk_clipboard_wait_for_text(clipboard
);
8177 s
= gtk_clipboard_wait_for_text(primary
);
8180 * if s and p are the same the string was set by
8181 * clipb_primary_cb so do nothing in that case
8182 * to prevent endless loop and deselection of text
8187 gtk_clipboard_set_text(primary
, p
, -1);
8202 char file
[PATH_MAX
];
8205 vbox
= gtk_vbox_new(FALSE
, 0);
8206 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8207 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8208 #if !GTK_CHECK_VERSION(3, 0, 0)
8209 /* XXX seems to be needed with gtk+2 */
8210 gtk_notebook_set_tab_hborder(notebook
, 0);
8211 gtk_notebook_set_tab_vborder(notebook
, 0);
8213 gtk_notebook_set_scrollable(notebook
, TRUE
);
8214 gtk_notebook_set_show_border(notebook
, FALSE
);
8215 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8217 abtn
= gtk_button_new();
8218 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8219 gtk_widget_set_size_request(arrow
, -1, -1);
8220 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8221 gtk_widget_set_size_request(abtn
, -1, 20);
8223 #if GTK_CHECK_VERSION(2, 20, 0)
8224 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8226 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8228 /* compact tab bar */
8229 tab_bar
= gtk_hbox_new(TRUE
, 0);
8231 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8232 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8233 gtk_widget_set_size_request(vbox
, -1, -1);
8235 g_object_connect(G_OBJECT(notebook
),
8236 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8238 g_object_connect(G_OBJECT(notebook
),
8239 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
), NULL
,
8241 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8242 G_CALLBACK(arrow_cb
), NULL
);
8244 main_window
= create_window();
8245 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8246 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
8249 for (i
= 0; i
< LENGTH(icons
); i
++) {
8250 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8251 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8252 l
= g_list_append(l
, pb
);
8254 gtk_window_set_default_icon_list(l
);
8257 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8258 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8259 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
)),
8260 "owner-change", G_CALLBACK(clipb_clipboard_cb
), NULL
);
8262 gtk_widget_show_all(abtn
);
8263 gtk_widget_show_all(main_window
);
8264 notebook_tab_set_visibility();
8268 set_hook(void **hook
, char *name
)
8271 errx(1, "set_hook");
8273 if (*hook
== NULL
) {
8274 *hook
= dlsym(RTLD_NEXT
, name
);
8276 errx(1, "can't hook %s", name
);
8280 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8282 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8284 g_return_val_if_fail(cookie1
, FALSE
);
8285 g_return_val_if_fail(cookie2
, FALSE
);
8287 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8288 !strcmp (cookie1
->value
, cookie2
->value
) &&
8289 !strcmp (cookie1
->path
, cookie2
->path
) &&
8290 !strcmp (cookie1
->domain
, cookie2
->domain
));
8294 transfer_cookies(void)
8297 SoupCookie
*sc
, *pc
;
8299 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8301 for (;cf
; cf
= cf
->next
) {
8303 sc
= soup_cookie_copy(pc
);
8304 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8307 soup_cookies_free(cf
);
8311 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8316 print_cookie("soup_cookie_jar_delete_cookie", c
);
8318 if (cookies_enabled
== 0)
8321 if (jar
== NULL
|| c
== NULL
)
8324 /* find and remove from persistent jar */
8325 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8327 for (;cf
; cf
= cf
->next
) {
8329 if (soup_cookie_equal(ci
, c
)) {
8330 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8335 soup_cookies_free(cf
);
8337 /* delete from session jar */
8338 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8342 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8344 struct domain
*d
= NULL
;
8348 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8349 jar
, p_cookiejar
, s_cookiejar
);
8351 if (cookies_enabled
== 0)
8354 /* see if we are up and running */
8355 if (p_cookiejar
== NULL
) {
8356 _soup_cookie_jar_add_cookie(jar
, cookie
);
8359 /* disallow p_cookiejar adds, shouldn't happen */
8360 if (jar
== p_cookiejar
)
8364 if (jar
== NULL
|| cookie
== NULL
)
8367 if (enable_cookie_whitelist
&&
8368 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8370 DNPRINTF(XT_D_COOKIE
,
8371 "soup_cookie_jar_add_cookie: reject %s\n",
8373 if (save_rejected_cookies
) {
8374 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8375 show_oops(NULL
, "can't open reject cookie file");
8378 fseek(r_cookie_f
, 0, SEEK_END
);
8379 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8380 cookie
->http_only
? "#HttpOnly_" : "",
8382 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8384 cookie
->secure
? "TRUE" : "FALSE",
8386 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8393 if (!allow_volatile_cookies
)
8397 if (cookie
->expires
== NULL
&& session_timeout
) {
8398 soup_cookie_set_expires(cookie
,
8399 soup_date_new_from_now(session_timeout
));
8400 print_cookie("modified add cookie", cookie
);
8403 /* see if we are white listed for persistence */
8404 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8405 /* add to persistent jar */
8406 c
= soup_cookie_copy(cookie
);
8407 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8408 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8411 /* add to session jar */
8412 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8413 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8419 char file
[PATH_MAX
];
8421 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8422 "soup_cookie_jar_add_cookie");
8423 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8424 "soup_cookie_jar_delete_cookie");
8426 if (cookies_enabled
== 0)
8430 * the following code is intricate due to overriding several libsoup
8432 * do not alter order of these operations.
8435 /* rejected cookies */
8436 if (save_rejected_cookies
)
8437 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
, XT_REJECT_FILE
);
8439 /* persistent cookies */
8440 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8441 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8443 /* session cookies */
8444 s_cookiejar
= soup_cookie_jar_new();
8445 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8446 cookie_policy
, (void *)NULL
);
8449 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8453 setup_proxy(char *uri
)
8456 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8457 soup_uri_free(proxy_uri
);
8461 if (http_proxy
!= uri
) {
8468 http_proxy
= g_strdup(uri
);
8469 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8470 proxy_uri
= soup_uri_new(http_proxy
);
8471 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
8476 send_cmd_to_socket(char *cmd
)
8479 struct sockaddr_un sa
;
8481 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8482 warnx("%s: socket", __func__
);
8486 sa
.sun_family
= AF_UNIX
;
8487 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8488 work_dir
, XT_SOCKET_FILE
);
8491 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8492 warnx("%s: connect", __func__
);
8496 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
8497 warnx("%s: send", __func__
);
8508 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
8511 char str
[XT_MAX_URL_LENGTH
];
8512 socklen_t t
= sizeof(struct sockaddr_un
);
8513 struct sockaddr_un sa
;
8518 gint fd
= g_io_channel_unix_get_fd(source
);
8520 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
8525 if (getpeereid(s
, &uid
, &gid
) == -1) {
8529 if (uid
!= getuid() || gid
!= getgid()) {
8530 warnx("unauthorized user");
8536 warnx("not a valid user");
8540 n
= recv(s
, str
, sizeof(str
), 0);
8544 tt
= TAILQ_LAST(&tabs
, tab_list
);
8545 cmd_execute(tt
, str
);
8553 struct sockaddr_un sa
;
8555 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8556 warn("is_running: socket");
8560 sa
.sun_family
= AF_UNIX
;
8561 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8562 work_dir
, XT_SOCKET_FILE
);
8565 /* connect to see if there is a listener */
8566 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
8567 rv
= 0; /* not running */
8569 rv
= 1; /* already running */
8580 struct sockaddr_un sa
;
8582 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8583 warn("build_socket: socket");
8587 sa
.sun_family
= AF_UNIX
;
8588 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8589 work_dir
, XT_SOCKET_FILE
);
8592 /* connect to see if there is a listener */
8593 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8594 /* no listener so we will */
8595 unlink(sa
.sun_path
);
8597 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8598 warn("build_socket: bind");
8602 if (listen(s
, 1) == -1) {
8603 warn("build_socket: listen");
8616 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8617 GtkTreeIter
*iter
, struct tab
*t
)
8621 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8629 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8630 GtkTreeIter
*iter
, struct tab
*t
)
8634 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8635 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
8636 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
8643 completion_add_uri(const gchar
*uri
)
8647 /* add uri to list_store */
8648 gtk_list_store_append(completion_model
, &iter
);
8649 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8653 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8654 GtkTreeIter
*iter
, gpointer user_data
)
8657 gboolean match
= FALSE
;
8659 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8665 match
= match_uri(value
, key
);
8672 completion_add(struct tab
*t
)
8674 /* enable completion for tab */
8675 t
->completion
= gtk_entry_completion_new();
8676 gtk_entry_completion_set_text_column(t
->completion
, 0);
8677 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8678 gtk_entry_completion_set_model(t
->completion
,
8679 GTK_TREE_MODEL(completion_model
));
8680 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8682 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8683 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
8684 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8685 G_CALLBACK(completion_select_cb
), t
);
8686 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
8687 G_CALLBACK(completion_hover_cb
), t
);
8695 if (stat(dir
, &sb
)) {
8696 if (mkdir(dir
, S_IRWXU
) == -1)
8697 err(1, "mkdir %s", dir
);
8699 err(1, "stat %s", dir
);
8701 if (S_ISDIR(sb
.st_mode
) == 0)
8702 errx(1, "%s not a dir", dir
);
8703 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8704 warnx("fixing invalid permissions on %s", dir
);
8705 if (chmod(dir
, S_IRWXU
) == -1)
8706 err(1, "chmod %s", dir
);
8714 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8720 main(int argc
, char *argv
[])
8723 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8724 char conf
[PATH_MAX
] = { '\0' };
8725 char file
[PATH_MAX
];
8726 char *env_proxy
= NULL
;
8729 struct sigaction sact
;
8730 GIOChannel
*channel
;
8735 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8737 /* fiddle with ulimits */
8738 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8741 /* just use them all */
8742 rlp
.rlim_cur
= rlp
.rlim_max
;
8743 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8745 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8747 else if (rlp
.rlim_cur
<= 256)
8748 warnx("%s requires at least 256 file descriptors",
8752 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8761 errx(0 , "Version: %s", version
);
8764 strlcpy(conf
, optarg
, sizeof(conf
));
8767 strlcpy(named_session
, optarg
, sizeof(named_session
));
8788 RB_INIT(&downloads
);
8792 TAILQ_INIT(&aliases
);
8798 gnutls_global_init();
8800 /* generate session keys for xtp pages */
8801 generate_xtp_session_key(&dl_session_key
);
8802 generate_xtp_session_key(&hl_session_key
);
8803 generate_xtp_session_key(&cl_session_key
);
8804 generate_xtp_session_key(&fl_session_key
);
8807 gtk_init(&argc
, &argv
);
8808 if (!g_thread_supported())
8809 g_thread_init(NULL
);
8812 bzero(&sact
, sizeof(sact
));
8813 sigemptyset(&sact
.sa_mask
);
8814 sact
.sa_handler
= sigchild
;
8815 sact
.sa_flags
= SA_NOCLDSTOP
;
8816 sigaction(SIGCHLD
, &sact
, NULL
);
8818 /* set download dir */
8819 pwd
= getpwuid(getuid());
8821 errx(1, "invalid user %d", getuid());
8822 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8824 /* set default string settings */
8825 home
= g_strdup("https://www.cyphertite.com");
8826 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8827 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8828 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
8829 cmd_font_name
= g_strdup("monospace normal 9");
8830 statusbar_font_name
= g_strdup("monospace normal 9");
8833 /* read config file */
8834 if (strlen(conf
) == 0)
8835 snprintf(conf
, sizeof conf
, "%s/.%s",
8836 pwd
->pw_dir
, XT_CONF_FILE
);
8837 config_parse(conf
, 0);
8840 cmd_font
= pango_font_description_from_string(cmd_font_name
);
8841 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
8843 /* working directory */
8844 if (strlen(work_dir
) == 0)
8845 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8846 pwd
->pw_dir
, XT_DIR
);
8849 /* icon cache dir */
8850 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8854 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8858 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8859 work_dir
, XT_SESSIONS_DIR
);
8860 xxx_dir(sessions_dir
);
8862 /* runtime settings that can override config file */
8863 if (runtime_settings
[0] != '\0')
8864 config_parse(runtime_settings
, 1);
8867 if (!strcmp(download_dir
, pwd
->pw_dir
))
8868 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8869 xxx_dir(download_dir
);
8871 /* favorites file */
8872 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8873 if (stat(file
, &sb
)) {
8874 warnx("favorites file doesn't exist, creating it");
8875 if ((f
= fopen(file
, "w")) == NULL
)
8876 err(1, "favorites");
8881 session
= webkit_get_default_session();
8886 if (stat(ssl_ca_file
, &sb
)) {
8887 warnx("no CA file: %s", ssl_ca_file
);
8888 g_free(ssl_ca_file
);
8891 g_object_set(session
,
8892 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8893 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8898 env_proxy
= getenv("http_proxy");
8900 setup_proxy(env_proxy
);
8902 setup_proxy(http_proxy
);
8905 send_cmd_to_socket(argv
[0]);
8909 /* set some connection parameters */
8910 /* XXX webkit 1.4.X overwrites these values! */
8911 /* https://bugs.webkit.org/show_bug.cgi?id=64355 */
8912 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8913 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8916 /* see if there is already an xxxterm running */
8917 if (single_instance
&& is_running()) {
8919 warnx("already running");
8925 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8926 send_cmd_to_socket(cmd
);
8936 /* uri completion */
8937 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8940 buffers_store
= gtk_list_store_new
8941 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
8945 notebook_tab_set_visibility();
8947 if (save_global_history
)
8948 restore_global_history();
8950 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8951 restore_saved_tabs();
8953 a
.s
= named_session
;
8954 a
.i
= XT_SES_DONOTHING
;
8955 open_tabs(NULL
, &a
);
8959 create_new_tab(argv
[0], NULL
, focus
, -1);
8966 if (TAILQ_EMPTY(&tabs
))
8967 create_new_tab(home
, NULL
, 1, -1);
8970 if ((s
= build_socket()) != -1) {
8971 channel
= g_io_channel_unix_new(s
);
8972 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
8977 gnutls_global_deinit();