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
;
193 GtkWidget
*js_toggle
;
194 GtkEntryCompletion
*completion
;
198 WebKitWebHistoryItem
*item
;
199 WebKitWebBackForwardList
*bfl
;
202 WebKitNetworkRequest
*icon_request
;
203 WebKitDownload
*icon_download
;
204 gchar
*icon_dest_uri
;
206 /* adjustments for browser */
209 GtkAdjustment
*adjust_h
;
210 GtkAdjustment
*adjust_v
;
216 int xtp_meaning
; /* identifies dls/favorites */
221 #define XT_HINT_NONE (0)
222 #define XT_HINT_NUMERICAL (1)
223 #define XT_HINT_ALPHANUM (2)
227 /* custom stylesheet */
236 WebKitWebSettings
*settings
;
240 TAILQ_HEAD(tab_list
, tab
);
243 RB_ENTRY(history
) entry
;
247 RB_HEAD(history_list
, history
);
250 RB_ENTRY(download
) entry
;
252 WebKitDownload
*download
;
255 RB_HEAD(download_list
, download
);
258 RB_ENTRY(domain
) entry
;
260 int handy
; /* app use */
262 RB_HEAD(domain_list
, domain
);
265 TAILQ_ENTRY(undo
) entry
;
268 int back
; /* Keeps track of how many back
269 * history items there are. */
271 TAILQ_HEAD(undo_tailq
, undo
);
273 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
274 int next_download_id
= 1;
283 #define XT_NAME ("XXXTerm")
284 #define XT_DIR (".xxxterm")
285 #define XT_CACHE_DIR ("cache")
286 #define XT_CERT_DIR ("certs/")
287 #define XT_SESSIONS_DIR ("sessions/")
288 #define XT_CONF_FILE ("xxxterm.conf")
289 #define XT_FAVS_FILE ("favorites")
290 #define XT_SAVED_TABS_FILE ("main_session")
291 #define XT_RESTART_TABS_FILE ("restart_tabs")
292 #define XT_SOCKET_FILE ("socket")
293 #define XT_HISTORY_FILE ("history")
294 #define XT_REJECT_FILE ("rejected.txt")
295 #define XT_COOKIE_FILE ("cookies.txt")
296 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
297 #define XT_CB_HANDLED (TRUE)
298 #define XT_CB_PASSTHROUGH (FALSE)
299 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
300 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
301 #define XT_DLMAN_REFRESH "10"
302 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
303 "td{overflow: hidden;" \
304 " padding: 2px 2px 2px 2px;" \
305 " border: 1px solid black;" \
306 " vertical-align:top;" \
307 " word-wrap: break-word}\n" \
308 "tr:hover{background: #ffff99}\n" \
309 "th{background-color: #cccccc;" \
310 " border: 1px solid black}\n" \
311 "table{width: 100%%;" \
312 " border: 1px black solid;" \
313 " border-collapse:collapse}\n" \
315 "border: 1px solid black;" \
318 ".progress-inner{float: left;" \
320 " background: green}\n" \
321 ".dlstatus{font-size: small;" \
322 " text-align: center}\n" \
324 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
325 #define XT_MAX_UNDO_CLOSE_TAB (32)
326 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
327 #define XT_PRINT_EXTRA_MARGIN 10
330 #define XT_COLOR_RED "#cc0000"
331 #define XT_COLOR_YELLOW "#ffff66"
332 #define XT_COLOR_BLUE "lightblue"
333 #define XT_COLOR_GREEN "#99ff66"
334 #define XT_COLOR_WHITE "white"
335 #define XT_COLOR_BLACK "black"
337 #define XT_COLOR_CT_BACKGROUND "#000000"
338 #define XT_COLOR_CT_INACTIVE "#dddddd"
339 #define XT_COLOR_CT_ACTIVE "#bbbb00"
340 #define XT_COLOR_CT_SEPARATOR "#555555"
343 * xxxterm "protocol" (xtp)
344 * We use this for managing stuff like downloads and favorites. They
345 * make magical HTML pages in memory which have xxxt:// links in order
346 * to communicate with xxxterm's internals. These links take the format:
347 * xxxt://class/session_key/action/arg
349 * Don't begin xtp class/actions as 0. atoi returns that on error.
351 * Typically we have not put addition of items in this framework, as
352 * adding items is either done via an ex-command or via a keybinding instead.
355 #define XT_XTP_STR "xxxt://"
357 /* XTP classes (xxxt://<class>) */
358 #define XT_XTP_INVALID 0 /* invalid */
359 #define XT_XTP_DL 1 /* downloads */
360 #define XT_XTP_HL 2 /* history */
361 #define XT_XTP_CL 3 /* cookies */
362 #define XT_XTP_FL 4 /* favorites */
364 /* XTP download actions */
365 #define XT_XTP_DL_LIST 1
366 #define XT_XTP_DL_CANCEL 2
367 #define XT_XTP_DL_REMOVE 3
369 /* XTP history actions */
370 #define XT_XTP_HL_LIST 1
371 #define XT_XTP_HL_REMOVE 2
373 /* XTP cookie actions */
374 #define XT_XTP_CL_LIST 1
375 #define XT_XTP_CL_REMOVE 2
377 /* XTP cookie actions */
378 #define XT_XTP_FL_LIST 1
379 #define XT_XTP_FL_REMOVE 2
382 #define XT_MOVE_INVALID (0)
383 #define XT_MOVE_DOWN (1)
384 #define XT_MOVE_UP (2)
385 #define XT_MOVE_BOTTOM (3)
386 #define XT_MOVE_TOP (4)
387 #define XT_MOVE_PAGEDOWN (5)
388 #define XT_MOVE_PAGEUP (6)
389 #define XT_MOVE_HALFDOWN (7)
390 #define XT_MOVE_HALFUP (8)
391 #define XT_MOVE_LEFT (9)
392 #define XT_MOVE_FARLEFT (10)
393 #define XT_MOVE_RIGHT (11)
394 #define XT_MOVE_FARRIGHT (12)
396 #define XT_TAB_LAST (-4)
397 #define XT_TAB_FIRST (-3)
398 #define XT_TAB_PREV (-2)
399 #define XT_TAB_NEXT (-1)
400 #define XT_TAB_INVALID (0)
401 #define XT_TAB_NEW (1)
402 #define XT_TAB_DELETE (2)
403 #define XT_TAB_DELQUIT (3)
404 #define XT_TAB_OPEN (4)
405 #define XT_TAB_UNDO_CLOSE (5)
406 #define XT_TAB_SHOW (6)
407 #define XT_TAB_HIDE (7)
408 #define XT_TAB_NEXTSTYLE (8)
410 #define XT_NAV_INVALID (0)
411 #define XT_NAV_BACK (1)
412 #define XT_NAV_FORWARD (2)
413 #define XT_NAV_RELOAD (3)
414 #define XT_NAV_RELOAD_CACHE (4)
416 #define XT_FOCUS_INVALID (0)
417 #define XT_FOCUS_URI (1)
418 #define XT_FOCUS_SEARCH (2)
420 #define XT_SEARCH_INVALID (0)
421 #define XT_SEARCH_NEXT (1)
422 #define XT_SEARCH_PREV (2)
424 #define XT_PASTE_CURRENT_TAB (0)
425 #define XT_PASTE_NEW_TAB (1)
427 #define XT_FONT_SET (0)
429 #define XT_URL_SHOW (1)
430 #define XT_URL_HIDE (2)
432 #define XT_STATUSBAR_SHOW (1)
433 #define XT_STATUSBAR_HIDE (2)
435 #define XT_WL_TOGGLE (1<<0)
436 #define XT_WL_ENABLE (1<<1)
437 #define XT_WL_DISABLE (1<<2)
438 #define XT_WL_FQDN (1<<3) /* default */
439 #define XT_WL_TOPLEVEL (1<<4)
440 #define XT_WL_PERSISTENT (1<<5)
441 #define XT_WL_SESSION (1<<6)
442 #define XT_WL_RELOAD (1<<7)
444 #define XT_SHOW (1<<7)
445 #define XT_DELETE (1<<8)
446 #define XT_SAVE (1<<9)
447 #define XT_OPEN (1<<10)
449 #define XT_CMD_OPEN (0)
450 #define XT_CMD_OPEN_CURRENT (1)
451 #define XT_CMD_TABNEW (2)
452 #define XT_CMD_TABNEW_CURRENT (3)
454 #define XT_STATUS_NOTHING (0)
455 #define XT_STATUS_LINK (1)
456 #define XT_STATUS_URI (2)
457 #define XT_STATUS_LOADING (3)
459 #define XT_SES_DONOTHING (0)
460 #define XT_SES_CLOSETABS (1)
462 #define XT_BM_NORMAL (0)
463 #define XT_BM_WHITELIST (1)
464 #define XT_BM_KIOSK (2)
466 #define XT_PREFIX (1<<0)
467 #define XT_USERARG (1<<1)
468 #define XT_URLARG (1<<2)
469 #define XT_INTARG (1<<3)
471 #define XT_TABS_NORMAL 0
472 #define XT_TABS_COMPACT 1
480 TAILQ_ENTRY(mime_type
) entry
;
482 TAILQ_HEAD(mime_type_list
, mime_type
);
488 TAILQ_ENTRY(alias
) entry
;
490 TAILQ_HEAD(alias_list
, alias
);
492 /* settings that require restart */
493 int tabless
= 0; /* allow only 1 tab */
494 int enable_socket
= 0;
495 int single_instance
= 0; /* only allow one xxxterm to run */
496 int fancy_bar
= 1; /* fancy toolbar */
497 int browser_mode
= XT_BM_NORMAL
;
498 int enable_localstorage
= 0;
500 /* runtime settings */
501 int show_tabs
= 1; /* show tabs on notebook */
502 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
503 int show_url
= 1; /* show url toolbar on notebook */
504 int show_statusbar
= 0; /* vimperator style status bar */
505 int ctrl_click_focus
= 0; /* ctrl click gets focus */
506 int cookies_enabled
= 1; /* enable cookies */
507 int read_only_cookies
= 0; /* enable to not write cookies */
508 int enable_scripts
= 1;
509 int enable_plugins
= 0;
510 int default_font_size
= 12;
511 gfloat default_zoom_level
= 1.0;
512 int window_height
= 768;
513 int window_width
= 1024;
514 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
515 int refresh_interval
= 10; /* download refresh interval */
516 int enable_cookie_whitelist
= 0;
517 int enable_js_whitelist
= 0;
518 int session_timeout
= 3600; /* cookie session timeout */
519 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
520 char *ssl_ca_file
= NULL
;
521 char *resource_dir
= NULL
;
522 gboolean ssl_strict_certs
= FALSE
;
523 int append_next
= 1; /* append tab after current tab */
525 char *search_string
= NULL
;
526 char *http_proxy
= NULL
;
527 char download_dir
[PATH_MAX
];
528 char runtime_settings
[PATH_MAX
]; /* override of settings */
529 int allow_volatile_cookies
= 0;
530 int save_global_history
= 0; /* save global history to disk */
531 char *user_agent
= NULL
;
532 int save_rejected_cookies
= 0;
533 int session_autosave
= 0;
534 int guess_search
= 0;
535 int dns_prefetch
= FALSE
;
536 gint max_connections
= 25;
537 gint max_host_connections
= 5;
538 gint enable_spell_checking
= 0;
539 char *spell_check_languages
= NULL
;
541 char *cmd_font_name
= NULL
;
542 char *statusbar_font_name
= NULL
;
543 PangoFontDescription
*cmd_font
;
544 PangoFontDescription
*statusbar_font
;
548 int set_browser_mode(struct settings
*, char *);
549 int set_cookie_policy(struct settings
*, char *);
550 int set_download_dir(struct settings
*, char *);
551 int set_runtime_dir(struct settings
*, char *);
552 int set_tab_style(struct settings
*, char *);
553 int set_work_dir(struct settings
*, char *);
554 int add_alias(struct settings
*, char *);
555 int add_mime_type(struct settings
*, char *);
556 int add_cookie_wl(struct settings
*, char *);
557 int add_js_wl(struct settings
*, char *);
558 int add_kb(struct settings
*, char *);
559 void button_set_stockid(GtkWidget
*, char *);
560 GtkWidget
* create_button(char *, char *, int);
562 char *get_browser_mode(struct settings
*);
563 char *get_cookie_policy(struct settings
*);
564 char *get_download_dir(struct settings
*);
565 char *get_runtime_dir(struct settings
*);
566 char *get_tab_style(struct settings
*);
567 char *get_work_dir(struct settings
*);
569 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
570 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
571 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
572 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
573 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
575 void recalc_tabs(void);
576 void recolor_compact_tabs(void);
577 void set_current_tab(int page_num
);
580 int (*set
)(struct settings
*, char *);
581 char *(*get
)(struct settings
*);
582 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
585 struct special s_browser_mode
= {
591 struct special s_cookie
= {
597 struct special s_alias
= {
603 struct special s_mime
= {
609 struct special s_js
= {
615 struct special s_kb
= {
621 struct special s_cookie_wl
= {
627 struct special s_download_dir
= {
633 struct special s_work_dir
= {
639 struct special s_tab_style
= {
648 #define XT_S_INVALID (0)
651 #define XT_S_FLOAT (3)
653 #define XT_SF_RESTART (1<<0)
654 #define XT_SF_RUNTIME (1<<1)
660 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
661 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
662 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
663 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
664 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
665 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
666 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
667 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
668 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
669 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
670 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
671 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
672 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
673 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
674 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
675 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
676 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
677 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
678 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
679 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
680 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
681 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
682 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
683 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
684 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
685 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
686 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
687 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
688 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
689 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
690 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
691 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
692 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
693 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
694 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
695 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
696 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
697 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
698 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
699 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
700 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
701 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
702 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
705 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
706 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
708 /* runtime settings */
709 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
710 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
711 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
712 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
713 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
716 int about(struct tab
*, struct karg
*);
717 int blank(struct tab
*, struct karg
*);
718 int ca_cmd(struct tab
*, struct karg
*);
719 int cookie_show_wl(struct tab
*, struct karg
*);
720 int js_show_wl(struct tab
*, struct karg
*);
721 int help(struct tab
*, struct karg
*);
722 int set(struct tab
*, struct karg
*);
723 int stats(struct tab
*, struct karg
*);
724 int marco(struct tab
*, struct karg
*);
725 const char * marco_message(int *);
726 int xtp_page_cl(struct tab
*, struct karg
*);
727 int xtp_page_dl(struct tab
*, struct karg
*);
728 int xtp_page_fl(struct tab
*, struct karg
*);
729 int xtp_page_hl(struct tab
*, struct karg
*);
730 void xt_icon_from_file(struct tab
*, char *);
731 const gchar
*get_uri(struct tab
*);
732 const gchar
*get_title(struct tab
*);
734 #define XT_URI_ABOUT ("about:")
735 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
736 #define XT_URI_ABOUT_ABOUT ("about")
737 #define XT_URI_ABOUT_BLANK ("blank")
738 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
739 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
740 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
741 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
742 #define XT_URI_ABOUT_FAVORITES ("favorites")
743 #define XT_URI_ABOUT_HELP ("help")
744 #define XT_URI_ABOUT_HISTORY ("history")
745 #define XT_URI_ABOUT_JSWL ("jswl")
746 #define XT_URI_ABOUT_SET ("set")
747 #define XT_URI_ABOUT_STATS ("stats")
748 #define XT_URI_ABOUT_MARCO ("marco")
752 int (*func
)(struct tab
*, struct karg
*);
754 { XT_URI_ABOUT_ABOUT
, about
},
755 { XT_URI_ABOUT_BLANK
, blank
},
756 { XT_URI_ABOUT_CERTS
, ca_cmd
},
757 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
758 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
759 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
760 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
761 { XT_URI_ABOUT_HELP
, help
},
762 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
763 { XT_URI_ABOUT_JSWL
, js_show_wl
},
764 { XT_URI_ABOUT_SET
, set
},
765 { XT_URI_ABOUT_STATS
, stats
},
766 { XT_URI_ABOUT_MARCO
, marco
},
769 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
770 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
771 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
772 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
773 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
774 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
775 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
778 extern char *__progname
;
781 GtkWidget
*main_window
;
782 GtkNotebook
*notebook
;
784 GtkWidget
*arrow
, *abtn
;
785 struct tab_list tabs
;
786 struct history_list hl
;
787 struct download_list downloads
;
788 struct domain_list c_wl
;
789 struct domain_list js_wl
;
790 struct undo_tailq undos
;
791 struct keybinding_list kbl
;
793 int updating_dl_tabs
= 0;
794 int updating_hl_tabs
= 0;
795 int updating_cl_tabs
= 0;
796 int updating_fl_tabs
= 0;
798 uint64_t blocked_cookies
= 0;
799 char named_session
[PATH_MAX
];
800 int icon_size_map(int);
802 GtkListStore
*completion_model
;
803 void completion_add(struct tab
*);
804 void completion_add_uri(const gchar
*);
805 GtkListStore
*buffers_store
;
806 void xxx_dir(char *);
811 int saved_errno
, status
;
816 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
820 if (errno
!= ECHILD
) {
822 clog_warn("sigchild: waitpid:");
828 if (WIFEXITED(status
)) {
829 if (WEXITSTATUS(status
) != 0) {
831 clog_warnx("sigchild: child exit status: %d",
832 WEXITSTATUS(status));
837 clog_warnx("sigchild: child is terminated abnormally");
846 is_g_object_setting(GObject
*o
, char *str
)
848 guint n_props
= 0, i
;
849 GParamSpec
**proplist
;
851 if (! G_IS_OBJECT(o
))
854 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
857 for (i
=0; i
< n_props
; i
++) {
858 if (! strcmp(proplist
[i
]->name
, str
))
865 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
869 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
871 "<title>%s</title>\n"
880 addstyles
? XT_PAGE_STYLE
: "",
889 * Display a web page from a HTML string in memory, rather than from a URL
892 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
897 /* we set this to indicate we want to manually do navaction */
899 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
901 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
903 /* set t->xtp_meaning */
904 for (i
= 0; i
< LENGTH(about_list
); i
++)
905 if (!strcmp(title
, about_list
[i
].name
)) {
910 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
911 #if GTK_CHECK_VERSION(2, 20, 0)
912 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
913 gtk_widget_hide(t
->spinner
);
915 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
916 xt_icon_from_file(t
, file
);
921 get_current_tab(void)
925 TAILQ_FOREACH(t
, &tabs
, entry
) {
926 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
930 warnx("%s: no current tab", __func__
);
936 set_status(struct tab
*t
, gchar
*s
, int status
)
944 case XT_STATUS_LOADING
:
945 type
= g_strdup_printf("Loading: %s", s
);
949 type
= g_strdup_printf("Link: %s", s
);
951 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
955 type
= g_strdup_printf("%s", s
);
957 t
->status
= g_strdup(type
);
961 t
->status
= g_strdup(s
);
963 case XT_STATUS_NOTHING
:
968 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
974 hide_cmd(struct tab
*t
)
976 gtk_widget_hide(t
->cmd
);
980 show_cmd(struct tab
*t
)
982 gtk_widget_hide(t
->oops
);
983 gtk_widget_show(t
->cmd
);
987 hide_buffers(struct tab
*t
)
989 gtk_widget_hide(t
->buffers
);
990 gtk_list_store_clear(buffers_store
);
1000 sort_tabs_by_page_num(struct tab
***stabs
)
1005 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1007 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1009 TAILQ_FOREACH(t
, &tabs
, entry
)
1010 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1016 buffers_make_list(void)
1019 const gchar
*title
= NULL
;
1021 struct tab
**stabs
= NULL
;
1023 num_tabs
= sort_tabs_by_page_num(&stabs
);
1025 for (i
= 0; i
< num_tabs
; i
++)
1027 gtk_list_store_append(buffers_store
, &iter
);
1028 title
= get_title(stabs
[i
]);
1029 gtk_list_store_set(buffers_store
, &iter
,
1030 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1040 show_buffers(struct tab
*t
)
1042 buffers_make_list();
1043 gtk_widget_show(t
->buffers
);
1044 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1048 toggle_buffers(struct tab
*t
)
1050 if (gtk_widget_get_visible(t
->buffers
))
1057 buffers(struct tab
*t
, struct karg
*args
)
1065 hide_oops(struct tab
*t
)
1067 gtk_widget_hide(t
->oops
);
1071 show_oops(struct tab
*at
, const char *fmt
, ...)
1075 struct tab
*t
= NULL
;
1081 if ((t
= get_current_tab()) == NULL
)
1087 if (vasprintf(&msg
, fmt
, ap
) == -1)
1088 errx(1, "show_oops failed");
1091 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1092 gtk_widget_hide(t
->cmd
);
1093 gtk_widget_show(t
->oops
);
1097 get_as_string(struct settings
*s
)
1108 warnx("get_as_string skip %s\n", s
->name
);
1109 } else if (s
->type
== XT_S_INT
)
1110 r
= g_strdup_printf("%d", *s
->ival
);
1111 else if (s
->type
== XT_S_STR
)
1112 r
= g_strdup(*s
->sval
);
1113 else if (s
->type
== XT_S_FLOAT
)
1114 r
= g_strdup_printf("%f", *s
->fval
);
1116 r
= g_strdup_printf("INVALID TYPE");
1122 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1127 for (i
= 0; i
< LENGTH(rs
); i
++) {
1128 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1129 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1131 s
= get_as_string(&rs
[i
]);
1132 cb(&rs
[i
], s
, cb_args
);
1139 set_browser_mode(struct settings
*s
, char *val
)
1141 if (!strcmp(val
, "whitelist")) {
1142 browser_mode
= XT_BM_WHITELIST
;
1143 allow_volatile_cookies
= 0;
1144 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1145 cookies_enabled
= 1;
1146 enable_cookie_whitelist
= 1;
1147 read_only_cookies
= 0;
1148 save_rejected_cookies
= 0;
1149 session_timeout
= 3600;
1151 enable_js_whitelist
= 1;
1152 enable_localstorage
= 0;
1153 } else if (!strcmp(val
, "normal")) {
1154 browser_mode
= XT_BM_NORMAL
;
1155 allow_volatile_cookies
= 0;
1156 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1157 cookies_enabled
= 1;
1158 enable_cookie_whitelist
= 0;
1159 read_only_cookies
= 0;
1160 save_rejected_cookies
= 0;
1161 session_timeout
= 3600;
1163 enable_js_whitelist
= 0;
1164 enable_localstorage
= 1;
1165 } else if (!strcmp(val
, "kiosk")) {
1166 browser_mode
= XT_BM_KIOSK
;
1167 allow_volatile_cookies
= 0;
1168 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1169 cookies_enabled
= 1;
1170 enable_cookie_whitelist
= 0;
1171 read_only_cookies
= 0;
1172 save_rejected_cookies
= 0;
1173 session_timeout
= 3600;
1175 enable_js_whitelist
= 0;
1176 enable_localstorage
= 1;
1186 get_browser_mode(struct settings
*s
)
1190 if (browser_mode
== XT_BM_WHITELIST
)
1191 r
= g_strdup("whitelist");
1192 else if (browser_mode
== XT_BM_NORMAL
)
1193 r
= g_strdup("normal");
1194 else if (browser_mode
== XT_BM_KIOSK
)
1195 r
= g_strdup("kiosk");
1203 set_cookie_policy(struct settings
*s
, char *val
)
1205 if (!strcmp(val
, "no3rdparty"))
1206 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1207 else if (!strcmp(val
, "accept"))
1208 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1209 else if (!strcmp(val
, "reject"))
1210 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1218 get_cookie_policy(struct settings
*s
)
1222 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1223 r
= g_strdup("no3rdparty");
1224 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1225 r
= g_strdup("accept");
1226 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1227 r
= g_strdup("reject");
1235 get_download_dir(struct settings
*s
)
1237 if (download_dir
[0] == '\0')
1239 return (g_strdup(download_dir
));
1243 set_download_dir(struct settings
*s
, char *val
)
1246 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1247 pwd
->pw_dir
, &val
[1]);
1249 strlcpy(download_dir
, val
, sizeof download_dir
);
1256 * We use these to prevent people putting xxxt:// URLs on
1257 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1259 #define XT_XTP_SES_KEY_SZ 8
1260 #define XT_XTP_SES_KEY_HEX_FMT \
1261 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1262 char *dl_session_key
; /* downloads */
1263 char *hl_session_key
; /* history list */
1264 char *cl_session_key
; /* cookie list */
1265 char *fl_session_key
; /* favorites list */
1267 char work_dir
[PATH_MAX
];
1268 char certs_dir
[PATH_MAX
];
1269 char cache_dir
[PATH_MAX
];
1270 char sessions_dir
[PATH_MAX
];
1271 char cookie_file
[PATH_MAX
];
1272 SoupURI
*proxy_uri
= NULL
;
1273 SoupSession
*session
;
1274 SoupCookieJar
*s_cookiejar
;
1275 SoupCookieJar
*p_cookiejar
;
1276 char rc_fname
[PATH_MAX
];
1278 struct mime_type_list mtl
;
1279 struct alias_list aliases
;
1282 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1283 void delete_tab(struct tab
*);
1284 void adjustfont_webkit(struct tab
*, int);
1285 int run_script(struct tab
*, char *);
1286 int download_rb_cmp(struct download
*, struct download
*);
1287 gboolean
cmd_execute(struct tab
*t
, char *str
);
1290 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1292 return (strcmp(h1
->uri
, h2
->uri
));
1294 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1297 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1299 return (strcmp(d1
->d
, d2
->d
));
1301 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1304 get_work_dir(struct settings
*s
)
1306 if (work_dir
[0] == '\0')
1308 return (g_strdup(work_dir
));
1312 set_work_dir(struct settings
*s
, char *val
)
1315 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1316 pwd
->pw_dir
, &val
[1]);
1318 strlcpy(work_dir
, val
, sizeof work_dir
);
1324 get_tab_style(struct settings
*s
)
1326 if (tab_style
== XT_TABS_NORMAL
)
1327 return (g_strdup("normal"));
1329 return (g_strdup("compact"));
1333 set_tab_style(struct settings
*s
, char *val
)
1335 if (!strcmp(val
, "normal"))
1336 tab_style
= XT_TABS_NORMAL
;
1337 else if (!strcmp(val
, "compact"))
1338 tab_style
= XT_TABS_COMPACT
;
1346 * generate a session key to secure xtp commands.
1347 * pass in a ptr to the key in question and it will
1348 * be modified in place.
1351 generate_xtp_session_key(char **key
)
1353 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1359 /* make a new one */
1360 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1361 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1362 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1363 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1365 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1369 * validate a xtp session key.
1373 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1375 if (strcmp(trusted
, untrusted
) != 0) {
1376 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1385 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1387 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1389 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1391 struct valid_url_types
{
1402 valid_url_type(char *url
)
1406 for (i
= 0; i
< LENGTH(vut
); i
++)
1407 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1414 print_cookie(char *msg
, SoupCookie
*c
)
1420 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1421 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1422 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1423 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1424 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1425 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1426 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1427 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1428 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1429 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1433 walk_alias(struct settings
*s
,
1434 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1439 if (s
== NULL
|| cb
== NULL
) {
1440 show_oops(NULL
, "walk_alias invalid parameters");
1444 TAILQ_FOREACH(a
, &aliases
, entry
) {
1445 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1446 cb(s
, str
, cb_args
);
1452 match_alias(char *url_in
)
1456 char *url_out
= NULL
, *search
, *enc_arg
;
1458 search
= g_strdup(url_in
);
1460 if (strsep(&arg
, " \t") == NULL
) {
1461 show_oops(NULL
, "match_alias: NULL URL");
1465 TAILQ_FOREACH(a
, &aliases
, entry
) {
1466 if (!strcmp(search
, a
->a_name
))
1471 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1474 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1475 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1478 url_out
= g_strdup_printf(a
->a_uri
, "");
1486 guess_url_type(char *url_in
)
1489 char *url_out
= NULL
, *enc_search
= NULL
;
1491 url_out
= match_alias(url_in
);
1492 if (url_out
!= NULL
)
1497 * If there is no dot nor slash in the string and it isn't a
1498 * path to a local file and doesn't resolves to an IP, assume
1499 * that the user wants to search for the string.
1502 if (strchr(url_in
, '.') == NULL
&&
1503 strchr(url_in
, '/') == NULL
&&
1504 stat(url_in
, &sb
) != 0 &&
1505 gethostbyname(url_in
) == NULL
) {
1507 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1508 url_out
= g_strdup_printf(search_string
, enc_search
);
1514 /* XXX not sure about this heuristic */
1515 if (stat(url_in
, &sb
) == 0)
1516 url_out
= g_strdup_printf("file://%s", url_in
);
1518 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1520 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1526 load_uri(struct tab
*t
, gchar
*uri
)
1529 gchar
*newuri
= NULL
;
1535 /* Strip leading spaces. */
1536 while (*uri
&& isspace(*uri
))
1539 if (strlen(uri
) == 0) {
1544 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1546 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1547 for (i
= 0; i
< LENGTH(about_list
); i
++)
1548 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1549 bzero(&args
, sizeof args
);
1550 about_list
[i
].func(t
, &args
);
1551 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1555 show_oops(t
, "invalid about page");
1559 if (valid_url_type(uri
)) {
1560 newuri
= guess_url_type(uri
);
1564 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1565 webkit_web_view_load_uri(t
->wv
, uri
);
1572 get_uri(struct tab
*t
)
1574 const gchar
*uri
= NULL
;
1576 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
)
1577 uri
= webkit_web_view_get_uri(t
->wv
);
1579 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, about_list
[t
->xtp_meaning
].name
);
1585 get_title(struct tab
*t
)
1587 const gchar
*set
= NULL
, *title
= NULL
;
1589 title
= webkit_web_view_get_title(t
->wv
);
1590 set
= title
? title
: get_uri(t
);
1591 if (!set
|| t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
) {
1598 add_alias(struct settings
*s
, char *line
)
1601 struct alias
*a
= NULL
;
1603 if (s
== NULL
|| line
== NULL
) {
1604 show_oops(NULL
, "add_alias invalid parameters");
1609 a
= g_malloc(sizeof(*a
));
1611 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1612 show_oops(NULL
, "add_alias: incomplete alias definition");
1615 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1616 show_oops(NULL
, "add_alias: invalid alias definition");
1620 a
->a_name
= g_strdup(alias
);
1621 a
->a_uri
= g_strdup(l
);
1623 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1625 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1635 add_mime_type(struct settings
*s
, char *line
)
1639 struct mime_type
*m
= NULL
;
1640 int downloadfirst
= 0;
1642 /* XXX this could be smarter */
1644 if (line
== NULL
|| strlen(line
) == 0) {
1645 show_oops(NULL
, "add_mime_type invalid parameters");
1654 m
= g_malloc(sizeof(*m
));
1656 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1657 show_oops(NULL
, "add_mime_type: invalid mime_type");
1660 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1661 mime_type
[strlen(mime_type
) - 1] = '\0';
1666 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1667 show_oops(NULL
, "add_mime_type: invalid mime_type");
1671 m
->mt_type
= g_strdup(mime_type
);
1672 m
->mt_action
= g_strdup(l
);
1673 m
->mt_download
= downloadfirst
;
1675 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1676 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1678 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1688 find_mime_type(char *mime_type
)
1690 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1692 TAILQ_FOREACH(m
, &mtl
, entry
) {
1693 if (m
->mt_default
&&
1694 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1697 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1710 walk_mime_type(struct settings
*s
,
1711 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1713 struct mime_type
*m
;
1716 if (s
== NULL
|| cb
== NULL
) {
1717 show_oops(NULL
, "walk_mime_type invalid parameters");
1721 TAILQ_FOREACH(m
, &mtl
, entry
) {
1722 str
= g_strdup_printf("%s%s --> %s",
1724 m
->mt_default
? "*" : "",
1726 cb(s
, str
, cb_args
);
1732 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1737 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1740 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1742 /* treat *.moo.com the same as .moo.com */
1743 if (str
[0] == '*' && str
[1] == '.')
1745 else if (str
[0] == '.')
1750 d
= g_malloc(sizeof *d
);
1752 d
->d
= g_strdup_printf(".%s", str
);
1754 d
->d
= g_strdup(str
);
1757 if (RB_INSERT(domain_list
, wl
, d
))
1760 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1771 add_cookie_wl(struct settings
*s
, char *entry
)
1773 wl_add(entry
, &c_wl
, 1);
1778 walk_cookie_wl(struct settings
*s
,
1779 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1783 if (s
== NULL
|| cb
== NULL
) {
1784 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1788 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1789 cb(s
, d
->d
, cb_args
);
1793 walk_js_wl(struct settings
*s
,
1794 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1798 if (s
== NULL
|| cb
== NULL
) {
1799 show_oops(NULL
, "walk_js_wl invalid parameters");
1803 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1804 cb(s
, d
->d
, cb_args
);
1808 add_js_wl(struct settings
*s
, char *entry
)
1810 wl_add(entry
, &js_wl
, 1 /* persistent */);
1815 wl_find(const gchar
*search
, struct domain_list
*wl
)
1818 struct domain
*d
= NULL
, dfind
;
1821 if (search
== NULL
|| wl
== NULL
)
1823 if (strlen(search
) < 2)
1826 if (search
[0] != '.')
1827 s
= g_strdup_printf(".%s", search
);
1829 s
= g_strdup(search
);
1831 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1834 d
= RB_FIND(domain_list
, wl
, &dfind
);
1848 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1854 if (s
== NULL
|| wl
== NULL
)
1857 if (!strncmp(s
, "http://", strlen("http://")))
1858 s
= &s
[strlen("http://")];
1859 else if (!strncmp(s
, "https://", strlen("https://")))
1860 s
= &s
[strlen("https://")];
1865 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1866 /* chop string at first slash */
1867 if (s
[i
] == '/' || s
[i
] == '\0') {
1870 r
= wl_find(ss
, wl
);
1879 get_toplevel_domain(char *domain
)
1886 if (strlen(domain
) < 2)
1889 s
= &domain
[strlen(domain
) - 1];
1890 while (s
!= domain
) {
1906 settings_add(char *var
, char *val
)
1913 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1914 if (strcmp(var
, rs
[i
].name
))
1918 if (rs
[i
].s
->set(&rs
[i
], val
))
1919 errx(1, "invalid value for %s: %s", var
, val
);
1923 switch (rs
[i
].type
) {
1932 errx(1, "invalid sval for %s",
1946 errx(1, "invalid type for %s", var
);
1955 config_parse(char *filename
, int runtime
)
1958 char *line
, *cp
, *var
, *val
;
1959 size_t len
, lineno
= 0;
1961 char file
[PATH_MAX
];
1964 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1966 if (filename
== NULL
)
1969 if (runtime
&& runtime_settings
[0] != '\0') {
1970 snprintf(file
, sizeof file
, "%s/%s",
1971 work_dir
, runtime_settings
);
1972 if (stat(file
, &sb
)) {
1973 warnx("runtime file doesn't exist, creating it");
1974 if ((f
= fopen(file
, "w")) == NULL
)
1976 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1980 strlcpy(file
, filename
, sizeof file
);
1982 if ((config
= fopen(file
, "r")) == NULL
) {
1983 warn("config_parse: cannot open %s", filename
);
1988 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1989 if (feof(config
) || ferror(config
))
1993 cp
+= (long)strspn(cp
, WS
);
1994 if (cp
[0] == '\0') {
2000 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2001 errx(1, "invalid config file entry: %s", line
);
2003 cp
+= (long)strspn(cp
, WS
);
2005 if ((val
= strsep(&cp
, "\0")) == NULL
)
2008 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2009 handled
= settings_add(var
, val
);
2011 errx(1, "invalid conf file entry: %s=%s", var
, val
);
2020 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2026 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2030 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2033 JSStringGetUTF8CString(jsref
, s
, l
);
2034 JSStringRelease(jsref
);
2040 disable_hints(struct tab
*t
)
2042 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2043 bzero(t
->hint_num
, sizeof t
->hint_num
);
2044 run_script(t
, "vimprobable_clear()");
2046 t
->hint_mode
= XT_HINT_NONE
;
2050 enable_hints(struct tab
*t
)
2052 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2053 run_script(t
, "vimprobable_show_hints()");
2055 t
->hint_mode
= XT_HINT_NONE
;
2058 #define XT_JS_OPEN ("open;")
2059 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2060 #define XT_JS_FIRE ("fire;")
2061 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2062 #define XT_JS_FOUND ("found;")
2063 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2066 run_script(struct tab
*t
, char *s
)
2068 JSGlobalContextRef ctx
;
2069 WebKitWebFrame
*frame
;
2071 JSValueRef val
, exception
;
2074 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2075 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2077 frame
= webkit_web_view_get_main_frame(t
->wv
);
2078 ctx
= webkit_web_frame_get_global_context(frame
);
2080 str
= JSStringCreateWithUTF8CString(s
);
2081 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2082 NULL
, 0, &exception
);
2083 JSStringRelease(str
);
2085 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2087 es
= js_ref_to_string(ctx
, exception
);
2088 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2092 es
= js_ref_to_string(ctx
, val
);
2093 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2095 /* handle return value right here */
2096 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2098 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
2101 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2102 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2103 &es
[XT_JS_FIRE_LEN
]);
2108 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2109 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2120 hint(struct tab
*t
, struct karg
*args
)
2123 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2125 if (t
->hints_on
== 0)
2134 apply_style(struct tab
*t
)
2136 g_object_set(G_OBJECT(t
->settings
),
2137 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2141 userstyle(struct tab
*t
, struct karg
*args
)
2143 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2147 g_object_set(G_OBJECT(t
->settings
),
2148 "user-stylesheet-uri", NULL
, (char *)NULL
);
2157 * Doesn't work fully, due to the following bug:
2158 * https://bugs.webkit.org/show_bug.cgi?id=51747
2161 restore_global_history(void)
2163 char file
[PATH_MAX
];
2168 const char delim
[3] = {'\\', '\\', '\0'};
2170 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2172 if ((f
= fopen(file
, "r")) == NULL
) {
2173 warnx("%s: fopen", __func__
);
2178 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2179 if (feof(f
) || ferror(f
))
2182 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2183 if (feof(f
) || ferror(f
)) {
2185 warnx("%s: broken history file\n", __func__
);
2189 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2190 webkit_web_history_item_new_with_data(uri
, title
);
2191 h
= g_malloc(sizeof(struct history
));
2192 h
->uri
= g_strdup(uri
);
2193 h
->title
= g_strdup(title
);
2194 RB_INSERT(history_list
, &hl
, h
);
2195 completion_add_uri(h
->uri
);
2197 warnx("%s: failed to restore history\n", __func__
);
2213 save_global_history_to_disk(struct tab
*t
)
2215 char file
[PATH_MAX
];
2219 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2221 if ((f
= fopen(file
, "w")) == NULL
) {
2222 show_oops(t
, "%s: global history file: %s",
2223 __func__
, strerror(errno
));
2227 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2228 if (h
->uri
&& h
->title
)
2229 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2238 quit(struct tab
*t
, struct karg
*args
)
2240 if (save_global_history
)
2241 save_global_history_to_disk(t
);
2249 open_tabs(struct tab
*t
, struct karg
*a
)
2251 char file
[PATH_MAX
];
2255 struct tab
*ti
, *tt
;
2260 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2261 if ((f
= fopen(file
, "r")) == NULL
)
2264 ti
= TAILQ_LAST(&tabs
, tab_list
);
2267 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2268 if (feof(f
) || ferror(f
))
2271 /* retrieve session name */
2272 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2273 strlcpy(named_session
,
2274 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2275 sizeof named_session
);
2279 if (uri
&& strlen(uri
))
2280 create_new_tab(uri
, NULL
, 1, -1);
2286 /* close open tabs */
2287 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2289 tt
= TAILQ_FIRST(&tabs
);
2309 restore_saved_tabs(void)
2311 char file
[PATH_MAX
];
2312 int unlink_file
= 0;
2317 snprintf(file
, sizeof file
, "%s/%s",
2318 sessions_dir
, XT_RESTART_TABS_FILE
);
2319 if (stat(file
, &sb
) == -1)
2320 a
.s
= XT_SAVED_TABS_FILE
;
2323 a
.s
= XT_RESTART_TABS_FILE
;
2326 a
.i
= XT_SES_DONOTHING
;
2327 rv
= open_tabs(NULL
, &a
);
2336 save_tabs(struct tab
*t
, struct karg
*a
)
2338 char file
[PATH_MAX
];
2340 int num_tabs
= 0, i
;
2341 struct tab
**stabs
= NULL
;
2346 snprintf(file
, sizeof file
, "%s/%s",
2347 sessions_dir
, named_session
);
2349 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2351 if ((f
= fopen(file
, "w")) == NULL
) {
2352 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2356 /* save session name */
2357 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2359 /* Save tabs, in the order they are arranged in the notebook. */
2360 num_tabs
= sort_tabs_by_page_num(&stabs
);
2362 for (i
= 0; i
< num_tabs
; i
++)
2363 if (stabs
[i
] && get_uri(stabs
[i
]) != NULL
)
2364 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2368 /* try and make sure this gets to disk NOW. XXX Backup first? */
2369 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2370 show_oops(t
, "May not have managed to save session: %s",
2380 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2392 yank_uri(struct tab
*t
, struct karg
*args
)
2395 GtkClipboard
*clipboard
;
2397 if ((uri
= get_uri(t
)) == NULL
)
2400 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2401 gtk_clipboard_set_text(clipboard
, uri
, -1);
2407 paste_uri(struct tab
*t
, struct karg
*args
)
2409 GtkClipboard
*clipboard
;
2410 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2412 gchar
*p
= NULL
, *uri
;
2414 /* try primary clipboard first */
2415 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2416 p
= gtk_clipboard_wait_for_text(clipboard
);
2418 /* if it failed get whatever text is in cut_buffer0 */
2420 if (gdk_property_get(gdk_get_default_root_window(),
2422 gdk_atom_intern("STRING", FALSE
),
2424 65536 /* picked out of my butt */,
2430 /* yes sir, we need to NUL the string */
2436 while (*uri
&& isspace(*uri
))
2438 if (strlen(uri
) == 0) {
2439 show_oops(t
, "empty paste buffer");
2442 if (guess_search
== 0 && valid_url_type(uri
)) {
2443 /* we can be clever and paste this in search box */
2444 show_oops(t
, "not a valid URL");
2448 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2450 else if (args
->i
== XT_PASTE_NEW_TAB
)
2451 create_new_tab(uri
, NULL
, 1, -1);
2462 find_domain(const gchar
*s
, int add_dot
)
2465 char *r
= NULL
, *ss
= NULL
;
2470 if (!strncmp(s
, "http://", strlen("http://")))
2471 s
= &s
[strlen("http://")];
2472 else if (!strncmp(s
, "https://", strlen("https://")))
2473 s
= &s
[strlen("https://")];
2479 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2480 /* chop string at first slash */
2481 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2484 r
= g_strdup_printf(".%s", ss
);
2495 toggle_cwl(struct tab
*t
, struct karg
*args
)
2499 char *dom
= NULL
, *dom_toggle
= NULL
;
2506 dom
= find_domain(uri
, 1);
2507 d
= wl_find(dom
, &c_wl
);
2514 if (args
->i
& XT_WL_TOGGLE
)
2516 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2518 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2521 if (args
->i
& XT_WL_TOPLEVEL
)
2522 dom_toggle
= get_toplevel_domain(dom
);
2527 /* enable cookies for domain */
2528 wl_add(dom_toggle
, &c_wl
, 0);
2530 /* disable cookies for domain */
2531 RB_REMOVE(domain_list
, &c_wl
, d
);
2533 if (args
->i
& XT_WL_RELOAD
)
2534 webkit_web_view_reload(t
->wv
);
2541 toggle_js(struct tab
*t
, struct karg
*args
)
2546 char *dom
= NULL
, *dom_toggle
= NULL
;
2551 g_object_get(G_OBJECT(t
->settings
),
2552 "enable-scripts", &es
, (char *)NULL
);
2553 if (args
->i
& XT_WL_TOGGLE
)
2555 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2557 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2563 dom
= find_domain(uri
, 1);
2565 if (uri
== NULL
|| dom
== NULL
) {
2566 show_oops(t
, "Can't toggle domain in JavaScript white list");
2570 if (args
->i
& XT_WL_TOPLEVEL
)
2571 dom_toggle
= get_toplevel_domain(dom
);
2576 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2577 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2579 d
= wl_find(dom_toggle
, &js_wl
);
2581 RB_REMOVE(domain_list
, &js_wl
, d
);
2582 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2584 g_object_set(G_OBJECT(t
->settings
),
2585 "enable-scripts", es
, (char *)NULL
);
2586 g_object_set(G_OBJECT(t
->settings
),
2587 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2588 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2590 if (args
->i
& XT_WL_RELOAD
)
2591 webkit_web_view_reload(t
->wv
);
2599 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2603 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2606 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2611 toggle_src(struct tab
*t
, struct karg
*args
)
2618 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2619 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2620 webkit_web_view_reload(t
->wv
);
2626 focus_webview(struct tab
*t
)
2631 /* only grab focus if we are visible */
2632 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2633 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2637 focus(struct tab
*t
, struct karg
*args
)
2639 if (t
== NULL
|| args
== NULL
)
2645 if (args
->i
== XT_FOCUS_URI
)
2646 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2647 else if (args
->i
== XT_FOCUS_SEARCH
)
2648 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2654 stats(struct tab
*t
, struct karg
*args
)
2656 char *page
, *body
, *s
, line
[64 * 1024];
2657 uint64_t line_count
= 0;
2661 show_oops(NULL
, "stats invalid parameters");
2664 if (save_rejected_cookies
) {
2665 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2667 s
= fgets(line
, sizeof line
, r_cookie_f
);
2668 if (s
== NULL
|| feof(r_cookie_f
) ||
2674 snprintf(line
, sizeof line
,
2675 "<br/>Cookies blocked(*) total: %llu", line_count
);
2677 show_oops(t
, "Can't open blocked cookies file: %s",
2681 body
= g_strdup_printf(
2682 "Cookies blocked(*) this session: %llu"
2684 "<p><small><b>*</b> results vary based on settings</small></p>",
2688 page
= get_html_page("Statistics", body
, "", 0);
2691 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2698 marco(struct tab
*t
, struct karg
*args
)
2700 char *page
, line
[64 * 1024];
2704 show_oops(NULL
, "marco invalid parameters");
2707 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2709 page
= get_html_page("Marco Sez...", line
, "", 0);
2711 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2718 blank(struct tab
*t
, struct karg
*args
)
2721 show_oops(NULL
, "blank invalid parameters");
2723 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2728 about(struct tab
*t
, struct karg
*args
)
2733 show_oops(NULL
, "about invalid parameters");
2735 body
= g_strdup_printf("<b>Version: %s</b><p>"
2738 "<li>Marco Peereboom <marco@peereboom.us></li>"
2739 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2740 "<li>Edd Barrett <vext01@gmail.com> </li>"
2741 "<li>Todd T. Fries <todd@fries.net> </li>"
2742 "<li>Raphael Graf <r@undefined.ch> </li>"
2744 "Copyrights and licenses can be found on the XXXterm "
2745 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2749 page
= get_html_page("About", body
, "", 0);
2752 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2759 help(struct tab
*t
, struct karg
*args
)
2761 char *page
, *head
, *body
;
2764 show_oops(NULL
, "help invalid parameters");
2766 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2767 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2769 body
= "XXXterm man page <a href=\"http://opensource.conformal.com/"
2770 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2771 "cgi-bin/man-cgi?xxxterm</a>";
2773 page
= get_html_page("XXXterm", body
, head
, FALSE
);
2775 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2782 * update all favorite tabs apart from one. Pass NULL if
2783 * you want to update all.
2786 update_favorite_tabs(struct tab
*apart_from
)
2789 if (!updating_fl_tabs
) {
2790 updating_fl_tabs
= 1; /* stop infinite recursion */
2791 TAILQ_FOREACH(t
, &tabs
, entry
)
2792 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2793 && (t
!= apart_from
))
2794 xtp_page_fl(t
, NULL
);
2795 updating_fl_tabs
= 0;
2799 /* show a list of favorites (bookmarks) */
2801 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2803 char file
[PATH_MAX
];
2805 char *uri
= NULL
, *title
= NULL
;
2806 size_t len
, lineno
= 0;
2808 char *body
, *tmp
, *page
= NULL
;
2809 const char delim
[3] = {'\\', '\\', '\0'};
2811 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2814 warn("%s: bad param", __func__
);
2816 /* new session key */
2817 if (!updating_fl_tabs
)
2818 generate_xtp_session_key(&fl_session_key
);
2820 /* open favorites */
2821 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2822 if ((f
= fopen(file
, "r")) == NULL
) {
2823 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2828 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2829 "<th style='width: 40px'>#</th><th>Link</th>"
2830 "<th style='width: 40px'>Rm</th></tr>\n");
2833 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2834 if (feof(f
) || ferror(f
))
2842 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2843 if (feof(f
) || ferror(f
)) {
2844 show_oops(t
, "favorites file corrupt");
2850 body
= g_strdup_printf("%s<tr>"
2852 "<td><a href='%s'>%s</a></td>"
2853 "<td style='text-align: center'>"
2854 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2856 body
, i
, uri
, title
,
2857 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2869 /* if none, say so */
2872 body
= g_strdup_printf("%s<tr>"
2873 "<td colspan='3' style='text-align: center'>"
2874 "No favorites - To add one use the 'favadd' command."
2875 "</td></tr>", body
);
2880 body
= g_strdup_printf("%s</table>", body
);
2890 page
= get_html_page("Favorites", body
, "", 1);
2891 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
2895 update_favorite_tabs(t
);
2904 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2905 size_t cert_count
, char *title
)
2907 gnutls_datum_t cinfo
;
2911 body
= g_strdup("");
2913 for (i
= 0; i
< cert_count
; i
++) {
2914 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2919 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2920 body
, i
, cinfo
.data
);
2921 gnutls_free(cinfo
.data
);
2925 tmp
= get_html_page(title
, body
, "", 0);
2928 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2933 ca_cmd(struct tab
*t
, struct karg
*args
)
2936 int rv
= 1, certs
= 0, certs_read
;
2939 gnutls_x509_crt_t
*c
= NULL
;
2940 char *certs_buf
= NULL
, *s
;
2942 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2943 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
2947 if (fstat(fileno(f
), &sb
) == -1) {
2948 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
2952 certs_buf
= g_malloc(sb
.st_size
+ 1);
2953 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2954 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2957 certs_buf
[sb
.st_size
] = '\0';
2960 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2962 s
+= strlen("BEGIN CERTIFICATE");
2965 bzero(&dt
, sizeof dt
);
2966 dt
.data
= (unsigned char *)certs_buf
;
2967 dt
.size
= sb
.st_size
;
2968 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2969 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
2970 GNUTLS_X509_FMT_PEM
, 0);
2971 if (certs_read
<= 0) {
2972 show_oops(t
, "No cert(s) available");
2975 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2988 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2991 struct addrinfo hints
, *res
= NULL
, *ai
;
2995 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2998 su
= soup_uri_new(uri
);
3001 if (!SOUP_URI_VALID_FOR_HTTP(su
))
3004 snprintf(port
, sizeof port
, "%d", su
->port
);
3005 bzero(&hints
, sizeof(struct addrinfo
));
3006 hints
.ai_flags
= AI_CANONNAME
;
3007 hints
.ai_family
= AF_UNSPEC
;
3008 hints
.ai_socktype
= SOCK_STREAM
;
3010 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
3013 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3014 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3017 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3020 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3024 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
3029 strlcpy(domain
, su
->host
, domain_sz
);
3040 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3043 gnutls_deinit(gsession
);
3045 gnutls_certificate_free_credentials(xcred
);
3051 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3052 gnutls_certificate_credentials_t
*xc
)
3054 gnutls_certificate_credentials_t xcred
;
3055 gnutls_session_t gsession
;
3058 if (gs
== NULL
|| xc
== NULL
)
3064 gnutls_certificate_allocate_credentials(&xcred
);
3065 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3066 GNUTLS_X509_FMT_PEM
);
3067 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3068 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3069 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3070 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3071 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3072 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3074 gnutls_error_is_fatal(rv
),
3075 gnutls_strerror_name(rv
));
3076 stop_tls(gsession
, xcred
);
3080 gnutls_credentials_type_t cred
;
3081 cred
= gnutls_auth_get_type(gsession
);
3082 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3083 stop_tls(gsession
, xcred
);
3095 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3099 const gnutls_datum_t
*cl
;
3100 gnutls_x509_crt_t
*all_certs
;
3103 if (certs
== NULL
|| cert_count
== NULL
)
3105 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3107 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3111 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3112 for (i
= 0; i
< len
; i
++) {
3113 gnutls_x509_crt_init(&all_certs
[i
]);
3114 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3115 GNUTLS_X509_FMT_PEM
< 0)) {
3129 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3133 for (i
= 0; i
< cert_count
; i
++)
3134 gnutls_x509_crt_deinit(certs
[i
]);
3139 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3140 size_t cert_count
, char *domain
)
3143 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3148 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3151 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3152 if ((f
= fopen(file
, "w")) == NULL
) {
3153 show_oops(t
, "Can't create cert file %s %s",
3154 file
, strerror(errno
));
3158 for (i
= 0; i
< cert_count
; i
++) {
3159 cert_buf_sz
= sizeof cert_buf
;
3160 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3161 cert_buf
, &cert_buf_sz
)) {
3162 show_oops(t
, "gnutls_x509_crt_export failed");
3165 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3166 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3171 /* not the best spot but oh well */
3172 gdk_color_parse("lightblue", &color
);
3173 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3174 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3175 gdk_color_parse(XT_COLOR_BLACK
, &color
);
3176 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3182 load_compare_cert(struct tab
*t
, struct karg
*args
)
3185 char domain
[8182], file
[PATH_MAX
];
3186 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3187 int s
= -1, rv
= 1, i
;
3191 gnutls_session_t gsession
;
3192 gnutls_x509_crt_t
*certs
;
3193 gnutls_certificate_credentials_t xcred
;
3198 if ((uri
= get_uri(t
)) == NULL
)
3201 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3205 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3206 show_oops(t
, "Start TLS failed");
3211 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3212 show_oops(t
, "Can't get connection certificates");
3216 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3217 if ((f
= fopen(file
, "r")) == NULL
)
3220 for (i
= 0; i
< cert_count
; i
++) {
3221 cert_buf_sz
= sizeof cert_buf
;
3222 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3223 cert_buf
, &cert_buf_sz
)) {
3226 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3227 rv
= -1; /* critical */
3230 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3231 rv
= -1; /* critical */
3240 free_connection_certs(certs
, cert_count
);
3242 /* we close the socket first for speed */
3245 stop_tls(gsession
, xcred
);
3251 cert_cmd(struct tab
*t
, struct karg
*args
)
3257 gnutls_session_t gsession
;
3258 gnutls_x509_crt_t
*certs
;
3259 gnutls_certificate_credentials_t xcred
;
3264 if (ssl_ca_file
== NULL
) {
3265 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3269 if ((uri
= get_uri(t
)) == NULL
) {
3270 show_oops(t
, "Invalid URI");
3274 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3275 show_oops(t
, "Invalid certificate URI: %s", uri
);
3280 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3281 show_oops(t
, "Start TLS failed");
3286 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3287 show_oops(t
, "get_connection_certs failed");
3291 if (args
->i
& XT_SHOW
)
3292 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3293 else if (args
->i
& XT_SAVE
)
3294 save_certs(t
, certs
, cert_count
, domain
);
3296 free_connection_certs(certs
, cert_count
);
3298 /* we close the socket first for speed */
3301 stop_tls(gsession
, xcred
);
3307 remove_cookie(int index
)
3313 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3315 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3317 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3321 print_cookie("remove cookie", c
);
3322 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3327 soup_cookies_free(cf
);
3333 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3338 body
= g_strdup("");
3341 if (args
->i
& XT_WL_PERSISTENT
) {
3343 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3345 RB_FOREACH(d
, domain_list
, wl
) {
3349 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3355 if (args
->i
& XT_WL_SESSION
) {
3357 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3359 RB_FOREACH(d
, domain_list
, wl
) {
3363 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3368 tmp
= get_html_page(title
, body
, "", 0);
3371 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3373 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3379 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3381 char file
[PATH_MAX
];
3383 char *line
= NULL
, *lt
= NULL
;
3386 char *dom
= NULL
, *dom_save
= NULL
;
3392 if (t
== NULL
|| args
== NULL
)
3395 if (runtime_settings
[0] == '\0')
3398 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3399 if ((f
= fopen(file
, "r+")) == NULL
)
3403 dom
= find_domain(uri
, 1);
3404 if (uri
== NULL
|| dom
== NULL
) {
3405 show_oops(t
, "Can't add domain to %s white list",
3406 js
? "JavaScript" : "cookie");
3410 if (args
->i
& XT_WL_TOPLEVEL
) {
3412 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3413 show_oops(t
, "invalid domain: %s", dom
);
3416 } else if (args
->i
& XT_WL_FQDN
) {
3422 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3425 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3428 if (!strcmp(line
, lt
))
3434 fprintf(f
, "%s\n", lt
);
3439 d
= wl_find(dom_save
, &js_wl
);
3441 settings_add("js_wl", dom_save
);
3442 d
= wl_find(dom_save
, &js_wl
);
3446 d
= wl_find(dom_save
, &c_wl
);
3448 settings_add("cookie_wl", dom_save
);
3449 d
= wl_find(dom_save
, &c_wl
);
3453 /* find and add to persistent jar */
3454 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3455 for (;cf
; cf
= cf
->next
) {
3457 if (!strcmp(dom_save
, ci
->domain
) ||
3458 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3459 c
= soup_cookie_copy(ci
);
3460 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3463 soup_cookies_free(cf
);
3481 js_show_wl(struct tab
*t
, struct karg
*args
)
3483 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3484 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3490 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3492 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3493 wl_show(t
, args
, "Cookie White List", &c_wl
);
3499 cookie_cmd(struct tab
*t
, struct karg
*args
)
3501 if (args
->i
& XT_SHOW
)
3502 wl_show(t
, args
, "Cookie White List", &c_wl
);
3503 else if (args
->i
& XT_WL_TOGGLE
) {
3504 args
->i
|= XT_WL_RELOAD
;
3505 toggle_cwl(t
, args
);
3506 } else if (args
->i
& XT_SAVE
) {
3507 args
->i
|= XT_WL_RELOAD
;
3508 wl_save(t
, args
, 0);
3509 } else if (args
->i
& XT_DELETE
)
3510 show_oops(t
, "'cookie delete' currently unimplemented");
3516 js_cmd(struct tab
*t
, struct karg
*args
)
3518 if (args
->i
& XT_SHOW
)
3519 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3520 else if (args
->i
& XT_SAVE
) {
3521 args
->i
|= XT_WL_RELOAD
;
3522 wl_save(t
, args
, 1);
3523 } else if (args
->i
& XT_WL_TOGGLE
) {
3524 args
->i
|= XT_WL_RELOAD
;
3526 } else if (args
->i
& XT_DELETE
)
3527 show_oops(t
, "'js delete' currently unimplemented");
3533 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3535 js_toggle_cb(t
->js_toggle
, t
);
3541 add_favorite(struct tab
*t
, struct karg
*args
)
3543 char file
[PATH_MAX
];
3546 size_t urilen
, linelen
;
3547 const gchar
*uri
, *title
;
3552 /* don't allow adding of xtp pages to favorites */
3553 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3554 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3558 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3559 if ((f
= fopen(file
, "r+")) == NULL
) {
3560 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3564 title
= webkit_web_view_get_title(t
->wv
);
3570 if (title
== NULL
|| uri
== NULL
) {
3571 show_oops(t
, "can't add page to favorites");
3575 urilen
= strlen(uri
);
3578 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3579 if (feof(f
) || ferror(f
))
3582 if (linelen
== urilen
&& !strcmp(line
, uri
))
3589 fprintf(f
, "\n%s\n%s", title
, uri
);
3595 update_favorite_tabs(NULL
);
3601 navaction(struct tab
*t
, struct karg
*args
)
3603 WebKitWebHistoryItem
*item
;
3605 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3606 t
->tab_id
, args
->i
);
3609 if (args
->i
== XT_NAV_BACK
)
3610 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3612 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3614 return (XT_CB_PASSTHROUGH
);
3615 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3617 return (XT_CB_PASSTHROUGH
);
3622 webkit_web_view_go_back(t
->wv
);
3624 case XT_NAV_FORWARD
:
3625 webkit_web_view_go_forward(t
->wv
);
3628 webkit_web_view_reload(t
->wv
);
3630 case XT_NAV_RELOAD_CACHE
:
3631 webkit_web_view_reload_bypass_cache(t
->wv
);
3634 return (XT_CB_PASSTHROUGH
);
3638 move(struct tab
*t
, struct karg
*args
)
3640 GtkAdjustment
*adjust
;
3641 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3646 case XT_MOVE_BOTTOM
:
3648 case XT_MOVE_PAGEDOWN
:
3649 case XT_MOVE_PAGEUP
:
3650 case XT_MOVE_HALFDOWN
:
3651 case XT_MOVE_HALFUP
:
3652 adjust
= t
->adjust_v
;
3655 adjust
= t
->adjust_h
;
3659 pos
= gtk_adjustment_get_value(adjust
);
3660 ps
= gtk_adjustment_get_page_size(adjust
);
3661 upper
= gtk_adjustment_get_upper(adjust
);
3662 lower
= gtk_adjustment_get_lower(adjust
);
3663 si
= gtk_adjustment_get_step_increment(adjust
);
3664 pi
= gtk_adjustment_get_page_increment(adjust
);
3667 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3668 "max %f si %f pi %f\n",
3669 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3670 pos
, ps
, upper
, lower
, max
, si
, pi
);
3676 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3681 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3683 case XT_MOVE_BOTTOM
:
3684 case XT_MOVE_FARRIGHT
:
3685 gtk_adjustment_set_value(adjust
, max
);
3688 case XT_MOVE_FARLEFT
:
3689 gtk_adjustment_set_value(adjust
, lower
);
3691 case XT_MOVE_PAGEDOWN
:
3693 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3695 case XT_MOVE_PAGEUP
:
3697 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3699 case XT_MOVE_HALFDOWN
:
3701 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3703 case XT_MOVE_HALFUP
:
3705 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3708 return (XT_CB_PASSTHROUGH
);
3711 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3713 return (XT_CB_HANDLED
);
3717 url_set_visibility(void)
3721 TAILQ_FOREACH(t
, &tabs
, entry
) {
3722 if (show_url
== 0) {
3723 gtk_widget_hide(t
->toolbar
);
3726 gtk_widget_show(t
->toolbar
);
3731 notebook_tab_set_visibility()
3733 if (show_tabs
== 0) {
3734 gtk_widget_hide(tab_bar
);
3735 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3737 if (tab_style
== XT_TABS_NORMAL
) {
3738 gtk_widget_hide(tab_bar
);
3739 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3740 } else if (tab_style
== XT_TABS_COMPACT
) {
3741 gtk_widget_show(tab_bar
);
3742 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3748 statusbar_set_visibility(void)
3752 TAILQ_FOREACH(t
, &tabs
, entry
) {
3753 if (show_statusbar
== 0) {
3754 gtk_widget_hide(t
->statusbar
);
3757 gtk_widget_show(t
->statusbar
);
3762 url_set(struct tab
*t
, int enable_url_entry
)
3767 show_url
= enable_url_entry
;
3769 if (enable_url_entry
) {
3770 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3771 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3772 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3774 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3775 GTK_ENTRY_ICON_PRIMARY
);
3777 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3778 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3779 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3780 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3786 fullscreen(struct tab
*t
, struct karg
*args
)
3788 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3791 return (XT_CB_PASSTHROUGH
);
3793 if (show_url
== 0) {
3801 url_set_visibility();
3802 notebook_tab_set_visibility();
3804 return (XT_CB_HANDLED
);
3808 statusaction(struct tab
*t
, struct karg
*args
)
3810 int rv
= XT_CB_HANDLED
;
3812 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3815 return (XT_CB_PASSTHROUGH
);
3818 case XT_STATUSBAR_SHOW
:
3819 if (show_statusbar
== 0) {
3821 statusbar_set_visibility();
3824 case XT_STATUSBAR_HIDE
:
3825 if (show_statusbar
== 1) {
3827 statusbar_set_visibility();
3835 urlaction(struct tab
*t
, struct karg
*args
)
3837 int rv
= XT_CB_HANDLED
;
3839 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3842 return (XT_CB_PASSTHROUGH
);
3846 if (show_url
== 0) {
3848 url_set_visibility();
3852 if (show_url
== 1) {
3854 url_set_visibility();
3862 tabaction(struct tab
*t
, struct karg
*args
)
3864 int rv
= XT_CB_HANDLED
;
3865 char *url
= args
->s
;
3869 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3872 return (XT_CB_PASSTHROUGH
);
3876 if (strlen(url
) > 0)
3877 create_new_tab(url
, NULL
, 1, args
->p
);
3879 create_new_tab(NULL
, NULL
, 1, args
->p
);
3885 TAILQ_FOREACH(tt
, &tabs
, entry
)
3886 if (tt
->tab_id
== args
->p
- 1) {
3892 case XT_TAB_DELQUIT
:
3893 if (gtk_notebook_get_n_pages(notebook
) > 1)
3899 if (strlen(url
) > 0)
3902 rv
= XT_CB_PASSTHROUGH
;
3908 if (show_tabs
== 0) {
3910 notebook_tab_set_visibility();
3914 if (show_tabs
== 1) {
3916 notebook_tab_set_visibility();
3919 case XT_TAB_NEXTSTYLE
:
3920 if (tab_style
== XT_TABS_NORMAL
)
3921 tab_style
= XT_TABS_COMPACT
;
3923 tab_style
= XT_TABS_NORMAL
;
3924 notebook_tab_set_visibility();
3926 case XT_TAB_UNDO_CLOSE
:
3927 if (undo_count
== 0) {
3928 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3932 u
= TAILQ_FIRST(&undos
);
3933 create_new_tab(u
->uri
, u
, 1, -1);
3935 TAILQ_REMOVE(&undos
, u
, entry
);
3937 /* u->history is freed in create_new_tab() */
3942 rv
= XT_CB_PASSTHROUGH
;
3956 resizetab(struct tab
*t
, struct karg
*args
)
3958 if (t
== NULL
|| args
== NULL
) {
3959 show_oops(NULL
, "resizetab invalid parameters");
3960 return (XT_CB_PASSTHROUGH
);
3963 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3964 t
->tab_id
, args
->i
);
3966 adjustfont_webkit(t
, args
->i
);
3968 return (XT_CB_HANDLED
);
3972 movetab(struct tab
*t
, struct karg
*args
)
3976 if (t
== NULL
|| args
== NULL
) {
3977 show_oops(NULL
, "movetab invalid parameters");
3978 return (XT_CB_PASSTHROUGH
);
3981 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3982 t
->tab_id
, args
->i
);
3984 if (args
->i
>= XT_TAB_INVALID
)
3985 return (XT_CB_PASSTHROUGH
);
3987 if (TAILQ_EMPTY(&tabs
))
3988 return (XT_CB_PASSTHROUGH
);
3990 n
= gtk_notebook_get_n_pages(notebook
);
3991 dest
= gtk_notebook_get_current_page(notebook
);
3996 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4005 dest
-= args
->p
% n
;
4018 return (XT_CB_PASSTHROUGH
);
4021 if (dest
< 0 || dest
>= n
)
4022 return (XT_CB_PASSTHROUGH
);
4023 if (t
->tab_id
== dest
) {
4024 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4025 return (XT_CB_HANDLED
);
4028 set_current_tab(dest
);
4030 return (XT_CB_HANDLED
);
4036 command(struct tab
*t
, struct karg
*args
)
4038 char *s
= NULL
, *ss
= NULL
;
4042 if (t
== NULL
|| args
== NULL
) {
4043 show_oops(NULL
, "command invalid parameters");
4044 return (XT_CB_PASSTHROUGH
);
4055 if (cmd_prefix
== 0)
4058 ss
= g_strdup_printf(":%d", cmd_prefix
);
4069 case XT_CMD_OPEN_CURRENT
:
4072 case XT_CMD_TABNEW_CURRENT
:
4073 if (!s
) /* FALL THROUGH? */
4075 if ((uri
= get_uri(t
)) != NULL
) {
4076 ss
= g_strdup_printf("%s%s", s
, uri
);
4081 show_oops(t
, "command: invalid opcode %d", args
->i
);
4082 return (XT_CB_PASSTHROUGH
);
4085 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4087 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4088 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4089 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4091 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4092 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4097 return (XT_CB_HANDLED
);
4101 * Return a new string with a download row (in html)
4102 * appended. Old string is freed.
4105 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4108 WebKitDownloadStatus stat
;
4109 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4111 char cur_sz
[FMT_SCALED_STRSIZE
];
4112 char tot_sz
[FMT_SCALED_STRSIZE
];
4115 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4117 /* All actions wil take this form:
4118 * xxxt://class/seskey
4120 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4121 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4123 stat
= webkit_download_get_status(dl
->download
);
4126 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4127 status_html
= g_strdup_printf("Finished");
4128 cmd_html
= g_strdup_printf(
4129 "<a href='%s%d/%d'>Remove</a>",
4130 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4132 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4133 /* gather size info */
4134 progress
= 100 * webkit_download_get_progress(dl
->download
);
4137 webkit_download_get_current_size(dl
->download
), cur_sz
);
4139 webkit_download_get_total_size(dl
->download
), tot_sz
);
4141 status_html
= g_strdup_printf(
4142 "<div style='width: 100%%' align='center'>"
4143 "<div class='progress-outer'>"
4144 "<div class='progress-inner' style='width: %.2f%%'>"
4145 "</div></div></div>"
4146 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4147 progress
, cur_sz
, tot_sz
, progress
);
4149 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4150 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4154 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4155 status_html
= g_strdup_printf("Cancelled");
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_ERROR
:
4160 status_html
= g_strdup_printf("Error!");
4161 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4162 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4164 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4165 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4166 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4167 status_html
= g_strdup_printf("Starting");
4170 show_oops(t
, "%s: unknown download status", __func__
);
4173 new_html
= g_strdup_printf(
4174 "%s\n<tr><td>%s</td><td>%s</td>"
4175 "<td style='text-align:center'>%s</td></tr>\n",
4176 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4177 status_html
, cmd_html
);
4181 g_free(status_html
);
4192 * update all download tabs apart from one. Pass NULL if
4193 * you want to update all.
4196 update_download_tabs(struct tab
*apart_from
)
4199 if (!updating_dl_tabs
) {
4200 updating_dl_tabs
= 1; /* stop infinite recursion */
4201 TAILQ_FOREACH(t
, &tabs
, entry
)
4202 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4203 && (t
!= apart_from
))
4204 xtp_page_dl(t
, NULL
);
4205 updating_dl_tabs
= 0;
4210 * update all cookie tabs apart from one. Pass NULL if
4211 * you want to update all.
4214 update_cookie_tabs(struct tab
*apart_from
)
4217 if (!updating_cl_tabs
) {
4218 updating_cl_tabs
= 1; /* stop infinite recursion */
4219 TAILQ_FOREACH(t
, &tabs
, entry
)
4220 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4221 && (t
!= apart_from
))
4222 xtp_page_cl(t
, NULL
);
4223 updating_cl_tabs
= 0;
4228 * update all history tabs apart from one. Pass NULL if
4229 * you want to update all.
4232 update_history_tabs(struct tab
*apart_from
)
4236 if (!updating_hl_tabs
) {
4237 updating_hl_tabs
= 1; /* stop infinite recursion */
4238 TAILQ_FOREACH(t
, &tabs
, entry
)
4239 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4240 && (t
!= apart_from
))
4241 xtp_page_hl(t
, NULL
);
4242 updating_hl_tabs
= 0;
4246 /* cookie management XTP page */
4248 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4250 char *body
, *page
, *tmp
;
4251 int i
= 1; /* all ids start 1 */
4252 GSList
*sc
, *pc
, *pc_start
;
4254 char *type
, *table_headers
, *last_domain
;
4256 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4259 show_oops(NULL
, "%s invalid parameters", __func__
);
4263 /* Generate a new session key */
4264 if (!updating_cl_tabs
)
4265 generate_xtp_session_key(&cl_session_key
);
4268 table_headers
= g_strdup_printf("<table><tr>"
4271 "<th style='width:200px'>Value</th>"
4275 "<th>HTTP<br />only</th>"
4276 "<th style='width:40px'>Rm</th></tr>\n");
4278 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4279 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4283 last_domain
= strdup("");
4284 for (; sc
; sc
= sc
->next
) {
4287 if (strcmp(last_domain
, c
->domain
) != 0) {
4290 last_domain
= strdup(c
->domain
);
4294 body
= g_strdup_printf("%s</table>"
4296 body
, c
->domain
, table_headers
);
4300 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4301 c
->domain
, table_headers
);
4306 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4307 if (soup_cookie_equal(pc
->data
, c
)) {
4308 type
= "Session + Persistent";
4313 body
= g_strdup_printf(
4316 "<td style='word-wrap:normal'>%s</td>"
4318 " <textarea rows='4'>%s</textarea>"
4324 "<td style='text-align:center'>"
4325 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4332 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4347 soup_cookies_free(sc
);
4348 soup_cookies_free(pc
);
4350 /* small message if there are none */
4352 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4353 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4356 body
= g_strdup_printf("%s</table>", body
);
4359 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4361 g_free(table_headers
);
4362 g_free(last_domain
);
4364 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4365 update_cookie_tabs(t
);
4373 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4375 char *body
, *page
, *tmp
;
4377 int i
= 1; /* all ids start 1 */
4379 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4382 show_oops(NULL
, "%s invalid parameters", __func__
);
4386 /* Generate a new session key */
4387 if (!updating_hl_tabs
)
4388 generate_xtp_session_key(&hl_session_key
);
4391 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4392 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4394 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4396 body
= g_strdup_printf(
4398 "<td><a href='%s'>%s</a></td>"
4400 "<td style='text-align: center'>"
4401 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4402 body
, h
->uri
, h
->uri
, h
->title
,
4403 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4404 XT_XTP_HL_REMOVE
, i
);
4410 /* small message if there are none */
4413 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4414 "colspan='3'>No History</td></tr>\n", body
);
4419 body
= g_strdup_printf("%s</table>", body
);
4422 page
= get_html_page("History", body
, "", TRUE
);
4426 * update all history manager tabs as the xtp session
4427 * key has now changed. No need to update the current tab.
4428 * Already did that above.
4430 update_history_tabs(t
);
4432 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4439 * Generate a web page detailing the status of any downloads
4442 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4444 struct download
*dl
;
4445 char *body
, *page
, *tmp
;
4449 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4452 show_oops(NULL
, "%s invalid parameters", __func__
);
4457 * Generate a new session key for next page instance.
4458 * This only happens for the top level call to xtp_page_dl()
4459 * in which case updating_dl_tabs is 0.
4461 if (!updating_dl_tabs
)
4462 generate_xtp_session_key(&dl_session_key
);
4464 /* header - with refresh so as to update */
4465 if (refresh_interval
>= 1)
4466 ref
= g_strdup_printf(
4467 "<meta http-equiv='refresh' content='%u"
4468 ";url=%s%d/%s/%d' />\n",
4477 body
= g_strdup_printf("<div align='center'>"
4478 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4479 "</p><table><tr><th style='width: 60%%'>"
4480 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4481 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4483 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4484 body
= xtp_page_dl_row(t
, body
, dl
);
4488 /* message if no downloads in list */
4491 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4492 " style='text-align: center'>"
4493 "No downloads</td></tr>\n", body
);
4498 body
= g_strdup_printf("%s</table></div>", body
);
4501 page
= get_html_page("Downloads", body
, ref
, 1);
4506 * update all download manager tabs as the xtp session
4507 * key has now changed. No need to update the current tab.
4508 * Already did that above.
4510 update_download_tabs(t
);
4512 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4519 search(struct tab
*t
, struct karg
*args
)
4523 if (t
== NULL
|| args
== NULL
) {
4524 show_oops(NULL
, "search invalid parameters");
4527 if (t
->search_text
== NULL
) {
4528 if (global_search
== NULL
)
4529 return (XT_CB_PASSTHROUGH
);
4531 t
->search_text
= g_strdup(global_search
);
4532 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4533 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4537 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4538 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4541 case XT_SEARCH_NEXT
:
4542 d
= t
->search_forward
;
4544 case XT_SEARCH_PREV
:
4545 d
= !t
->search_forward
;
4548 return (XT_CB_PASSTHROUGH
);
4551 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4553 return (XT_CB_HANDLED
);
4556 struct settings_args
{
4562 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4565 struct settings_args
*sa
= cb_args
;
4570 if (s
->flags
& XT_SF_RUNTIME
)
4576 *sa
->body
= g_strdup_printf(
4578 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4579 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4591 set(struct tab
*t
, struct karg
*args
)
4593 char *body
, *page
, *tmp
;
4595 struct settings_args sa
;
4597 bzero(&sa
, sizeof sa
);
4601 body
= g_strdup_printf("<div align='center'><table><tr>"
4602 "<th align='left'>Setting</th>"
4603 "<th align='left'>Value</th></tr>\n");
4605 settings_walk(print_setting
, &sa
);
4608 /* small message if there are none */
4611 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4612 "colspan='2'>No settings</td></tr>\n", body
);
4617 body
= g_strdup_printf("%s</table></div>", body
);
4620 page
= get_html_page("Settings", body
, "", 0);
4624 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4628 return (XT_CB_PASSTHROUGH
);
4632 session_save(struct tab
*t
, char *filename
)
4637 if (strlen(filename
) == 0)
4640 if (filename
[0] == '.' || filename
[0] == '/')
4644 if (save_tabs(t
, &a
))
4646 strlcpy(named_session
, filename
, sizeof named_session
);
4654 session_open(struct tab
*t
, char *filename
)
4659 if (strlen(filename
) == 0)
4662 if (filename
[0] == '.' || filename
[0] == '/')
4666 a
.i
= XT_SES_CLOSETABS
;
4667 if (open_tabs(t
, &a
))
4670 strlcpy(named_session
, filename
, sizeof named_session
);
4678 session_delete(struct tab
*t
, char *filename
)
4680 char file
[PATH_MAX
];
4683 if (strlen(filename
) == 0)
4686 if (filename
[0] == '.' || filename
[0] == '/')
4689 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4693 if (!strcmp(filename
, named_session
))
4694 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4695 sizeof named_session
);
4703 session_cmd(struct tab
*t
, struct karg
*args
)
4705 char *filename
= args
->s
;
4710 if (args
->i
& XT_SHOW
)
4711 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4712 XT_SAVED_TABS_FILE
: named_session
);
4713 else if (args
->i
& XT_SAVE
) {
4714 if (session_save(t
, filename
)) {
4715 show_oops(t
, "Can't save session: %s",
4716 filename
? filename
: "INVALID");
4719 } else if (args
->i
& XT_OPEN
) {
4720 if (session_open(t
, filename
)) {
4721 show_oops(t
, "Can't open session: %s",
4722 filename
? filename
: "INVALID");
4725 } else if (args
->i
& XT_DELETE
) {
4726 if (session_delete(t
, filename
)) {
4727 show_oops(t
, "Can't delete session: %s",
4728 filename
? filename
: "INVALID");
4733 return (XT_CB_PASSTHROUGH
);
4737 * Make a hardcopy of the page
4740 print_page(struct tab
*t
, struct karg
*args
)
4742 WebKitWebFrame
*frame
;
4744 GtkPrintOperation
*op
;
4745 GtkPrintOperationAction action
;
4746 GtkPrintOperationResult print_res
;
4747 GError
*g_err
= NULL
;
4748 int marg_l
, marg_r
, marg_t
, marg_b
;
4750 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4752 ps
= gtk_page_setup_new();
4753 op
= gtk_print_operation_new();
4754 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4755 frame
= webkit_web_view_get_main_frame(t
->wv
);
4757 /* the default margins are too small, so we will bump them */
4758 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4759 XT_PRINT_EXTRA_MARGIN
;
4760 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4761 XT_PRINT_EXTRA_MARGIN
;
4762 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4763 XT_PRINT_EXTRA_MARGIN
;
4764 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4765 XT_PRINT_EXTRA_MARGIN
;
4768 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4769 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4770 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4771 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4773 gtk_print_operation_set_default_page_setup(op
, ps
);
4775 /* this appears to free 'op' and 'ps' */
4776 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4778 /* check it worked */
4779 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4780 show_oops(NULL
, "can't print: %s", g_err
->message
);
4781 g_error_free (g_err
);
4789 go_home(struct tab
*t
, struct karg
*args
)
4796 restart(struct tab
*t
, struct karg
*args
)
4800 a
.s
= XT_RESTART_TABS_FILE
;
4802 execvp(start_argv
[0], start_argv
);
4808 #define CTRL GDK_CONTROL_MASK
4809 #define MOD1 GDK_MOD1_MASK
4810 #define SHFT GDK_SHIFT_MASK
4812 /* inherent to GTK not all keys will be caught at all times */
4813 /* XXX sort key bindings */
4814 struct key_binding
{
4819 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4821 { "cookiejar", MOD1
, 0, GDK_j
},
4822 { "downloadmgr", MOD1
, 0, GDK_d
},
4823 { "history", MOD1
, 0, GDK_h
},
4824 { "print", CTRL
, 0, GDK_p
},
4825 { "search", 0, 0, GDK_slash
},
4826 { "searchb", 0, 0, GDK_question
},
4827 { "command", 0, 0, GDK_colon
},
4828 { "qa", CTRL
, 0, GDK_q
},
4829 { "restart", MOD1
, 0, GDK_q
},
4830 { "js toggle", CTRL
, 0, GDK_j
},
4831 { "cookie toggle", MOD1
, 0, GDK_c
},
4832 { "togglesrc", CTRL
, 0, GDK_s
},
4833 { "yankuri", 0, 0, GDK_y
},
4834 { "pasteuricur", 0, 0, GDK_p
},
4835 { "pasteurinew", 0, 0, GDK_P
},
4836 { "toplevel toggle", 0, 0, GDK_F4
},
4837 { "help", 0, 0, GDK_F1
},
4840 { "searchnext", 0, 0, GDK_n
},
4841 { "searchprevious", 0, 0, GDK_N
},
4844 { "focusaddress", 0, 0, GDK_F6
},
4845 { "focussearch", 0, 0, GDK_F7
},
4848 { "hinting", 0, 0, GDK_f
},
4850 /* custom stylesheet */
4851 { "userstyle", 0, 0, GDK_i
},
4854 { "goback", 0, 0, GDK_BackSpace
},
4855 { "goback", MOD1
, 0, GDK_Left
},
4856 { "goforward", SHFT
, 0, GDK_BackSpace
},
4857 { "goforward", MOD1
, 0, GDK_Right
},
4858 { "reload", 0, 0, GDK_F5
},
4859 { "reload", CTRL
, 0, GDK_r
},
4860 { "reloadforce", CTRL
, 0, GDK_R
},
4861 { "reload", CTRL
, 0, GDK_l
},
4862 { "favorites", MOD1
, 1, GDK_f
},
4864 /* vertical movement */
4865 { "scrolldown", 0, 0, GDK_j
},
4866 { "scrolldown", 0, 0, GDK_Down
},
4867 { "scrollup", 0, 0, GDK_Up
},
4868 { "scrollup", 0, 0, GDK_k
},
4869 { "scrollbottom", 0, 0, GDK_G
},
4870 { "scrollbottom", 0, 0, GDK_End
},
4871 { "scrolltop", 0, 0, GDK_Home
},
4872 { "scrolltop", 0, 0, GDK_g
},
4873 { "scrollpagedown", 0, 0, GDK_space
},
4874 { "scrollpagedown", CTRL
, 0, GDK_f
},
4875 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4876 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4877 { "scrollpageup", 0, 0, GDK_Page_Up
},
4878 { "scrollpageup", CTRL
, 0, GDK_b
},
4879 { "scrollhalfup", CTRL
, 0, GDK_u
},
4880 /* horizontal movement */
4881 { "scrollright", 0, 0, GDK_l
},
4882 { "scrollright", 0, 0, GDK_Right
},
4883 { "scrollleft", 0, 0, GDK_Left
},
4884 { "scrollleft", 0, 0, GDK_h
},
4885 { "scrollfarright", 0, 0, GDK_dollar
},
4886 { "scrollfarleft", 0, 0, GDK_0
},
4889 { "tabnew", CTRL
, 0, GDK_t
},
4890 { "999tabnew", CTRL
, 0, GDK_T
},
4891 { "tabclose", CTRL
, 1, GDK_w
},
4892 { "tabundoclose", 0, 0, GDK_U
},
4893 { "tabnext 1", CTRL
, 0, GDK_1
},
4894 { "tabnext 2", CTRL
, 0, GDK_2
},
4895 { "tabnext 3", CTRL
, 0, GDK_3
},
4896 { "tabnext 4", CTRL
, 0, GDK_4
},
4897 { "tabnext 5", CTRL
, 0, GDK_5
},
4898 { "tabnext 6", CTRL
, 0, GDK_6
},
4899 { "tabnext 7", CTRL
, 0, GDK_7
},
4900 { "tabnext 8", CTRL
, 0, GDK_8
},
4901 { "tabnext 9", CTRL
, 0, GDK_9
},
4902 { "tabnext 10", CTRL
, 0, GDK_0
},
4903 { "tabfirst", CTRL
, 0, GDK_less
},
4904 { "tablast", CTRL
, 0, GDK_greater
},
4905 { "tabprevious", CTRL
, 0, GDK_Left
},
4906 { "tabnext", CTRL
, 0, GDK_Right
},
4907 { "focusout", CTRL
, 0, GDK_minus
},
4908 { "focusin", CTRL
, 0, GDK_plus
},
4909 { "focusin", CTRL
, 0, GDK_equal
},
4911 /* command aliases (handy when -S flag is used) */
4912 { "promptopen", 0, 0, GDK_F9
},
4913 { "promptopencurrent", 0, 0, GDK_F10
},
4914 { "prompttabnew", 0, 0, GDK_F11
},
4915 { "prompttabnewcurrent",0, 0, GDK_F12
},
4917 TAILQ_HEAD(keybinding_list
, key_binding
);
4920 walk_kb(struct settings
*s
,
4921 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4923 struct key_binding
*k
;
4926 if (s
== NULL
|| cb
== NULL
) {
4927 show_oops(NULL
, "walk_kb invalid parameters");
4931 TAILQ_FOREACH(k
, &kbl
, entry
) {
4937 if (gdk_keyval_name(k
->key
) == NULL
)
4940 strlcat(str
, k
->cmd
, sizeof str
);
4941 strlcat(str
, ",", sizeof str
);
4943 if (k
->mask
& GDK_SHIFT_MASK
)
4944 strlcat(str
, "S-", sizeof str
);
4945 if (k
->mask
& GDK_CONTROL_MASK
)
4946 strlcat(str
, "C-", sizeof str
);
4947 if (k
->mask
& GDK_MOD1_MASK
)
4948 strlcat(str
, "M1-", sizeof str
);
4949 if (k
->mask
& GDK_MOD2_MASK
)
4950 strlcat(str
, "M2-", sizeof str
);
4951 if (k
->mask
& GDK_MOD3_MASK
)
4952 strlcat(str
, "M3-", sizeof str
);
4953 if (k
->mask
& GDK_MOD4_MASK
)
4954 strlcat(str
, "M4-", sizeof str
);
4955 if (k
->mask
& GDK_MOD5_MASK
)
4956 strlcat(str
, "M5-", sizeof str
);
4958 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4959 cb(s
, str
, cb_args
);
4964 init_keybindings(void)
4967 struct key_binding
*k
;
4969 for (i
= 0; i
< LENGTH(keys
); i
++) {
4970 k
= g_malloc0(sizeof *k
);
4971 k
->cmd
= keys
[i
].cmd
;
4972 k
->mask
= keys
[i
].mask
;
4973 k
->use_in_entry
= keys
[i
].use_in_entry
;
4974 k
->key
= keys
[i
].key
;
4975 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4977 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4978 k
->cmd
? k
->cmd
: "unnamed key");
4983 keybinding_clearall(void)
4985 struct key_binding
*k
, *next
;
4987 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4988 next
= TAILQ_NEXT(k
, entry
);
4992 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4993 k
->cmd
? k
->cmd
: "unnamed key");
4994 TAILQ_REMOVE(&kbl
, k
, entry
);
5000 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5002 struct key_binding
*k
;
5003 guint keyval
, mask
= 0;
5006 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5008 /* Keys which are to be used in entry have been prefixed with an
5009 * exclamation mark. */
5013 /* find modifier keys */
5014 if (strstr(key
, "S-"))
5015 mask
|= GDK_SHIFT_MASK
;
5016 if (strstr(key
, "C-"))
5017 mask
|= GDK_CONTROL_MASK
;
5018 if (strstr(key
, "M1-"))
5019 mask
|= GDK_MOD1_MASK
;
5020 if (strstr(key
, "M2-"))
5021 mask
|= GDK_MOD2_MASK
;
5022 if (strstr(key
, "M3-"))
5023 mask
|= GDK_MOD3_MASK
;
5024 if (strstr(key
, "M4-"))
5025 mask
|= GDK_MOD4_MASK
;
5026 if (strstr(key
, "M5-"))
5027 mask
|= GDK_MOD5_MASK
;
5030 for (i
= strlen(key
) - 1; i
> 0; i
--)
5034 /* validate keyname */
5035 keyval
= gdk_keyval_from_name(key
);
5036 if (keyval
== GDK_VoidSymbol
) {
5037 warnx("invalid keybinding name %s", key
);
5040 /* must run this test too, gtk+ doesn't handle 10 for example */
5041 if (gdk_keyval_name(keyval
) == NULL
) {
5042 warnx("invalid keybinding name %s", key
);
5046 /* Remove eventual dupes. */
5047 TAILQ_FOREACH(k
, &kbl
, entry
)
5048 if (k
->key
== keyval
&& k
->mask
== mask
) {
5049 TAILQ_REMOVE(&kbl
, k
, entry
);
5055 k
= g_malloc0(sizeof *k
);
5056 k
->cmd
= g_strdup(cmd
);
5058 k
->use_in_entry
= use_in_entry
;
5061 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5066 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5067 k
->cmd
, gdk_keyval_name(keyval
));
5069 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5075 add_kb(struct settings
*s
, char *entry
)
5079 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5081 /* clearall is special */
5082 if (!strcmp(entry
, "clearall")) {
5083 keybinding_clearall();
5087 kb
= strstr(entry
, ",");
5093 return (keybinding_add(entry
, key
, key
[0] == '!'));
5099 int (*func
)(struct tab
*, struct karg
*);
5103 { "command", 0, command
, ':', 0 },
5104 { "search", 0, command
, '/', 0 },
5105 { "searchb", 0, command
, '?', 0 },
5106 { "togglesrc", 0, toggle_src
, 0, 0 },
5108 /* yanking and pasting */
5109 { "yankuri", 0, yank_uri
, 0, 0 },
5110 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5111 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5112 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5115 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5116 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5119 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5120 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5123 { "hinting", 0, hint
, 0, 0 },
5125 /* custom stylesheet */
5126 { "userstyle", 0, userstyle
, 0, 0 },
5129 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5130 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5131 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5132 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
5134 /* vertical movement */
5135 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5136 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5137 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5138 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5139 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5140 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5141 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5142 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5143 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5144 /* horizontal movement */
5145 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5146 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5147 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5148 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5151 { "favorites", 0, xtp_page_fl
, 0, 0 },
5152 { "fav", 0, xtp_page_fl
, 0, 0 },
5153 { "favadd", 0, add_favorite
, 0, 0 },
5155 { "qall", 0, quit
, 0, 0 },
5156 { "quitall", 0, quit
, 0, 0 },
5157 { "w", 0, save_tabs
, 0, 0 },
5158 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5159 { "help", 0, help
, 0, 0 },
5160 { "about", 0, about
, 0, 0 },
5161 { "stats", 0, stats
, 0, 0 },
5162 { "version", 0, about
, 0, 0 },
5165 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5166 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5167 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5168 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5169 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5170 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5171 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5172 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5173 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5174 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5175 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5177 /* cookie command */
5178 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5179 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5180 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5181 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5182 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5183 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5184 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5185 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5186 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5187 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5188 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5190 /* toplevel (domain) command */
5191 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5192 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5195 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5198 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5199 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5200 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5202 { "ca", 0, ca_cmd
, 0, 0 },
5203 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5204 { "dl", 0, xtp_page_dl
, 0, 0 },
5205 { "h", 0, xtp_page_hl
, 0, 0 },
5206 { "history", 0, xtp_page_hl
, 0, 0 },
5207 { "home", 0, go_home
, 0, 0 },
5208 { "restart", 0, restart
, 0, 0 },
5209 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5210 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5211 { "statushide", 0, statusaction
, XT_STATUSBAR_HIDE
, 0 },
5212 { "statusshow", 0, statusaction
, XT_STATUSBAR_SHOW
, 0 },
5214 { "print", 0, print_page
, 0, 0 },
5217 { "focusin", 0, resizetab
, 1, 0 },
5218 { "focusout", 0, resizetab
, -1, 0 },
5219 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5220 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5221 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5222 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5223 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5224 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5225 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5226 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5227 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5228 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5229 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5230 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5231 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5232 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5233 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5234 { "buffers", 0, buffers
, 0, 0 },
5235 { "ls", 0, buffers
, 0, 0 },
5236 { "tabs", 0, buffers
, 0, 0 },
5238 /* command aliases (handy when -S flag is used) */
5239 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5240 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5241 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5242 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5245 { "set", 0, set
, 0, 0 },
5246 { "fullscreen", 0, fullscreen
, 0, 0 },
5247 { "f", 0, fullscreen
, 0, 0 },
5250 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5251 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5252 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5253 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5254 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5261 } cmd_status
= {-1, 0};
5264 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5271 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5277 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5279 a
.i
= XT_NAV_FORWARD
;
5289 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5291 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5293 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5300 * cancel, remove, etc. downloads
5303 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5305 struct download find
, *d
= NULL
;
5307 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5309 /* some commands require a valid download id */
5310 if (cmd
!= XT_XTP_DL_LIST
) {
5311 /* lookup download in question */
5313 d
= RB_FIND(download_list
, &downloads
, &find
);
5316 show_oops(t
, "%s: no such download", __func__
);
5321 /* decide what to do */
5323 case XT_XTP_DL_CANCEL
:
5324 webkit_download_cancel(d
->download
);
5326 case XT_XTP_DL_REMOVE
:
5327 webkit_download_cancel(d
->download
); /* just incase */
5328 g_object_unref(d
->download
);
5329 RB_REMOVE(download_list
, &downloads
, d
);
5331 case XT_XTP_DL_LIST
:
5335 show_oops(t
, "%s: unknown command", __func__
);
5338 xtp_page_dl(t
, NULL
);
5342 * Actions on history, only does one thing for now, but
5343 * we provide the function for future actions
5346 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5348 struct history
*h
, *next
;
5352 case XT_XTP_HL_REMOVE
:
5353 /* walk backwards, as listed in reverse */
5354 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5355 next
= RB_PREV(history_list
, &hl
, h
);
5357 RB_REMOVE(history_list
, &hl
, h
);
5358 g_free((gpointer
) h
->title
);
5359 g_free((gpointer
) h
->uri
);
5366 case XT_XTP_HL_LIST
:
5367 /* Nothing - just xtp_page_hl() below */
5370 show_oops(t
, "%s: unknown command", __func__
);
5374 xtp_page_hl(t
, NULL
);
5377 /* remove a favorite */
5379 remove_favorite(struct tab
*t
, int index
)
5381 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5382 char *new_favs
, *tmp
;
5387 /* open favorites */
5388 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5390 if ((f
= fopen(file
, "r")) == NULL
) {
5391 show_oops(t
, "%s: can't open favorites: %s",
5392 __func__
, strerror(errno
));
5396 /* build a string which will become the new favroites file */
5397 new_favs
= g_strdup("");
5400 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5401 if (feof(f
) || ferror(f
))
5403 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5410 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5411 if (feof(f
) || ferror(f
)) {
5412 show_oops(t
, "%s: can't parse favorites %s",
5413 __func__
, strerror(errno
));
5418 /* as long as this isn't the one we are deleting add to file */
5421 new_favs
= g_strdup_printf("%s%s\n%s\n",
5422 new_favs
, title
, uri
);
5434 /* write back new favorites file */
5435 if ((f
= fopen(file
, "w")) == NULL
) {
5436 show_oops(t
, "%s: can't open favorites: %s",
5437 __func__
, strerror(errno
));
5441 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5454 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5457 case XT_XTP_FL_LIST
:
5458 /* nothing, just the below call to xtp_page_fl() */
5460 case XT_XTP_FL_REMOVE
:
5461 remove_favorite(t
, arg
);
5464 show_oops(t
, "%s: invalid favorites command", __func__
);
5468 xtp_page_fl(t
, NULL
);
5472 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5475 case XT_XTP_CL_LIST
:
5476 /* nothing, just xtp_page_cl() */
5478 case XT_XTP_CL_REMOVE
:
5482 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5486 xtp_page_cl(t
, NULL
);
5489 /* link an XTP class to it's session key and handler function */
5490 struct xtp_despatch
{
5493 void (*handle_func
)(struct tab
*, uint8_t, int);
5496 struct xtp_despatch xtp_despatches
[] = {
5497 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5498 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5499 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5500 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5501 { XT_XTP_INVALID
, NULL
, NULL
}
5505 * is the url xtp protocol? (xxxt://)
5506 * if so, parse and despatch correct bahvior
5509 parse_xtp_url(struct tab
*t
, const char *url
)
5511 char *dup
= NULL
, *p
, *last
;
5512 uint8_t n_tokens
= 0;
5513 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5514 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5519 * tokens array meaning:
5521 * tokens[1] = session key
5522 * tokens[2] = action
5523 * tokens[3] = optional argument
5526 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5528 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5531 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5533 /* split out the url */
5534 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5535 (p
= strtok_r(NULL
, "/", &last
))) {
5537 tokens
[n_tokens
++] = p
;
5540 /* should be atleast three fields 'class/seskey/command/arg' */
5544 dsp
= xtp_despatches
;
5545 req_class
= atoi(tokens
[0]);
5546 while (dsp
->xtp_class
) {
5547 if (dsp
->xtp_class
== req_class
) {
5554 /* did we find one atall? */
5555 if (dsp_match
== NULL
) {
5556 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5560 /* check session key and call despatch function */
5561 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5562 ret
= TRUE
; /* all is well, this was a valid xtp request */
5563 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5576 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5578 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5580 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5583 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5588 show_oops(t
, "activate_uri_entry_cb no uri");
5592 uri
+= strspn(uri
, "\t ");
5594 /* if xxxt:// treat specially */
5595 if (parse_xtp_url(t
, uri
))
5598 /* otherwise continue to load page normally */
5599 load_uri(t
, (gchar
*)uri
);
5604 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5606 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5607 char *newuri
= NULL
;
5610 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5613 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5617 if (search_string
== NULL
) {
5618 show_oops(t
, "no search_string");
5622 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5623 newuri
= g_strdup_printf(search_string
, enc_search
);
5626 webkit_web_view_load_uri(t
->wv
, newuri
);
5634 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5636 struct domain
*d
= NULL
;
5639 if (uri
== NULL
|| t
== NULL
)
5642 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5647 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5648 es
? "enable" : "disable", uri
);
5650 g_object_set(G_OBJECT(t
->settings
),
5651 "enable-scripts", es
, (char *)NULL
);
5652 g_object_set(G_OBJECT(t
->settings
),
5653 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5654 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5656 button_set_stockid(t
->js_toggle
,
5657 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5661 show_ca_status(struct tab
*t
, const char *uri
)
5663 WebKitWebFrame
*frame
;
5664 WebKitWebDataSource
*source
;
5665 WebKitNetworkRequest
*request
;
5666 SoupMessage
*message
;
5668 gchar
*col_str
= XT_COLOR_WHITE
;
5671 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5672 ssl_strict_certs
, ssl_ca_file
, uri
);
5676 if (ssl_ca_file
== NULL
) {
5677 if (g_str_has_prefix(uri
, "http://"))
5679 if (g_str_has_prefix(uri
, "https://")) {
5680 col_str
= XT_COLOR_RED
;
5685 if (g_str_has_prefix(uri
, "http://") ||
5686 !g_str_has_prefix(uri
, "https://"))
5689 frame
= webkit_web_view_get_main_frame(t
->wv
);
5690 source
= webkit_web_frame_get_data_source(frame
);
5691 request
= webkit_web_data_source_get_request(source
);
5692 message
= webkit_network_request_get_message(request
);
5694 if (message
&& (soup_message_get_flags(message
) &
5695 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5696 col_str
= XT_COLOR_GREEN
;
5699 r
= load_compare_cert(t
, NULL
);
5701 col_str
= XT_COLOR_BLUE
;
5703 col_str
= XT_COLOR_YELLOW
;
5705 col_str
= XT_COLOR_RED
;
5710 gdk_color_parse(col_str
, &color
);
5711 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5713 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5714 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5716 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5717 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5720 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5722 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5723 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5730 free_favicon(struct tab
*t
)
5732 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5733 __func__
, t
->icon_download
, t
->icon_request
);
5735 if (t
->icon_request
)
5736 g_object_unref(t
->icon_request
);
5737 if (t
->icon_dest_uri
)
5738 g_free(t
->icon_dest_uri
);
5740 t
->icon_request
= NULL
;
5741 t
->icon_dest_uri
= NULL
;
5745 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5747 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5748 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5750 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5751 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5753 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5754 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5758 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5760 GdkPixbuf
*pb_scaled
;
5762 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5763 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16, GDK_INTERP_BILINEAR
);
5767 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5768 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5770 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5771 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5773 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5774 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5776 if (pb_scaled
!= pb
)
5777 g_object_unref(pb_scaled
);
5781 xt_icon_from_file(struct tab
*t
, char *file
)
5785 if (g_str_has_prefix(file
, "file://"))
5786 file
+= strlen("file://");
5788 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5790 xt_icon_from_pixbuf(t
, pb
);
5793 xt_icon_from_name(t
, "text-html");
5797 is_valid_icon(char *file
)
5800 const char *mime_type
;
5804 gf
= g_file_new_for_path(file
);
5805 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5807 mime_type
= g_file_info_get_content_type(fi
);
5808 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5809 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5810 g_strcmp0(mime_type
, "image/png") == 0 ||
5811 g_strcmp0(mime_type
, "image/gif") == 0 ||
5812 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5820 set_favicon_from_file(struct tab
*t
, char *file
)
5824 if (t
== NULL
|| file
== NULL
)
5827 if (g_str_has_prefix(file
, "file://"))
5828 file
+= strlen("file://");
5829 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5831 if (!stat(file
, &sb
)) {
5832 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5833 /* corrupt icon so trash it */
5834 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5837 /* no need to set icon to default here */
5841 xt_icon_from_file(t
, file
);
5845 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5848 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5849 struct tab
*tt
= NULL
, *t
= NULL
;
5852 * find the webview instead of passing in the tab as it could have been
5853 * deleted from underneath us.
5855 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5864 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5865 __func__
, t
->tab_id
, status
);
5868 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5870 t
->icon_download
= NULL
;
5873 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5876 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5879 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5881 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5882 __func__
, t
->tab_id
);
5883 t
->icon_download
= NULL
;
5886 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5889 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5890 __func__
, t
->icon_dest_uri
);
5891 set_favicon_from_file(t
, t
->icon_dest_uri
);
5892 /* these will be freed post callback */
5893 t
->icon_request
= NULL
;
5894 t
->icon_download
= NULL
;
5902 abort_favicon_download(struct tab
*t
)
5904 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5906 if (!WEBKIT_CHECK_VERSION(1, 4, 0)) {
5907 if (t
->icon_download
) {
5908 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5909 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5910 webkit_download_cancel(t
->icon_download
);
5911 t
->icon_download
= NULL
;
5916 xt_icon_from_name(t
, "text-html");
5920 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5923 gchar
*name_hash
, file
[PATH_MAX
];
5926 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
5928 if (uri
== NULL
|| t
== NULL
)
5931 if (WEBKIT_CHECK_VERSION(1, 4, 0)) {
5932 /* take icon from WebKitIconDatabase */
5933 pb
= webkit_web_view_get_icon_pixbuf(wv
);
5935 xt_icon_from_pixbuf(t
, pb
);
5938 xt_icon_from_name(t
, "text-html");
5940 } else if (WEBKIT_CHECK_VERSION(1, 1, 18)) {
5941 /* download icon to cache dir */
5942 if (t
->icon_request
) {
5943 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5947 /* check to see if we got the icon in cache */
5948 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5949 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5952 if (!stat(file
, &sb
)) {
5953 if (sb
.st_size
> 0) {
5954 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5956 set_favicon_from_file(t
, file
);
5960 /* corrupt icon so trash it */
5961 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5966 /* create download for icon */
5967 t
->icon_request
= webkit_network_request_new(uri
);
5968 if (t
->icon_request
== NULL
) {
5969 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5974 t
->icon_download
= webkit_download_new(t
->icon_request
);
5975 if (t
->icon_download
== NULL
)
5978 /* we have to free icon_dest_uri later */
5979 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5980 webkit_download_set_destination_uri(t
->icon_download
,
5983 if (webkit_download_get_status(t
->icon_download
) ==
5984 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5985 g_object_unref(t
->icon_request
);
5986 g_free(t
->icon_dest_uri
);
5987 t
->icon_request
= NULL
;
5988 t
->icon_dest_uri
= NULL
;
5992 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5993 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5995 webkit_download_start(t
->icon_download
);
6000 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6002 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
6003 struct history
*h
, find
;
6004 const gchar
*s_loading
;
6007 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6008 webkit_web_view_get_load_status(wview
), get_uri(t
) ? get_uri(t
) : "NOTHING");
6011 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6015 switch (webkit_web_view_get_load_status(wview
)) {
6016 case WEBKIT_LOAD_PROVISIONAL
:
6018 abort_favicon_download(t
);
6019 #if GTK_CHECK_VERSION(2, 20, 0)
6020 gtk_widget_show(t
->spinner
);
6021 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6023 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6025 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6027 /* take focus if we are visible */
6033 case WEBKIT_LOAD_COMMITTED
:
6035 if ((uri
= get_uri(t
)) != NULL
) {
6036 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6042 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6045 /* check if js white listing is enabled */
6046 if (enable_js_whitelist
) {
6048 check_and_set_js(uri
, t
);
6054 show_ca_status(t
, uri
);
6056 /* we know enough to autosave the session */
6057 if (session_autosave
) {
6063 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6067 case WEBKIT_LOAD_FINISHED
:
6073 if (!strncmp(uri
, "http://", strlen("http://")) ||
6074 !strncmp(uri
, "https://", strlen("https://")) ||
6075 !strncmp(uri
, "file://", strlen("file://"))) {
6077 h
= RB_FIND(history_list
, &hl
, &find
);
6079 title
= webkit_web_view_get_title(wview
);
6080 set
= title
? title
: uri
;
6081 h
= g_malloc(sizeof *h
);
6082 h
->uri
= g_strdup(uri
);
6083 h
->title
= g_strdup(set
);
6084 RB_INSERT(history_list
, &hl
, h
);
6085 completion_add_uri(h
->uri
);
6086 update_history_tabs(NULL
);
6090 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6091 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6092 case WEBKIT_LOAD_FAILED
:
6095 #if GTK_CHECK_VERSION(2, 20, 0)
6096 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6097 gtk_widget_hide(t
->spinner
);
6099 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
6100 if (s_loading
&& !strcmp(s_loading
, "Loading"))
6101 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6103 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6108 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6110 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6111 webkit_web_view_can_go_back(wview
));
6113 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6114 webkit_web_view_can_go_forward(wview
));
6118 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6120 const gchar
*set
= NULL
, *title
= NULL
;
6122 title
= webkit_web_view_get_title(wview
);
6123 set
= title
? title
: get_uri(t
);
6125 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
6126 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), set
);
6127 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
6129 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6130 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), "(untitled)");
6131 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6136 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6138 run_script(t
, JS_HINTING
);
6142 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6144 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6145 progress
== 100 ? 0 : (double)progress
/ 100);
6146 if (show_url
== 0) {
6147 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
6148 progress
== 100 ? 0 : (double)progress
/ 100);
6153 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6154 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6155 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6158 WebKitWebNavigationReason reason
;
6159 struct domain
*d
= NULL
;
6162 show_oops(NULL
, "webview_npd_cb invalid parameters");
6166 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6168 webkit_network_request_get_uri(request
));
6170 uri
= (char *)webkit_network_request_get_uri(request
);
6172 /* if this is an xtp url, we don't load anything else */
6173 if (parse_xtp_url(t
, uri
))
6176 if (t
->ctrl_click
) {
6178 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6179 webkit_web_policy_decision_ignore(pd
);
6180 return (TRUE
); /* we made the decission */
6184 * This is a little hairy but it comes down to this:
6185 * when we run in whitelist mode we have to assist the browser in
6186 * opening the URL that it would have opened in a new tab.
6188 reason
= webkit_web_navigation_action_get_reason(na
);
6189 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6190 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6191 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6192 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6194 webkit_web_policy_decision_use(pd
);
6195 return (TRUE
); /* we made the decision */
6202 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6205 struct domain
*d
= NULL
;
6207 WebKitWebView
*webview
= NULL
;
6209 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6210 webkit_web_view_get_uri(wv
));
6213 /* open in current tab */
6215 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6216 uri
= webkit_web_view_get_uri(wv
);
6217 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6220 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6222 } else if (enable_scripts
== 1) {
6223 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6231 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6234 struct domain
*d
= NULL
;
6236 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6238 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6239 uri
= webkit_web_view_get_uri(wv
);
6240 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6244 } else if (enable_scripts
== 1)
6251 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6253 /* we can not eat the event without throwing gtk off so defer it */
6255 /* catch middle click */
6256 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6261 /* catch ctrl click */
6262 if (e
->type
== GDK_BUTTON_RELEASE
&&
6263 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6268 return (XT_CB_PASSTHROUGH
);
6272 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6274 struct mime_type
*m
;
6276 m
= find_mime_type(mime_type
);
6284 show_oops(t
, "can't fork mime handler");
6293 execlp(m
->mt_action
, m
->mt_action
,
6294 webkit_network_request_get_uri(request
), (void *)NULL
);
6303 get_mime_type(char *file
)
6305 const char *mime_type
;
6309 if (g_str_has_prefix(file
, "file://"))
6310 file
+= strlen("file://");
6312 gf
= g_file_new_for_path(file
);
6313 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6315 mime_type
= g_file_info_get_content_type(fi
);
6323 run_download_mimehandler(char *mime_type
, char *file
)
6325 struct mime_type
*m
;
6327 m
= find_mime_type(mime_type
);
6333 show_oops(NULL
, "can't fork download mime handler");
6343 if (g_str_has_prefix(file
, "file://"))
6344 file
+= strlen("file://");
6345 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6354 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6357 WebKitDownloadStatus status
;
6358 const gchar
*file
= NULL
, *mime
= NULL
;
6360 if (download
== NULL
)
6362 status
= webkit_download_get_status(download
);
6363 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6366 file
= webkit_download_get_destination_uri(download
);
6369 mime
= get_mime_type((char *)file
);
6373 run_download_mimehandler((char *)mime
, (char *)file
);
6377 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6378 WebKitNetworkRequest
*request
, char *mime_type
,
6379 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6382 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6386 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6387 t
->tab_id
, mime_type
);
6389 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6390 webkit_web_policy_decision_ignore(decision
);
6395 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6396 webkit_web_policy_decision_download(decision
);
6404 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6408 const gchar
*suggested_name
;
6409 gchar
*filename
= NULL
;
6411 struct download
*download_entry
;
6414 if (wk_download
== NULL
|| t
== NULL
) {
6415 show_oops(NULL
, "%s invalid parameters", __func__
);
6419 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6420 if (suggested_name
== NULL
)
6421 return (FALSE
); /* abort download */
6432 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6434 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6435 filename
: suggested_name
);
6437 } while (!stat(uri
+ strlen("file://"), &sb
));
6439 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6440 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6442 webkit_download_set_destination_uri(wk_download
, uri
);
6444 if (webkit_download_get_status(wk_download
) ==
6445 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6446 show_oops(t
, "%s: download failed to start", __func__
);
6448 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6450 /* connect "download first" mime handler */
6451 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6452 G_CALLBACK(download_status_changed_cb
), NULL
);
6454 download_entry
= g_malloc(sizeof(struct download
));
6455 download_entry
->download
= wk_download
;
6456 download_entry
->tab
= t
;
6457 download_entry
->id
= next_download_id
++;
6458 RB_INSERT(download_list
, &downloads
, download_entry
);
6459 /* get from history */
6460 g_object_ref(wk_download
);
6461 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6462 show_oops(t
, "Download of '%s' started...",
6463 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6472 /* sync other download manager tabs */
6473 update_download_tabs(NULL
);
6476 * NOTE: never redirect/render the current tab before this
6477 * function returns. This will cause the download to never start.
6479 return (ret
); /* start download */
6483 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6485 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6488 show_oops(NULL
, "webview_hover_cb");
6493 set_status(t
, uri
, XT_STATUS_LINK
);
6496 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6501 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6503 struct key_binding
*k
;
6505 TAILQ_FOREACH(k
, &kbl
, entry
)
6506 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6508 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6509 return (cmd_execute(t
, k
->cmd
));
6510 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6511 return (cmd_execute(t
, k
->cmd
));
6515 return (XT_CB_PASSTHROUGH
);
6519 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6521 char s
[2], buf
[128];
6522 const char *errstr
= NULL
;
6525 /* don't use w directly; use t->whatever instead */
6528 show_oops(NULL
, "wv_keypress_after_cb");
6529 return (XT_CB_PASSTHROUGH
);
6532 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6533 e
->keyval
, e
->state
, t
);
6537 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6539 return (XT_CB_HANDLED
);
6543 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6544 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6546 /* we have a string */
6548 /* we have a number */
6549 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6557 /* XXX unfuck this */
6558 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6559 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6560 /* last input was numerical */
6562 l
= strlen(t
->hint_num
);
6569 t
->hint_num
[l
] = '\0';
6573 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6574 /* last input was alphanumerical */
6576 l
= strlen(t
->hint_buf
);
6583 t
->hint_buf
[l
] = '\0';
6593 /* numerical input */
6594 if (CLEAN(e
->state
) == 0 &&
6595 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6596 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6597 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6598 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6601 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6603 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6606 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6608 t
->hint_mode
= XT_HINT_NUMERICAL
;
6612 /* empty the counter buffer */
6613 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6614 return (XT_CB_HANDLED
);
6617 /* alphanumerical input */
6619 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6620 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6621 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6622 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6623 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6624 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6625 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6628 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6631 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6633 t
->hint_mode
= XT_HINT_ALPHANUM
;
6636 /* empty the counter buffer */
6637 bzero(t
->hint_num
, sizeof t
->hint_num
);
6638 return (XT_CB_HANDLED
);
6641 return (XT_CB_HANDLED
);
6644 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6645 if (CLEAN(e
->state
) == 0 && isdigit(s
[0])) {
6646 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
6650 return (handle_keypress(t
, e
, 0));
6654 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6658 /* Hide buffers, if they are visible, with escape. */
6659 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
6660 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6661 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6663 return (XT_CB_HANDLED
);
6666 return (XT_CB_PASSTHROUGH
);
6670 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6672 const gchar
*c
= gtk_entry_get_text(w
);
6676 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6677 e
->keyval
, e
->state
, t
);
6680 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
6681 return (XT_CB_PASSTHROUGH
);
6684 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6685 e
->keyval
, e
->state
, t
);
6689 if (strlen(c
) == 1) {
6690 webkit_web_view_unmark_text_matches(t
->wv
);
6696 else if (c
[0] == '?')
6702 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6704 /* not found, mark red */
6705 gdk_color_parse(XT_COLOR_RED
, &color
);
6706 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6707 /* unmark and remove selection */
6708 webkit_web_view_unmark_text_matches(t
->wv
);
6709 /* my kingdom for a way to unselect text in webview */
6711 /* found, highlight all */
6712 webkit_web_view_unmark_text_matches(t
->wv
);
6713 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6714 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6715 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6716 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6719 return (XT_CB_PASSTHROUGH
);
6723 match_uri(const gchar
*uri
, const gchar
*key
) {
6726 gboolean match
= FALSE
;
6730 if (!strncmp(key
, uri
, len
))
6733 voffset
= strstr(uri
, "/") + 2;
6734 if (!strncmp(key
, voffset
, len
))
6736 else if (g_str_has_prefix(voffset
, "www.")) {
6737 voffset
= voffset
+ strlen("www.");
6738 if (!strncmp(key
, voffset
, len
))
6747 cmd_getlist(int id
, char *key
)
6752 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
6753 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6754 if (match_uri(h
->uri
, key
)) {
6755 cmd_status
.list
[c
] = (char *)h
->uri
;
6764 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6766 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6767 if(cmds
[i
].level
< dep
)
6769 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6770 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6778 cmd_getnext(int dir
)
6780 cmd_status
.index
+= dir
;
6782 if (cmd_status
.index
< 0)
6783 cmd_status
.index
= cmd_status
.len
- 1;
6784 else if (cmd_status
.index
>= cmd_status
.len
)
6785 cmd_status
.index
= 0;
6787 return cmd_status
.list
[cmd_status
.index
];
6791 cmd_tokenize(char *s
, char *tokens
[])
6795 size_t len
= strlen(s
);
6796 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6798 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6808 cmd_complete(struct tab
*t
, char *str
, int dir
)
6810 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6811 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1, matchcount
= 0;
6812 char *tok
, *match
, *s
= g_strdup(str
);
6814 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6817 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
6820 for (i
= 0; isdigit(s
[i
]); i
++)
6823 for (; isspace(s
[i
]); i
++)
6828 levels
= cmd_tokenize(s
, tokens
);
6830 for (i
= 0; i
< levels
- 1; i
++) {
6833 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6834 if (cmds
[j
].level
< dep
)
6836 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, strlen(tok
))) {
6839 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
6846 if (matchcount
== 1) {
6847 strlcat(res
, tok
, sizeof res
);
6848 strlcat(res
, " ", sizeof res
);
6858 if (cmd_status
.index
== -1)
6859 cmd_getlist(parent
, tokens
[i
]);
6861 if (cmd_status
.len
> 0) {
6862 match
= cmd_getnext(dir
);
6863 strlcat(res
, match
, sizeof res
);
6864 gtk_entry_set_text(w
, res
);
6865 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6872 cmd_execute(struct tab
*t
, char *str
)
6874 struct cmd
*cmd
= NULL
;
6875 char *tok
, *last
, *s
= g_strdup(str
), *sc
, prefixstr
[4];
6876 int j
, len
, c
= 0, dep
= 0, matchcount
= 0, prefix
= -1;
6877 struct karg arg
= {0, NULL
, -1};
6878 int rv
= XT_CB_PASSTHROUGH
;
6883 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
6889 while (isspace(s
[0]))
6892 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
6893 prefix
= atoi(prefixstr
);
6897 for (tok
= strtok_r(s
, " ", &last
); tok
;
6898 tok
= strtok_r(NULL
, " ", &last
)) {
6900 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6901 if (cmds
[j
].level
< dep
)
6903 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1: strlen(tok
);
6904 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, len
)) {
6908 if (len
== strlen(cmds
[j
].cmd
)) {
6914 if (matchcount
== 1) {
6919 show_oops(t
, "Invalid command: %s", str
);
6928 else if (cmd_prefix
> 0)
6931 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
6932 show_oops(t
, "No prefix allowed: %s", str
);
6936 arg
.s
= last
? g_strdup(last
) : g_strdup("");
6937 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
6938 arg
.p
= atoi(arg
.s
);
6941 show_oops(t
, "Zero count");
6943 show_oops(t
, "Trailing characters");
6948 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
6964 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6967 show_oops(NULL
, "entry_key_cb invalid parameters");
6968 return (XT_CB_PASSTHROUGH
);
6971 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6972 e
->keyval
, e
->state
, t
);
6976 if (e
->keyval
== GDK_Escape
) {
6977 /* don't use focus_webview(t) because we want to type :cmds */
6978 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6981 return (handle_keypress(t
, e
, 1));
6985 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6987 int rv
= XT_CB_HANDLED
;
6988 const gchar
*c
= gtk_entry_get_text(w
);
6991 show_oops(NULL
, "cmd_keypress_cb parameters");
6992 return (XT_CB_PASSTHROUGH
);
6995 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6996 e
->keyval
, e
->state
, t
);
7000 e
->keyval
= GDK_Escape
;
7001 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7002 e
->keyval
= GDK_Escape
;
7004 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
7005 cmd_status
.index
= -1;
7007 switch (e
->keyval
) {
7010 cmd_complete(t
, (char *)&c
[1], 1);
7012 case GDK_ISO_Left_Tab
:
7014 cmd_complete(t
, (char *)&c
[1], -1);
7018 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7026 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7027 webkit_web_view_unmark_text_matches(t
->wv
);
7031 rv
= XT_CB_PASSTHROUGH
;
7037 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7040 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7041 return (XT_CB_PASSTHROUGH
);
7043 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7048 if (show_url
== 0 || t
->focus_wv
)
7051 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7053 return (XT_CB_PASSTHROUGH
);
7057 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7060 const gchar
*c
= gtk_entry_get_text(entry
);
7063 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7067 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7074 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7080 if (c
[0] == '/' || c
[0] == '?') {
7081 if (t
->search_text
) {
7082 g_free(t
->search_text
);
7083 t
->search_text
= NULL
;
7086 t
->search_text
= g_strdup(s
);
7088 g_free(global_search
);
7089 global_search
= g_strdup(s
);
7090 t
->search_forward
= c
[0] == '/';
7102 backward_cb(GtkWidget
*w
, struct tab
*t
)
7107 show_oops(NULL
, "backward_cb invalid parameters");
7111 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7118 forward_cb(GtkWidget
*w
, struct tab
*t
)
7123 show_oops(NULL
, "forward_cb invalid parameters");
7127 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7129 a
.i
= XT_NAV_FORWARD
;
7134 home_cb(GtkWidget
*w
, struct tab
*t
)
7137 show_oops(NULL
, "home_cb invalid parameters");
7141 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7147 stop_cb(GtkWidget
*w
, struct tab
*t
)
7149 WebKitWebFrame
*frame
;
7152 show_oops(NULL
, "stop_cb invalid parameters");
7156 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7158 frame
= webkit_web_view_get_main_frame(t
->wv
);
7159 if (frame
== NULL
) {
7160 show_oops(t
, "stop_cb: no frame");
7164 webkit_web_frame_stop_loading(frame
);
7165 abort_favicon_download(t
);
7169 setup_webkit(struct tab
*t
)
7171 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7172 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7173 FALSE
, (char *)NULL
);
7175 warnx("webkit does not have \"enable-dns-prefetching\" property");
7176 g_object_set(G_OBJECT(t
->settings
),
7177 "user-agent", t
->user_agent
, (char *)NULL
);
7178 g_object_set(G_OBJECT(t
->settings
),
7179 "enable-scripts", enable_scripts
, (char *)NULL
);
7180 g_object_set(G_OBJECT(t
->settings
),
7181 "enable-plugins", enable_plugins
, (char *)NULL
);
7182 g_object_set(G_OBJECT(t
->settings
),
7183 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
7184 g_object_set(G_OBJECT(t
->settings
),
7185 "enable-html5-database", FALSE
, (char *)NULL
);
7186 g_object_set(G_OBJECT(t
->settings
),
7187 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7188 g_object_set(G_OBJECT(t
->settings
),
7189 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7190 g_object_set(G_OBJECT(t
->settings
),
7191 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7192 g_object_set(G_OBJECT(t
->wv
),
7193 "full-content-zoom", TRUE
, (char *)NULL
);
7194 adjustfont_webkit(t
, XT_FONT_SET
);
7196 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7200 create_browser(struct tab
*t
)
7206 show_oops(NULL
, "create_browser invalid parameters");
7210 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7211 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7212 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7213 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7215 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7216 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7217 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7219 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7220 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7223 t
->settings
= webkit_web_settings_new();
7225 if (user_agent
== NULL
) {
7226 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7228 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7231 t
->user_agent
= g_strdup(user_agent
);
7233 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7245 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7246 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7247 gtk_widget_set_name(w
, "xxxterm");
7248 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7249 g_signal_connect(G_OBJECT(w
), "delete_event",
7250 G_CALLBACK (gtk_main_quit
), NULL
);
7256 create_kiosk_toolbar(struct tab
*t
)
7258 GtkWidget
*toolbar
= NULL
, *b
;
7260 b
= gtk_hbox_new(FALSE
, 0);
7262 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7264 /* backward button */
7265 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7266 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7267 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7268 G_CALLBACK(backward_cb
), t
);
7269 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7271 /* forward button */
7272 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7273 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7274 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7275 G_CALLBACK(forward_cb
), t
);
7276 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7279 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7280 gtk_widget_set_sensitive(t
->gohome
, true);
7281 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7282 G_CALLBACK(home_cb
), t
);
7283 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7285 /* create widgets but don't use them */
7286 t
->uri_entry
= gtk_entry_new();
7287 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7288 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7289 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7295 create_toolbar(struct tab
*t
)
7297 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7299 b
= gtk_hbox_new(FALSE
, 0);
7301 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7304 /* backward button */
7305 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7306 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7307 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7308 G_CALLBACK(backward_cb
), t
);
7309 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7311 /* forward button */
7312 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7313 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7314 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7315 G_CALLBACK(forward_cb
), t
);
7316 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7320 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7321 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7322 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7323 G_CALLBACK(stop_cb
), t
);
7324 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7328 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7329 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7330 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7331 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7332 G_CALLBACK(js_toggle_cb
), t
);
7333 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7336 t
->uri_entry
= gtk_entry_new();
7337 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7338 G_CALLBACK(activate_uri_entry_cb
), t
);
7339 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7340 G_CALLBACK(entry_key_cb
), t
);
7342 eb1
= gtk_hbox_new(FALSE
, 0);
7343 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7344 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7345 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7348 if (fancy_bar
&& search_string
) {
7350 t
->search_entry
= gtk_entry_new();
7351 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7352 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7353 G_CALLBACK(activate_search_entry_cb
), t
);
7354 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7355 G_CALLBACK(entry_key_cb
), t
);
7356 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7357 eb2
= gtk_hbox_new(FALSE
, 0);
7358 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7359 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7361 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7367 create_buffers(struct tab
*t
)
7369 GtkCellRenderer
*renderer
;
7372 view
= gtk_tree_view_new();
7374 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7376 renderer
= gtk_cell_renderer_text_new();
7377 gtk_tree_view_insert_column_with_attributes
7378 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7380 renderer
= gtk_cell_renderer_text_new();
7381 gtk_tree_view_insert_column_with_attributes
7382 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
, NULL
);
7384 gtk_tree_view_set_model
7385 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7391 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7392 GtkTreeViewColumn
*col
, struct tab
*t
)
7397 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7399 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
, path
)) {
7401 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7402 set_current_tab(id
- 1);
7408 /* after tab reordering/creation/removal */
7416 TAILQ_FOREACH(t
, &tabs
, entry
) {
7417 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7418 if (t
->tab_id
> maxid
)
7421 gtk_widget_show(t
->tab_elems
.sep
);
7424 curid
= gtk_notebook_get_current_page(notebook
);
7425 TAILQ_FOREACH(t
, &tabs
, entry
) {
7426 if (t
->tab_id
== maxid
) {
7427 gtk_widget_hide(t
->tab_elems
.sep
);
7433 /* after active tab change */
7435 recolor_compact_tabs(void)
7441 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7442 TAILQ_FOREACH(t
, &tabs
, entry
)
7443 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7445 curid
= gtk_notebook_get_current_page(notebook
);
7446 TAILQ_FOREACH(t
, &tabs
, entry
)
7447 if (t
->tab_id
== curid
) {
7448 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
7449 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7455 set_current_tab(int page_num
)
7457 gtk_notebook_set_current_page(notebook
, page_num
);
7458 recolor_compact_tabs();
7462 undo_close_tab_save(struct tab
*t
)
7466 struct undo
*u1
, *u2
;
7468 WebKitWebHistoryItem
*item
;
7470 if ((uri
= get_uri(t
)) == NULL
)
7473 u1
= g_malloc0(sizeof(struct undo
));
7474 u1
->uri
= g_strdup(uri
);
7476 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7478 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7479 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7482 /* forward history */
7483 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7487 u1
->history
= g_list_prepend(u1
->history
,
7488 webkit_web_history_item_copy(item
));
7489 items
= g_list_next(items
);
7494 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7495 u1
->history
= g_list_prepend(u1
->history
,
7496 webkit_web_history_item_copy(item
));
7500 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7504 u1
->history
= g_list_prepend(u1
->history
,
7505 webkit_web_history_item_copy(item
));
7506 items
= g_list_next(items
);
7509 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7511 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7512 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7513 TAILQ_REMOVE(&undos
, u2
, entry
);
7515 g_list_free(u2
->history
);
7524 delete_tab(struct tab
*t
)
7528 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7533 TAILQ_REMOVE(&tabs
, t
, entry
);
7535 /* Halt all webkit activity. */
7536 abort_favicon_download(t
);
7537 webkit_web_view_stop_loading(t
->wv
);
7539 /* Save the tab, so we can undo the close. */
7540 undo_close_tab_save(t
);
7542 if (browser_mode
== XT_BM_KIOSK
) {
7543 gtk_widget_destroy(t
->uri_entry
);
7544 gtk_widget_destroy(t
->stop
);
7545 gtk_widget_destroy(t
->js_toggle
);
7548 gtk_widget_destroy(t
->tab_elems
.eventbox
);
7549 gtk_widget_destroy(t
->vbox
);
7551 g_free(t
->user_agent
);
7552 g_free(t
->stylesheet
);
7555 if (TAILQ_EMPTY(&tabs
)) {
7556 if (browser_mode
== XT_BM_KIOSK
)
7557 create_new_tab(home
, NULL
, 1, -1);
7559 create_new_tab(NULL
, NULL
, 1, -1);
7562 /* recreate session */
7563 if (session_autosave
) {
7569 recolor_compact_tabs();
7573 adjustfont_webkit(struct tab
*t
, int adjust
)
7578 show_oops(NULL
, "adjustfont_webkit invalid parameters");
7582 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7583 if (adjust
== XT_FONT_SET
) {
7584 t
->font_size
= default_font_size
;
7585 zoom
= default_zoom_level
;
7586 t
->font_size
+= adjust
;
7587 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7588 t
->font_size
, (char *)NULL
);
7589 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7590 &t
->font_size
, (char *)NULL
);
7592 t
->font_size
+= adjust
;
7593 zoom
+= adjust
/25.0;
7598 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7599 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7603 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
7605 struct tab
*t
= (struct tab
*) data
;
7607 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
7609 switch (event
->button
) {
7611 set_current_tab(t
->tab_id
);
7622 page_reordered_cb(GtkWidget
*nb
, GtkWidget
*eventbox
, guint pn
, gpointer data
)
7624 struct tab
*t
= (struct tab
*) data
;
7626 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
7629 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, t
->tab_id
);
7635 append_tab(struct tab
*t
)
7640 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7641 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7645 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
7650 WebKitWebHistoryItem
*item
;
7654 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7656 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7657 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7661 t
= g_malloc0(sizeof *t
);
7663 if (title
== NULL
) {
7664 title
= "(untitled)";
7668 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7670 /* label + button for tab */
7671 b
= gtk_hbox_new(FALSE
, 0);
7674 #if GTK_CHECK_VERSION(2, 20, 0)
7675 t
->spinner
= gtk_spinner_new();
7677 t
->label
= gtk_label_new(title
);
7678 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7679 gtk_widget_set_size_request(t
->label
, 100, 0);
7680 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
7681 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
7682 gtk_widget_set_size_request(b
, 130, 0);
7684 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7685 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7686 #if GTK_CHECK_VERSION(2, 20, 0)
7687 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7691 if (browser_mode
== XT_BM_KIOSK
)
7692 t
->toolbar
= create_kiosk_toolbar(t
);
7694 t
->toolbar
= create_toolbar(t
);
7696 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7699 t
->browser_win
= create_browser(t
);
7700 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7702 /* oops message for user feedback */
7703 t
->oops
= gtk_entry_new();
7704 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7705 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7706 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7707 gdk_color_parse(XT_COLOR_RED
, &color
);
7708 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7709 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7712 t
->cmd
= gtk_entry_new();
7713 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7714 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7715 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7716 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
7719 t
->statusbar
= gtk_entry_new();
7720 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
7721 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
7722 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
7723 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7724 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7725 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7726 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7727 gtk_widget_modify_font(GTK_WIDGET(t
->statusbar
), statusbar_font
);
7728 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
7731 t
->buffers
= create_buffers(t
);
7732 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
7734 /* xtp meaning is normal by default */
7735 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7737 /* set empty favicon */
7738 xt_icon_from_name(t
, "text-html");
7740 /* and show it all */
7741 gtk_widget_show_all(b
);
7742 gtk_widget_show_all(t
->vbox
);
7744 /* compact tab bar */
7745 t
->tab_elems
.label
= gtk_label_new(title
);
7746 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
7747 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
7748 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
7750 t
->tab_elems
.eventbox
= gtk_event_box_new();
7751 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
7752 t
->tab_elems
.sep
= gtk_vseparator_new();
7754 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
7755 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
7756 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7757 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7758 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
7759 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
7761 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
, TRUE
, 0);
7762 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
, FALSE
, 0);
7763 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
), t
->tab_elems
.box
);
7765 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
, TRUE
, 0);
7766 gtk_widget_show_all(t
->tab_elems
.eventbox
);
7768 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7771 id
= position
>= 0 ? position
: gtk_notebook_get_current_page(notebook
) + 1;
7772 if (id
> gtk_notebook_get_n_pages(notebook
))
7775 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7776 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
7777 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, id
);
7782 #if GTK_CHECK_VERSION(2, 20, 0)
7783 /* turn spinner off if we are a new tab without uri */
7785 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7786 gtk_widget_hide(t
->spinner
);
7789 /* make notebook tabs reorderable */
7790 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7792 /* compact tabs clickable */
7793 g_signal_connect(GTK_OBJECT(t
->tab_elems
.eventbox
),
7794 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
7796 g_signal_connect(GTK_OBJECT(notebook
),
7797 "page_reordered", G_CALLBACK(page_reordered_cb
), t
);
7799 g_object_connect(G_OBJECT(t
->cmd
),
7800 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7801 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7802 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7803 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7806 /* reuse wv_button_cb to hide oops */
7807 g_object_connect(G_OBJECT(t
->oops
),
7808 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7811 g_signal_connect(t
->buffers
,
7812 "row-activated", G_CALLBACK(row_activated_cb
), t
);
7813 g_object_connect(G_OBJECT(t
->buffers
),
7814 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
7816 g_object_connect(G_OBJECT(t
->wv
),
7817 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7818 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7819 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7820 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7821 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7822 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7823 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7824 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7825 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7826 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7827 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7828 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7829 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7830 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7832 g_signal_connect(t
->wv
,
7833 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7834 g_signal_connect(t
->wv
,
7835 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7837 /* hijack the unused keys as if we were the browser */
7838 g_object_connect(G_OBJECT(t
->toolbar
),
7839 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7842 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7843 G_CALLBACK(tab_close_cb
), t
);
7849 url_set_visibility();
7850 statusbar_set_visibility();
7853 set_current_tab(t
->tab_id
);
7854 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7859 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7863 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7868 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7869 /* restore the tab's history */
7870 if (u
&& u
->history
) {
7874 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7875 items
= g_list_next(items
);
7878 item
= g_list_nth_data(u
->history
, u
->back
);
7880 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7883 g_list_free(u
->history
);
7885 webkit_web_back_forward_list_clear(t
->bfl
);
7888 recolor_compact_tabs();
7893 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7899 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7901 if (gtk_notebook_get_current_page(notebook
) == -1)
7904 TAILQ_FOREACH(t
, &tabs
, entry
) {
7905 if (t
->tab_id
== pn
) {
7906 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7909 uri
= webkit_web_view_get_title(t
->wv
);
7912 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7918 /* can't use focus_webview here */
7919 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7926 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7933 menuitem_response(struct tab
*t
)
7935 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7939 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7941 GtkWidget
*menu
, *menu_items
;
7942 GdkEventButton
*bevent
;
7946 if (event
->type
== GDK_BUTTON_PRESS
) {
7947 bevent
= (GdkEventButton
*) event
;
7948 menu
= gtk_menu_new();
7950 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7951 if ((uri
= get_uri(ti
)) == NULL
)
7952 /* XXX make sure there is something to print */
7953 /* XXX add gui pages in here to look purdy */
7955 menu_items
= gtk_menu_item_new_with_label(uri
);
7956 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
7957 gtk_widget_show(menu_items
);
7959 g_signal_connect_swapped((menu_items
),
7960 "activate", G_CALLBACK(menuitem_response
),
7964 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7965 bevent
->button
, bevent
->time
);
7967 /* unref object so it'll free itself when popped down */
7968 #if !GTK_CHECK_VERSION(3, 0, 0)
7969 /* XXX does not need unref with gtk+3? */
7970 g_object_ref_sink(menu
);
7971 g_object_unref(menu
);
7974 return (TRUE
/* eat event */);
7977 return (FALSE
/* propagate */);
7981 icon_size_map(int icon_size
)
7983 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7984 icon_size
> GTK_ICON_SIZE_DIALOG
)
7985 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7991 create_button(char *name
, char *stockid
, int size
)
7993 GtkWidget
*button
, *image
;
7997 rcstring
= g_strdup_printf(
7998 "style \"%s-style\"\n"
8000 " GtkWidget::focus-padding = 0\n"
8001 " GtkWidget::focus-line-width = 0\n"
8005 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8006 gtk_rc_parse_string(rcstring
);
8008 button
= gtk_button_new();
8009 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8010 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8012 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8013 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8014 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8015 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8016 gtk_widget_set_name(button
, name
);
8017 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8023 button_set_stockid(GtkWidget
*button
, char *stockid
)
8027 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8028 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8029 gtk_button_set_image(GTK_BUTTON(button
), image
);
8033 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8035 GtkClipboard
*clipboard
;
8036 gchar
*p
= NULL
, *s
= NULL
;
8039 * This code is very aggressive!
8040 * It basically ensures that the primary and regular clipboard are
8041 * always set the same. This obviously messes with standard X protocol
8042 * but those clowns should have come up with something better.
8045 /* XXX make this setting? */
8046 clipboard
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
8047 p
= gtk_clipboard_wait_for_text(primary
);
8049 DNPRINTF(XT_D_CLIP
, "primary cleaned\n");
8050 p
= gtk_clipboard_wait_for_text(clipboard
);
8052 gtk_clipboard_set_text(primary
, p
, -1);
8054 DNPRINTF(XT_D_CLIP
, "primary got selection\n");
8055 s
= gtk_clipboard_wait_for_text(clipboard
);
8058 * if s and p are the same the string was set by
8059 * clipb_clipboard_cb so do nothing in that case
8060 * to prevent endless loop
8065 gtk_clipboard_set_text(clipboard
, p
, -1);
8075 clipb_clipboard_cb(GtkClipboard
*clipboard
, GdkEvent
*event
, gpointer notused
)
8077 GtkClipboard
*primary
;
8078 gchar
*p
= NULL
, *s
= NULL
;
8080 DNPRINTF(XT_D_CLIP
, "clipboard got content\n");
8082 primary
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8083 p
= gtk_clipboard_wait_for_text(clipboard
);
8085 s
= gtk_clipboard_wait_for_text(primary
);
8088 * if s and p are the same the string was set by
8089 * clipb_primary_cb so do nothing in that case
8090 * to prevent endless loop and deselection of text
8095 gtk_clipboard_set_text(primary
, p
, -1);
8110 char file
[PATH_MAX
];
8113 vbox
= gtk_vbox_new(FALSE
, 0);
8114 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8115 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8116 #if !GTK_CHECK_VERSION(3, 0, 0)
8117 /* XXX seems to be needed with gtk+2 */
8118 gtk_notebook_set_tab_hborder(notebook
, 0);
8119 gtk_notebook_set_tab_vborder(notebook
, 0);
8121 gtk_notebook_set_scrollable(notebook
, TRUE
);
8122 gtk_notebook_set_show_border(notebook
, FALSE
);
8123 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8125 abtn
= gtk_button_new();
8126 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8127 gtk_widget_set_size_request(arrow
, -1, -1);
8128 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8129 gtk_widget_set_size_request(abtn
, -1, 20);
8131 #if GTK_CHECK_VERSION(2, 20, 0)
8132 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8134 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8136 /* compact tab bar */
8137 tab_bar
= gtk_hbox_new(TRUE
, 0);
8139 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8140 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8141 gtk_widget_set_size_request(vbox
, -1, -1);
8143 g_object_connect(G_OBJECT(notebook
),
8144 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8146 g_object_connect(G_OBJECT(notebook
),
8147 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
), NULL
,
8149 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8150 G_CALLBACK(arrow_cb
), NULL
);
8152 main_window
= create_window();
8153 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8154 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
8157 for (i
= 0; i
< LENGTH(icons
); i
++) {
8158 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8159 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8160 l
= g_list_append(l
, pb
);
8162 gtk_window_set_default_icon_list(l
);
8165 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8166 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8167 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
)),
8168 "owner-change", G_CALLBACK(clipb_clipboard_cb
), NULL
);
8170 gtk_widget_show_all(abtn
);
8171 gtk_widget_show_all(main_window
);
8172 notebook_tab_set_visibility();
8176 set_hook(void **hook
, char *name
)
8179 errx(1, "set_hook");
8181 if (*hook
== NULL
) {
8182 *hook
= dlsym(RTLD_NEXT
, name
);
8184 errx(1, "can't hook %s", name
);
8188 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8190 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8192 g_return_val_if_fail(cookie1
, FALSE
);
8193 g_return_val_if_fail(cookie2
, FALSE
);
8195 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8196 !strcmp (cookie1
->value
, cookie2
->value
) &&
8197 !strcmp (cookie1
->path
, cookie2
->path
) &&
8198 !strcmp (cookie1
->domain
, cookie2
->domain
));
8202 transfer_cookies(void)
8205 SoupCookie
*sc
, *pc
;
8207 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8209 for (;cf
; cf
= cf
->next
) {
8211 sc
= soup_cookie_copy(pc
);
8212 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8215 soup_cookies_free(cf
);
8219 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8224 print_cookie("soup_cookie_jar_delete_cookie", c
);
8226 if (cookies_enabled
== 0)
8229 if (jar
== NULL
|| c
== NULL
)
8232 /* find and remove from persistent jar */
8233 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8235 for (;cf
; cf
= cf
->next
) {
8237 if (soup_cookie_equal(ci
, c
)) {
8238 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8243 soup_cookies_free(cf
);
8245 /* delete from session jar */
8246 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8250 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8252 struct domain
*d
= NULL
;
8256 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8257 jar
, p_cookiejar
, s_cookiejar
);
8259 if (cookies_enabled
== 0)
8262 /* see if we are up and running */
8263 if (p_cookiejar
== NULL
) {
8264 _soup_cookie_jar_add_cookie(jar
, cookie
);
8267 /* disallow p_cookiejar adds, shouldn't happen */
8268 if (jar
== p_cookiejar
)
8272 if (jar
== NULL
|| cookie
== NULL
)
8275 if (enable_cookie_whitelist
&&
8276 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8278 DNPRINTF(XT_D_COOKIE
,
8279 "soup_cookie_jar_add_cookie: reject %s\n",
8281 if (save_rejected_cookies
) {
8282 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8283 show_oops(NULL
, "can't open reject cookie file");
8286 fseek(r_cookie_f
, 0, SEEK_END
);
8287 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8288 cookie
->http_only
? "#HttpOnly_" : "",
8290 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8292 cookie
->secure
? "TRUE" : "FALSE",
8294 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8301 if (!allow_volatile_cookies
)
8305 if (cookie
->expires
== NULL
&& session_timeout
) {
8306 soup_cookie_set_expires(cookie
,
8307 soup_date_new_from_now(session_timeout
));
8308 print_cookie("modified add cookie", cookie
);
8311 /* see if we are white listed for persistence */
8312 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8313 /* add to persistent jar */
8314 c
= soup_cookie_copy(cookie
);
8315 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8316 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8319 /* add to session jar */
8320 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8321 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8327 char file
[PATH_MAX
];
8329 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8330 "soup_cookie_jar_add_cookie");
8331 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8332 "soup_cookie_jar_delete_cookie");
8334 if (cookies_enabled
== 0)
8338 * the following code is intricate due to overriding several libsoup
8340 * do not alter order of these operations.
8343 /* rejected cookies */
8344 if (save_rejected_cookies
)
8345 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
, XT_REJECT_FILE
);
8347 /* persistent cookies */
8348 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8349 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8351 /* session cookies */
8352 s_cookiejar
= soup_cookie_jar_new();
8353 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8354 cookie_policy
, (void *)NULL
);
8357 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8361 setup_proxy(char *uri
)
8364 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8365 soup_uri_free(proxy_uri
);
8369 if (http_proxy
!= uri
) {
8376 http_proxy
= g_strdup(uri
);
8377 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8378 proxy_uri
= soup_uri_new(http_proxy
);
8379 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
8384 send_cmd_to_socket(char *cmd
)
8387 struct sockaddr_un sa
;
8389 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8390 warnx("%s: socket", __func__
);
8394 sa
.sun_family
= AF_UNIX
;
8395 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8396 work_dir
, XT_SOCKET_FILE
);
8399 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8400 warnx("%s: connect", __func__
);
8404 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
8405 warnx("%s: send", __func__
);
8416 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
8419 char str
[XT_MAX_URL_LENGTH
];
8420 socklen_t t
= sizeof(struct sockaddr_un
);
8421 struct sockaddr_un sa
;
8426 gint fd
= g_io_channel_unix_get_fd(source
);
8428 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
8433 if (getpeereid(s
, &uid
, &gid
) == -1) {
8437 if (uid
!= getuid() || gid
!= getgid()) {
8438 warnx("unauthorized user");
8444 warnx("not a valid user");
8448 n
= recv(s
, str
, sizeof(str
), 0);
8452 tt
= TAILQ_LAST(&tabs
, tab_list
);
8453 cmd_execute(tt
, str
);
8461 struct sockaddr_un sa
;
8463 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8464 warn("is_running: socket");
8468 sa
.sun_family
= AF_UNIX
;
8469 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8470 work_dir
, XT_SOCKET_FILE
);
8473 /* connect to see if there is a listener */
8474 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
8475 rv
= 0; /* not running */
8477 rv
= 1; /* already running */
8488 struct sockaddr_un sa
;
8490 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8491 warn("build_socket: socket");
8495 sa
.sun_family
= AF_UNIX
;
8496 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8497 work_dir
, XT_SOCKET_FILE
);
8500 /* connect to see if there is a listener */
8501 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8502 /* no listener so we will */
8503 unlink(sa
.sun_path
);
8505 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8506 warn("build_socket: bind");
8510 if (listen(s
, 1) == -1) {
8511 warn("build_socket: listen");
8524 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8525 GtkTreeIter
*iter
, struct tab
*t
)
8529 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8537 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8538 GtkTreeIter
*iter
, struct tab
*t
)
8542 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8543 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
8544 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
8551 completion_add_uri(const gchar
*uri
)
8555 /* add uri to list_store */
8556 gtk_list_store_append(completion_model
, &iter
);
8557 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8561 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8562 GtkTreeIter
*iter
, gpointer user_data
)
8565 gboolean match
= FALSE
;
8567 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8573 match
= match_uri(value
, key
);
8580 completion_add(struct tab
*t
)
8582 /* enable completion for tab */
8583 t
->completion
= gtk_entry_completion_new();
8584 gtk_entry_completion_set_text_column(t
->completion
, 0);
8585 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8586 gtk_entry_completion_set_model(t
->completion
,
8587 GTK_TREE_MODEL(completion_model
));
8588 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8590 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8591 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
8592 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8593 G_CALLBACK(completion_select_cb
), t
);
8594 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
8595 G_CALLBACK(completion_hover_cb
), t
);
8603 if (stat(dir
, &sb
)) {
8604 if (mkdir(dir
, S_IRWXU
) == -1)
8605 err(1, "mkdir %s", dir
);
8607 err(1, "stat %s", dir
);
8609 if (S_ISDIR(sb
.st_mode
) == 0)
8610 errx(1, "%s not a dir", dir
);
8611 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8612 warnx("fixing invalid permissions on %s", dir
);
8613 if (chmod(dir
, S_IRWXU
) == -1)
8614 err(1, "chmod %s", dir
);
8622 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8628 main(int argc
, char *argv
[])
8631 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8632 char conf
[PATH_MAX
] = { '\0' };
8633 char file
[PATH_MAX
];
8634 char *env_proxy
= NULL
;
8637 struct sigaction sact
;
8638 GIOChannel
*channel
;
8643 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8645 /* fiddle with ulimits */
8646 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8649 /* just use them all */
8650 rlp
.rlim_cur
= rlp
.rlim_max
;
8651 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8653 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8655 else if (rlp
.rlim_cur
<= 256)
8656 warnx("%s requires at least 256 file descriptors",
8660 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8669 errx(0 , "Version: %s", version
);
8672 strlcpy(conf
, optarg
, sizeof(conf
));
8675 strlcpy(named_session
, optarg
, sizeof(named_session
));
8696 RB_INIT(&downloads
);
8700 TAILQ_INIT(&aliases
);
8706 gnutls_global_init();
8708 /* generate session keys for xtp pages */
8709 generate_xtp_session_key(&dl_session_key
);
8710 generate_xtp_session_key(&hl_session_key
);
8711 generate_xtp_session_key(&cl_session_key
);
8712 generate_xtp_session_key(&fl_session_key
);
8715 gtk_init(&argc
, &argv
);
8716 if (!g_thread_supported())
8717 g_thread_init(NULL
);
8720 bzero(&sact
, sizeof(sact
));
8721 sigemptyset(&sact
.sa_mask
);
8722 sact
.sa_handler
= sigchild
;
8723 sact
.sa_flags
= SA_NOCLDSTOP
;
8724 sigaction(SIGCHLD
, &sact
, NULL
);
8726 /* set download dir */
8727 pwd
= getpwuid(getuid());
8729 errx(1, "invalid user %d", getuid());
8730 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8732 /* set default string settings */
8733 home
= g_strdup("https://www.cyphertite.com");
8734 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8735 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8736 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
8737 cmd_font_name
= g_strdup("monospace normal 9");
8738 statusbar_font_name
= g_strdup("monospace normal 9");
8741 /* read config file */
8742 if (strlen(conf
) == 0)
8743 snprintf(conf
, sizeof conf
, "%s/.%s",
8744 pwd
->pw_dir
, XT_CONF_FILE
);
8745 config_parse(conf
, 0);
8748 cmd_font
= pango_font_description_from_string(cmd_font_name
);
8749 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
8751 /* working directory */
8752 if (strlen(work_dir
) == 0)
8753 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8754 pwd
->pw_dir
, XT_DIR
);
8757 /* icon cache dir */
8758 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8762 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8766 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8767 work_dir
, XT_SESSIONS_DIR
);
8768 xxx_dir(sessions_dir
);
8770 /* runtime settings that can override config file */
8771 if (runtime_settings
[0] != '\0')
8772 config_parse(runtime_settings
, 1);
8775 if (!strcmp(download_dir
, pwd
->pw_dir
))
8776 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8777 xxx_dir(download_dir
);
8779 /* favorites file */
8780 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8781 if (stat(file
, &sb
)) {
8782 warnx("favorites file doesn't exist, creating it");
8783 if ((f
= fopen(file
, "w")) == NULL
)
8784 err(1, "favorites");
8789 session
= webkit_get_default_session();
8794 if (stat(ssl_ca_file
, &sb
)) {
8795 warnx("no CA file: %s", ssl_ca_file
);
8796 g_free(ssl_ca_file
);
8799 g_object_set(session
,
8800 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8801 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8806 env_proxy
= getenv("http_proxy");
8808 setup_proxy(env_proxy
);
8810 setup_proxy(http_proxy
);
8813 send_cmd_to_socket(argv
[0]);
8817 /* set some connection parameters */
8818 /* XXX webkit 1.4.X overwrites these values! */
8819 /* https://bugs.webkit.org/show_bug.cgi?id=64355 */
8820 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8821 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8824 /* see if there is already an xxxterm running */
8825 if (single_instance
&& is_running()) {
8827 warnx("already running");
8833 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8834 send_cmd_to_socket(cmd
);
8844 /* uri completion */
8845 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8848 buffers_store
= gtk_list_store_new
8849 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
8853 notebook_tab_set_visibility();
8855 if (save_global_history
)
8856 restore_global_history();
8858 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8859 restore_saved_tabs();
8861 a
.s
= named_session
;
8862 a
.i
= XT_SES_DONOTHING
;
8863 open_tabs(NULL
, &a
);
8867 create_new_tab(argv
[0], NULL
, focus
, -1);
8874 if (TAILQ_EMPTY(&tabs
))
8875 create_new_tab(home
, NULL
, 1, -1);
8878 if ((s
= build_socket()) != -1) {
8879 channel
= g_io_channel_unix_new(s
);
8880 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
8885 gnutls_global_deinit();