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
46 #include <sys/types.h>
48 #if defined(__linux__)
49 #include "linux/util.h"
50 #include "linux/tree.h"
51 #elif defined(__FreeBSD__)
53 #include "freebsd/util.h"
59 #include <sys/queue.h>
60 #include <sys/resource.h>
61 #include <sys/socket.h>
67 #include <gdk/gdkkeysyms.h>
69 #if GTK_CHECK_VERSION(3,0,0)
70 /* we still use GDK_* instead of GDK_KEY_* */
71 #include <gdk/gdkkeysyms-compat.h>
74 #include <webkit/webkit.h>
75 #include <libsoup/soup.h>
76 #include <gnutls/gnutls.h>
77 #include <JavaScriptCore/JavaScript.h>
78 #include <gnutls/x509.h>
80 #include "javascript.h"
83 javascript.h borrowed from vimprobable2 under the following license:
85 Copyright (c) 2009 Leon Winter
86 Copyright (c) 2009 Hannes Schueller
87 Copyright (c) 2009 Matto Fransen
89 Permission is hereby granted, free of charge, to any person obtaining a copy
90 of this software and associated documentation files (the "Software"), to deal
91 in the Software without restriction, including without limitation the rights
92 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
93 copies of the Software, and to permit persons to whom the Software is
94 furnished to do so, subject to the following conditions:
96 The above copyright notice and this permission notice shall be included in
97 all copies or substantial portions of the Software.
99 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
100 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
101 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
102 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
103 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
104 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
108 static char *version
= "$xxxterm$";
110 /* hooked functions */
111 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
112 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
117 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
118 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
119 #define XT_D_MOVE 0x0001
120 #define XT_D_KEY 0x0002
121 #define XT_D_TAB 0x0004
122 #define XT_D_URL 0x0008
123 #define XT_D_CMD 0x0010
124 #define XT_D_NAV 0x0020
125 #define XT_D_DOWNLOAD 0x0040
126 #define XT_D_CONFIG 0x0080
127 #define XT_D_JS 0x0100
128 #define XT_D_FAVORITE 0x0200
129 #define XT_D_PRINTING 0x0400
130 #define XT_D_COOKIE 0x0800
131 #define XT_D_KEYBINDING 0x1000
132 #define XT_D_CLIP 0x2000
133 #define XT_D_BUFFERCMD 0x4000
134 u_int32_t swm_debug
= 0
152 #define DPRINTF(x...)
153 #define DNPRINTF(n,x...)
156 #define LENGTH(x) (sizeof x / sizeof x[0])
157 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
158 ~(GDK_BUTTON1_MASK) & \
159 ~(GDK_BUTTON2_MASK) & \
160 ~(GDK_BUTTON3_MASK) & \
161 ~(GDK_BUTTON4_MASK) & \
164 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
175 TAILQ_ENTRY(tab
) entry
;
177 GtkWidget
*tab_content
;
186 GtkWidget
*uri_entry
;
187 GtkWidget
*search_entry
;
189 GtkWidget
*browser_win
;
190 GtkWidget
*statusbar_box
;
192 GtkWidget
*statusbar
;
193 GtkWidget
*buffercmd
;
204 GtkWidget
*js_toggle
;
205 GtkEntryCompletion
*completion
;
209 WebKitWebHistoryItem
*item
;
210 WebKitWebBackForwardList
*bfl
;
213 WebKitNetworkRequest
*icon_request
;
214 WebKitDownload
*icon_download
;
215 gchar
*icon_dest_uri
;
217 /* adjustments for browser */
220 GtkAdjustment
*adjust_h
;
221 GtkAdjustment
*adjust_v
;
227 int xtp_meaning
; /* identifies dls/favorites */
233 #define XT_HINT_NONE (0)
234 #define XT_HINT_NUMERICAL (1)
235 #define XT_HINT_ALPHANUM (2)
239 /* custom stylesheet */
248 WebKitWebSettings
*settings
;
252 double mark
[XT_NOMARKS
];
254 TAILQ_HEAD(tab_list
, tab
);
257 RB_ENTRY(history
) entry
;
261 RB_HEAD(history_list
, history
);
264 RB_ENTRY(download
) entry
;
266 WebKitDownload
*download
;
269 RB_HEAD(download_list
, download
);
272 RB_ENTRY(domain
) entry
;
274 int handy
; /* app use */
276 RB_HEAD(domain_list
, domain
);
279 TAILQ_ENTRY(undo
) entry
;
282 int back
; /* Keeps track of how many back
283 * history items there are. */
285 TAILQ_HEAD(undo_tailq
, undo
);
287 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
288 int next_download_id
= 1;
297 #define XT_NAME ("XXXTerm")
298 #define XT_DIR (".xxxterm")
299 #define XT_CACHE_DIR ("cache")
300 #define XT_CERT_DIR ("certs/")
301 #define XT_SESSIONS_DIR ("sessions/")
302 #define XT_CONF_FILE ("xxxterm.conf")
303 #define XT_FAVS_FILE ("favorites")
304 #define XT_QMARKS_FILE ("quickmarks")
305 #define XT_SAVED_TABS_FILE ("main_session")
306 #define XT_RESTART_TABS_FILE ("restart_tabs")
307 #define XT_SOCKET_FILE ("socket")
308 #define XT_HISTORY_FILE ("history")
309 #define XT_REJECT_FILE ("rejected.txt")
310 #define XT_COOKIE_FILE ("cookies.txt")
311 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
312 #define XT_CB_HANDLED (TRUE)
313 #define XT_CB_PASSTHROUGH (FALSE)
314 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
315 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
316 #define XT_DLMAN_REFRESH "10"
317 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
318 "td{overflow: hidden;" \
319 " padding: 2px 2px 2px 2px;" \
320 " border: 1px solid black;" \
321 " vertical-align:top;" \
322 " word-wrap: break-word}\n" \
323 "tr:hover{background: #ffff99}\n" \
324 "th{background-color: #cccccc;" \
325 " border: 1px solid black}\n" \
326 "table{width: 100%%;" \
327 " border: 1px black solid;" \
328 " border-collapse:collapse}\n" \
330 "border: 1px solid black;" \
333 ".progress-inner{float: left;" \
335 " background: green}\n" \
336 ".dlstatus{font-size: small;" \
337 " text-align: center}\n" \
339 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
340 #define XT_MAX_UNDO_CLOSE_TAB (32)
341 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
342 #define XT_PRINT_EXTRA_MARGIN 10
343 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
346 #define XT_COLOR_RED "#cc0000"
347 #define XT_COLOR_YELLOW "#ffff66"
348 #define XT_COLOR_BLUE "lightblue"
349 #define XT_COLOR_GREEN "#99ff66"
350 #define XT_COLOR_WHITE "white"
351 #define XT_COLOR_BLACK "black"
353 #define XT_COLOR_CT_BACKGROUND "#000000"
354 #define XT_COLOR_CT_INACTIVE "#dddddd"
355 #define XT_COLOR_CT_ACTIVE "#bbbb00"
356 #define XT_COLOR_CT_SEPARATOR "#555555"
358 #define XT_COLOR_SB_SEPARATOR "#555555"
360 #define XT_PROTO_DELIM "://"
363 * xxxterm "protocol" (xtp)
364 * We use this for managing stuff like downloads and favorites. They
365 * make magical HTML pages in memory which have xxxt:// links in order
366 * to communicate with xxxterm's internals. These links take the format:
367 * xxxt://class/session_key/action/arg
369 * Don't begin xtp class/actions as 0. atoi returns that on error.
371 * Typically we have not put addition of items in this framework, as
372 * adding items is either done via an ex-command or via a keybinding instead.
375 #define XT_XTP_STR "xxxt://"
377 /* XTP classes (xxxt://<class>) */
378 #define XT_XTP_INVALID 0 /* invalid */
379 #define XT_XTP_DL 1 /* downloads */
380 #define XT_XTP_HL 2 /* history */
381 #define XT_XTP_CL 3 /* cookies */
382 #define XT_XTP_FL 4 /* favorites */
384 /* XTP download actions */
385 #define XT_XTP_DL_LIST 1
386 #define XT_XTP_DL_CANCEL 2
387 #define XT_XTP_DL_REMOVE 3
389 /* XTP history actions */
390 #define XT_XTP_HL_LIST 1
391 #define XT_XTP_HL_REMOVE 2
393 /* XTP cookie actions */
394 #define XT_XTP_CL_LIST 1
395 #define XT_XTP_CL_REMOVE 2
397 /* XTP cookie actions */
398 #define XT_XTP_FL_LIST 1
399 #define XT_XTP_FL_REMOVE 2
402 #define XT_MOVE_INVALID (0)
403 #define XT_MOVE_DOWN (1)
404 #define XT_MOVE_UP (2)
405 #define XT_MOVE_BOTTOM (3)
406 #define XT_MOVE_TOP (4)
407 #define XT_MOVE_PAGEDOWN (5)
408 #define XT_MOVE_PAGEUP (6)
409 #define XT_MOVE_HALFDOWN (7)
410 #define XT_MOVE_HALFUP (8)
411 #define XT_MOVE_LEFT (9)
412 #define XT_MOVE_FARLEFT (10)
413 #define XT_MOVE_RIGHT (11)
414 #define XT_MOVE_FARRIGHT (12)
415 #define XT_MOVE_PERCENT (13)
417 #define XT_QMARK_SET (0)
418 #define XT_QMARK_OPEN (1)
419 #define XT_QMARK_TAB (2)
421 #define XT_MARK_SET (0)
422 #define XT_MARK_GOTO (1)
424 #define XT_TAB_LAST (-4)
425 #define XT_TAB_FIRST (-3)
426 #define XT_TAB_PREV (-2)
427 #define XT_TAB_NEXT (-1)
428 #define XT_TAB_INVALID (0)
429 #define XT_TAB_NEW (1)
430 #define XT_TAB_DELETE (2)
431 #define XT_TAB_DELQUIT (3)
432 #define XT_TAB_OPEN (4)
433 #define XT_TAB_UNDO_CLOSE (5)
434 #define XT_TAB_SHOW (6)
435 #define XT_TAB_HIDE (7)
436 #define XT_TAB_NEXTSTYLE (8)
438 #define XT_NAV_INVALID (0)
439 #define XT_NAV_BACK (1)
440 #define XT_NAV_FORWARD (2)
441 #define XT_NAV_RELOAD (3)
442 #define XT_NAV_RELOAD_CACHE (4)
444 #define XT_FOCUS_INVALID (0)
445 #define XT_FOCUS_URI (1)
446 #define XT_FOCUS_SEARCH (2)
448 #define XT_SEARCH_INVALID (0)
449 #define XT_SEARCH_NEXT (1)
450 #define XT_SEARCH_PREV (2)
452 #define XT_PASTE_CURRENT_TAB (0)
453 #define XT_PASTE_NEW_TAB (1)
455 #define XT_ZOOM_IN (-1)
456 #define XT_ZOOM_OUT (-2)
457 #define XT_ZOOM_NORMAL (100)
459 #define XT_URL_SHOW (1)
460 #define XT_URL_HIDE (2)
462 #define XT_WL_TOGGLE (1<<0)
463 #define XT_WL_ENABLE (1<<1)
464 #define XT_WL_DISABLE (1<<2)
465 #define XT_WL_FQDN (1<<3) /* default */
466 #define XT_WL_TOPLEVEL (1<<4)
467 #define XT_WL_PERSISTENT (1<<5)
468 #define XT_WL_SESSION (1<<6)
469 #define XT_WL_RELOAD (1<<7)
471 #define XT_SHOW (1<<7)
472 #define XT_DELETE (1<<8)
473 #define XT_SAVE (1<<9)
474 #define XT_OPEN (1<<10)
476 #define XT_CMD_OPEN (0)
477 #define XT_CMD_OPEN_CURRENT (1)
478 #define XT_CMD_TABNEW (2)
479 #define XT_CMD_TABNEW_CURRENT (3)
481 #define XT_STATUS_NOTHING (0)
482 #define XT_STATUS_LINK (1)
483 #define XT_STATUS_URI (2)
484 #define XT_STATUS_LOADING (3)
486 #define XT_SES_DONOTHING (0)
487 #define XT_SES_CLOSETABS (1)
489 #define XT_BM_NORMAL (0)
490 #define XT_BM_WHITELIST (1)
491 #define XT_BM_KIOSK (2)
493 #define XT_PREFIX (1<<0)
494 #define XT_USERARG (1<<1)
495 #define XT_URLARG (1<<2)
496 #define XT_INTARG (1<<3)
498 #define XT_TABS_NORMAL 0
499 #define XT_TABS_COMPACT 1
507 TAILQ_ENTRY(mime_type
) entry
;
509 TAILQ_HEAD(mime_type_list
, mime_type
);
515 TAILQ_ENTRY(alias
) entry
;
517 TAILQ_HEAD(alias_list
, alias
);
519 /* settings that require restart */
520 int tabless
= 0; /* allow only 1 tab */
521 int enable_socket
= 0;
522 int single_instance
= 0; /* only allow one xxxterm to run */
523 int fancy_bar
= 1; /* fancy toolbar */
524 int browser_mode
= XT_BM_NORMAL
;
525 int enable_localstorage
= 0;
526 char *statusbar_elems
= NULL
;
528 /* runtime settings */
529 int show_tabs
= 1; /* show tabs on notebook */
530 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
531 int show_url
= 1; /* show url toolbar on notebook */
532 int show_statusbar
= 0; /* vimperator style status bar */
533 int ctrl_click_focus
= 0; /* ctrl click gets focus */
534 int cookies_enabled
= 1; /* enable cookies */
535 int read_only_cookies
= 0; /* enable to not write cookies */
536 int enable_scripts
= 1;
537 int enable_plugins
= 0;
538 gfloat default_zoom_level
= 1.0;
539 char default_script
[PATH_MAX
];
540 int window_height
= 768;
541 int window_width
= 1024;
542 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
543 int refresh_interval
= 10; /* download refresh interval */
544 int enable_cookie_whitelist
= 0;
545 int enable_js_whitelist
= 0;
546 int session_timeout
= 3600; /* cookie session timeout */
547 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
548 char *ssl_ca_file
= NULL
;
549 char *resource_dir
= NULL
;
550 gboolean ssl_strict_certs
= FALSE
;
551 int append_next
= 1; /* append tab after current tab */
553 char *search_string
= NULL
;
554 char *http_proxy
= NULL
;
555 char download_dir
[PATH_MAX
];
556 char runtime_settings
[PATH_MAX
]; /* override of settings */
557 int allow_volatile_cookies
= 0;
558 int save_global_history
= 0; /* save global history to disk */
559 char *user_agent
= NULL
;
560 int save_rejected_cookies
= 0;
561 int session_autosave
= 0;
562 int guess_search
= 0;
563 int dns_prefetch
= FALSE
;
564 gint max_connections
= 25;
565 gint max_host_connections
= 5;
566 gint enable_spell_checking
= 0;
567 char *spell_check_languages
= NULL
;
569 char *cmd_font_name
= NULL
;
570 char *oops_font_name
= NULL
;
571 char *statusbar_font_name
= NULL
;
572 char *tabbar_font_name
= NULL
;
573 PangoFontDescription
*cmd_font
;
574 PangoFontDescription
*oops_font
;
575 PangoFontDescription
*statusbar_font
;
576 PangoFontDescription
*tabbar_font
;
577 char *qmarks
[XT_NOMARKS
];
579 int btn_down
; /* M1 down in any wv */
583 int set_browser_mode(struct settings
*, char *);
584 int set_cookie_policy(struct settings
*, char *);
585 int set_download_dir(struct settings
*, char *);
586 int set_default_script(struct settings
*, char *);
587 int set_runtime_dir(struct settings
*, char *);
588 int set_tab_style(struct settings
*, char *);
589 int set_work_dir(struct settings
*, char *);
590 int add_alias(struct settings
*, char *);
591 int add_mime_type(struct settings
*, char *);
592 int add_cookie_wl(struct settings
*, char *);
593 int add_js_wl(struct settings
*, char *);
594 int add_kb(struct settings
*, char *);
595 void button_set_stockid(GtkWidget
*, char *);
596 GtkWidget
* create_button(char *, char *, int);
598 char *get_browser_mode(struct settings
*);
599 char *get_cookie_policy(struct settings
*);
600 char *get_download_dir(struct settings
*);
601 char *get_default_script(struct settings
*);
602 char *get_runtime_dir(struct settings
*);
603 char *get_tab_style(struct settings
*);
604 char *get_work_dir(struct settings
*);
606 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
607 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
608 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
609 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
610 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
612 void recalc_tabs(void);
613 void recolor_compact_tabs(void);
614 void set_current_tab(int page_num
);
615 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
616 void marks_clear(struct tab
*t
);
619 int (*set
)(struct settings
*, char *);
620 char *(*get
)(struct settings
*);
621 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
624 struct special s_browser_mode
= {
630 struct special s_cookie
= {
636 struct special s_alias
= {
642 struct special s_mime
= {
648 struct special s_js
= {
654 struct special s_kb
= {
660 struct special s_cookie_wl
= {
666 struct special s_default_script
= {
672 struct special s_download_dir
= {
678 struct special s_work_dir
= {
684 struct special s_tab_style
= {
693 #define XT_S_INVALID (0)
696 #define XT_S_FLOAT (3)
698 #define XT_SF_RESTART (1<<0)
699 #define XT_SF_RUNTIME (1<<1)
705 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
706 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
707 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
708 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
709 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
710 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
711 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
712 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
713 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
714 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
715 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
716 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
717 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
718 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
719 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
720 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
721 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
722 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
723 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
724 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
725 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
726 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
727 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
728 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
729 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
730 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
731 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
732 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
733 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
734 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
735 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
736 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
737 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
738 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
739 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
740 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
741 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
742 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
743 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
744 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
745 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
746 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
747 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
748 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
751 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
752 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
753 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
754 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
756 /* runtime settings */
757 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
758 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
759 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
760 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
761 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
764 int about(struct tab
*, struct karg
*);
765 int blank(struct tab
*, struct karg
*);
766 int ca_cmd(struct tab
*, struct karg
*);
767 int cookie_show_wl(struct tab
*, struct karg
*);
768 int js_show_wl(struct tab
*, struct karg
*);
769 int help(struct tab
*, struct karg
*);
770 int set(struct tab
*, struct karg
*);
771 int stats(struct tab
*, struct karg
*);
772 int marco(struct tab
*, struct karg
*);
773 const char * marco_message(int *);
774 int xtp_page_cl(struct tab
*, struct karg
*);
775 int xtp_page_dl(struct tab
*, struct karg
*);
776 int xtp_page_fl(struct tab
*, struct karg
*);
777 int xtp_page_hl(struct tab
*, struct karg
*);
778 void xt_icon_from_file(struct tab
*, char *);
779 const gchar
*get_uri(struct tab
*);
780 const gchar
*get_title(struct tab
*, bool);
782 #define XT_URI_ABOUT ("about:")
783 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
784 #define XT_URI_ABOUT_ABOUT ("about")
785 #define XT_URI_ABOUT_BLANK ("blank")
786 #define XT_URI_ABOUT_CERTS ("certs")
787 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
788 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
789 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
790 #define XT_URI_ABOUT_FAVORITES ("favorites")
791 #define XT_URI_ABOUT_HELP ("help")
792 #define XT_URI_ABOUT_HISTORY ("history")
793 #define XT_URI_ABOUT_JSWL ("jswl")
794 #define XT_URI_ABOUT_SET ("set")
795 #define XT_URI_ABOUT_STATS ("stats")
796 #define XT_URI_ABOUT_MARCO ("marco")
800 int (*func
)(struct tab
*, struct karg
*);
802 { XT_URI_ABOUT_ABOUT
, about
},
803 { XT_URI_ABOUT_BLANK
, blank
},
804 { XT_URI_ABOUT_CERTS
, ca_cmd
},
805 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
806 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
807 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
808 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
809 { XT_URI_ABOUT_HELP
, help
},
810 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
811 { XT_URI_ABOUT_JSWL
, js_show_wl
},
812 { XT_URI_ABOUT_SET
, set
},
813 { XT_URI_ABOUT_STATS
, stats
},
814 { XT_URI_ABOUT_MARCO
, marco
},
817 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
818 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
819 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
820 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
821 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
822 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
823 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
826 extern char *__progname
;
829 GtkWidget
*main_window
;
830 GtkNotebook
*notebook
;
832 GtkWidget
*arrow
, *abtn
;
833 struct tab_list tabs
;
834 struct history_list hl
;
835 struct download_list downloads
;
836 struct domain_list c_wl
;
837 struct domain_list js_wl
;
838 struct undo_tailq undos
;
839 struct keybinding_list kbl
;
841 int updating_dl_tabs
= 0;
842 int updating_hl_tabs
= 0;
843 int updating_cl_tabs
= 0;
844 int updating_fl_tabs
= 0;
846 uint64_t blocked_cookies
= 0;
847 char named_session
[PATH_MAX
];
848 int icon_size_map(int);
850 GtkListStore
*completion_model
;
851 void completion_add(struct tab
*);
852 void completion_add_uri(const gchar
*);
853 GtkListStore
*buffers_store
;
854 void xxx_dir(char *);
856 /* marks and quickmarks array storage.
857 * first a-z, then A-Z, then 0-9 */
864 if (i
>= 0 && i
<= 'z' - 'a')
868 if (i
>= 0 && i
<= 'Z' - 'A')
883 if (m
>= 'a' && m
<= 'z')
884 return ret
+ m
- 'a';
886 ret
+= 'z' - 'a' + 1;
887 if (m
>= 'A' && m
<= 'Z')
888 return ret
+ m
- 'A';
890 ret
+= 'Z' - 'A' + 1;
891 if (m
>= '0' && m
<= '9')
892 return ret
+ m
- '0';
901 int saved_errno
, status
;
906 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
910 if (errno
!= ECHILD
) {
912 clog_warn("sigchild: waitpid:");
918 if (WIFEXITED(status
)) {
919 if (WEXITSTATUS(status
) != 0) {
921 clog_warnx("sigchild: child exit status: %d",
922 WEXITSTATUS(status));
927 clog_warnx("sigchild: child is terminated abnormally");
936 is_g_object_setting(GObject
*o
, char *str
)
938 guint n_props
= 0, i
;
939 GParamSpec
**proplist
;
941 if (! G_IS_OBJECT(o
))
944 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
947 for (i
=0; i
< n_props
; i
++) {
948 if (! strcmp(proplist
[i
]->name
, str
))
955 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
959 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
961 "<title>%s</title>\n"
970 addstyles
? XT_PAGE_STYLE
: "",
979 * Display a web page from a HTML string in memory, rather than from a URL
982 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
987 /* we set this to indicate we want to manually do navaction */
989 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
991 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
993 /* set t->xtp_meaning */
994 for (i
= 0; i
< LENGTH(about_list
); i
++)
995 if (!strcmp(title
, about_list
[i
].name
)) {
1000 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1001 #if GTK_CHECK_VERSION(2, 20, 0)
1002 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1003 gtk_widget_hide(t
->spinner
);
1005 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1006 xt_icon_from_file(t
, file
);
1011 get_current_tab(void)
1015 TAILQ_FOREACH(t
, &tabs
, entry
) {
1016 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1020 warnx("%s: no current tab", __func__
);
1026 set_status(struct tab
*t
, gchar
*s
, int status
)
1034 case XT_STATUS_LOADING
:
1035 type
= g_strdup_printf("Loading: %s", s
);
1038 case XT_STATUS_LINK
:
1039 type
= g_strdup_printf("Link: %s", s
);
1041 t
->status
= g_strdup(gtk_entry_get_text(
1042 GTK_ENTRY(t
->sbe
.statusbar
)));
1046 type
= g_strdup_printf("%s", s
);
1048 t
->status
= g_strdup(type
);
1052 t
->status
= g_strdup(s
);
1054 case XT_STATUS_NOTHING
:
1059 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1065 hide_cmd(struct tab
*t
)
1067 gtk_widget_hide(t
->cmd
);
1071 show_cmd(struct tab
*t
)
1073 gtk_widget_hide(t
->oops
);
1074 gtk_widget_show(t
->cmd
);
1078 hide_buffers(struct tab
*t
)
1080 gtk_widget_hide(t
->buffers
);
1081 gtk_list_store_clear(buffers_store
);
1091 sort_tabs_by_page_num(struct tab
***stabs
)
1096 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1098 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1100 TAILQ_FOREACH(t
, &tabs
, entry
)
1101 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1107 buffers_make_list(void)
1110 const gchar
*title
= NULL
;
1112 struct tab
**stabs
= NULL
;
1114 num_tabs
= sort_tabs_by_page_num(&stabs
);
1116 for (i
= 0; i
< num_tabs
; i
++)
1118 gtk_list_store_append(buffers_store
, &iter
);
1119 title
= get_title(stabs
[i
], FALSE
);
1120 gtk_list_store_set(buffers_store
, &iter
,
1121 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1131 show_buffers(struct tab
*t
)
1133 buffers_make_list();
1134 gtk_widget_show(t
->buffers
);
1135 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1139 toggle_buffers(struct tab
*t
)
1141 if (gtk_widget_get_visible(t
->buffers
))
1148 buffers(struct tab
*t
, struct karg
*args
)
1156 hide_oops(struct tab
*t
)
1158 gtk_widget_hide(t
->oops
);
1162 show_oops(struct tab
*at
, const char *fmt
, ...)
1166 struct tab
*t
= NULL
;
1172 if ((t
= get_current_tab()) == NULL
)
1178 if (vasprintf(&msg
, fmt
, ap
) == -1)
1179 errx(1, "show_oops failed");
1182 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1183 gtk_widget_hide(t
->cmd
);
1184 gtk_widget_show(t
->oops
);
1188 get_as_string(struct settings
*s
)
1199 warnx("get_as_string skip %s\n", s
->name
);
1200 } else if (s
->type
== XT_S_INT
)
1201 r
= g_strdup_printf("%d", *s
->ival
);
1202 else if (s
->type
== XT_S_STR
)
1203 r
= g_strdup(*s
->sval
);
1204 else if (s
->type
== XT_S_FLOAT
)
1205 r
= g_strdup_printf("%f", *s
->fval
);
1207 r
= g_strdup_printf("INVALID TYPE");
1213 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1218 for (i
= 0; i
< LENGTH(rs
); i
++) {
1219 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1220 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1222 s
= get_as_string(&rs
[i
]);
1223 cb(&rs
[i
], s
, cb_args
);
1230 set_browser_mode(struct settings
*s
, char *val
)
1232 if (!strcmp(val
, "whitelist")) {
1233 browser_mode
= XT_BM_WHITELIST
;
1234 allow_volatile_cookies
= 0;
1235 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1236 cookies_enabled
= 1;
1237 enable_cookie_whitelist
= 1;
1238 read_only_cookies
= 0;
1239 save_rejected_cookies
= 0;
1240 session_timeout
= 3600;
1242 enable_js_whitelist
= 1;
1243 enable_localstorage
= 0;
1244 } else if (!strcmp(val
, "normal")) {
1245 browser_mode
= XT_BM_NORMAL
;
1246 allow_volatile_cookies
= 0;
1247 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1248 cookies_enabled
= 1;
1249 enable_cookie_whitelist
= 0;
1250 read_only_cookies
= 0;
1251 save_rejected_cookies
= 0;
1252 session_timeout
= 3600;
1254 enable_js_whitelist
= 0;
1255 enable_localstorage
= 1;
1256 } else if (!strcmp(val
, "kiosk")) {
1257 browser_mode
= XT_BM_KIOSK
;
1258 allow_volatile_cookies
= 0;
1259 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1260 cookies_enabled
= 1;
1261 enable_cookie_whitelist
= 0;
1262 read_only_cookies
= 0;
1263 save_rejected_cookies
= 0;
1264 session_timeout
= 3600;
1266 enable_js_whitelist
= 0;
1267 enable_localstorage
= 1;
1277 get_browser_mode(struct settings
*s
)
1281 if (browser_mode
== XT_BM_WHITELIST
)
1282 r
= g_strdup("whitelist");
1283 else if (browser_mode
== XT_BM_NORMAL
)
1284 r
= g_strdup("normal");
1285 else if (browser_mode
== XT_BM_KIOSK
)
1286 r
= g_strdup("kiosk");
1294 set_cookie_policy(struct settings
*s
, char *val
)
1296 if (!strcmp(val
, "no3rdparty"))
1297 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1298 else if (!strcmp(val
, "accept"))
1299 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1300 else if (!strcmp(val
, "reject"))
1301 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1309 get_cookie_policy(struct settings
*s
)
1313 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1314 r
= g_strdup("no3rdparty");
1315 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1316 r
= g_strdup("accept");
1317 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1318 r
= g_strdup("reject");
1326 get_default_script(struct settings
*s
)
1328 if (default_script
[0] == '\0')
1330 return (g_strdup(default_script
));
1334 set_default_script(struct settings
*s
, char *val
)
1337 snprintf(default_script
, sizeof default_script
, "%s/%s",
1338 pwd
->pw_dir
, &val
[1]);
1340 strlcpy(default_script
, val
, sizeof default_script
);
1346 get_download_dir(struct settings
*s
)
1348 if (download_dir
[0] == '\0')
1350 return (g_strdup(download_dir
));
1354 set_download_dir(struct settings
*s
, char *val
)
1357 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1358 pwd
->pw_dir
, &val
[1]);
1360 strlcpy(download_dir
, val
, sizeof download_dir
);
1367 * We use these to prevent people putting xxxt:// URLs on
1368 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1370 #define XT_XTP_SES_KEY_SZ 8
1371 #define XT_XTP_SES_KEY_HEX_FMT \
1372 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1373 char *dl_session_key
; /* downloads */
1374 char *hl_session_key
; /* history list */
1375 char *cl_session_key
; /* cookie list */
1376 char *fl_session_key
; /* favorites list */
1378 char work_dir
[PATH_MAX
];
1379 char certs_dir
[PATH_MAX
];
1380 char cache_dir
[PATH_MAX
];
1381 char sessions_dir
[PATH_MAX
];
1382 char cookie_file
[PATH_MAX
];
1383 SoupURI
*proxy_uri
= NULL
;
1384 SoupSession
*session
;
1385 SoupCookieJar
*s_cookiejar
;
1386 SoupCookieJar
*p_cookiejar
;
1387 char rc_fname
[PATH_MAX
];
1389 struct mime_type_list mtl
;
1390 struct alias_list aliases
;
1393 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1394 void delete_tab(struct tab
*);
1395 void setzoom_webkit(struct tab
*, int);
1396 int run_script(struct tab
*, char *);
1397 int download_rb_cmp(struct download
*, struct download
*);
1398 gboolean
cmd_execute(struct tab
*t
, char *str
);
1401 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1403 return (strcmp(h1
->uri
, h2
->uri
));
1405 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1408 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1410 return (strcmp(d1
->d
, d2
->d
));
1412 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1415 get_work_dir(struct settings
*s
)
1417 if (work_dir
[0] == '\0')
1419 return (g_strdup(work_dir
));
1423 set_work_dir(struct settings
*s
, char *val
)
1426 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1427 pwd
->pw_dir
, &val
[1]);
1429 strlcpy(work_dir
, val
, sizeof work_dir
);
1435 get_tab_style(struct settings
*s
)
1437 if (tab_style
== XT_TABS_NORMAL
)
1438 return (g_strdup("normal"));
1440 return (g_strdup("compact"));
1444 set_tab_style(struct settings
*s
, char *val
)
1446 if (!strcmp(val
, "normal"))
1447 tab_style
= XT_TABS_NORMAL
;
1448 else if (!strcmp(val
, "compact"))
1449 tab_style
= XT_TABS_COMPACT
;
1457 * generate a session key to secure xtp commands.
1458 * pass in a ptr to the key in question and it will
1459 * be modified in place.
1462 generate_xtp_session_key(char **key
)
1464 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1470 /* make a new one */
1471 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1472 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1473 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1474 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1476 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1480 * validate a xtp session key.
1484 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1486 if (strcmp(trusted
, untrusted
) != 0) {
1487 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1496 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1498 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1500 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1502 struct valid_url_types
{
1513 valid_url_type(char *url
)
1517 for (i
= 0; i
< LENGTH(vut
); i
++)
1518 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1525 print_cookie(char *msg
, SoupCookie
*c
)
1531 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1532 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1533 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1534 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1535 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1536 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1537 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1538 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1539 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1540 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1544 walk_alias(struct settings
*s
,
1545 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1550 if (s
== NULL
|| cb
== NULL
) {
1551 show_oops(NULL
, "walk_alias invalid parameters");
1555 TAILQ_FOREACH(a
, &aliases
, entry
) {
1556 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1557 cb(s
, str
, cb_args
);
1563 match_alias(char *url_in
)
1567 char *url_out
= NULL
, *search
, *enc_arg
;
1569 search
= g_strdup(url_in
);
1571 if (strsep(&arg
, " \t") == NULL
) {
1572 show_oops(NULL
, "match_alias: NULL URL");
1576 TAILQ_FOREACH(a
, &aliases
, entry
) {
1577 if (!strcmp(search
, a
->a_name
))
1582 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1585 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1586 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1589 url_out
= g_strdup_printf(a
->a_uri
, "");
1597 guess_url_type(char *url_in
)
1600 char *url_out
= NULL
, *enc_search
= NULL
;
1602 url_out
= match_alias(url_in
);
1603 if (url_out
!= NULL
)
1608 * If there is no dot nor slash in the string and it isn't a
1609 * path to a local file and doesn't resolves to an IP, assume
1610 * that the user wants to search for the string.
1613 if (strchr(url_in
, '.') == NULL
&&
1614 strchr(url_in
, '/') == NULL
&&
1615 stat(url_in
, &sb
) != 0 &&
1616 gethostbyname(url_in
) == NULL
) {
1618 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1619 url_out
= g_strdup_printf(search_string
, enc_search
);
1625 /* XXX not sure about this heuristic */
1626 if (stat(url_in
, &sb
) == 0)
1627 url_out
= g_strdup_printf("file://%s", url_in
);
1629 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1631 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1637 load_uri(struct tab
*t
, gchar
*uri
)
1640 gchar
*newuri
= NULL
;
1646 /* Strip leading spaces. */
1647 while (*uri
&& isspace(*uri
))
1650 if (strlen(uri
) == 0) {
1655 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1657 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1658 for (i
= 0; i
< LENGTH(about_list
); i
++)
1659 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1660 bzero(&args
, sizeof args
);
1661 about_list
[i
].func(t
, &args
);
1662 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1666 show_oops(t
, "invalid about page");
1670 if (valid_url_type(uri
)) {
1671 newuri
= guess_url_type(uri
);
1675 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1677 webkit_web_view_load_uri(t
->wv
, uri
);
1684 get_uri(struct tab
*t
)
1686 const gchar
*uri
= NULL
;
1688 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1690 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1691 uri
= webkit_web_view_get_uri(t
->wv
);
1693 /* use tmp_uri to make sure it is g_freed */
1696 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1697 about_list
[t
->xtp_meaning
].name
);
1704 get_title(struct tab
*t
, bool window
)
1706 const gchar
*set
= NULL
, *title
= NULL
;
1707 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1709 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1710 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1713 title
= webkit_web_view_get_title(t
->wv
);
1714 if ((set
= title
? title
: get_uri(t
)))
1718 set
= window
? XT_NAME
: "(untitled)";
1724 add_alias(struct settings
*s
, char *line
)
1727 struct alias
*a
= NULL
;
1729 if (s
== NULL
|| line
== NULL
) {
1730 show_oops(NULL
, "add_alias invalid parameters");
1735 a
= g_malloc(sizeof(*a
));
1737 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1738 show_oops(NULL
, "add_alias: incomplete alias definition");
1741 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1742 show_oops(NULL
, "add_alias: invalid alias definition");
1746 a
->a_name
= g_strdup(alias
);
1747 a
->a_uri
= g_strdup(l
);
1749 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1751 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1761 add_mime_type(struct settings
*s
, char *line
)
1765 struct mime_type
*m
= NULL
;
1766 int downloadfirst
= 0;
1768 /* XXX this could be smarter */
1770 if (line
== NULL
|| strlen(line
) == 0) {
1771 show_oops(NULL
, "add_mime_type invalid parameters");
1780 m
= g_malloc(sizeof(*m
));
1782 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1783 show_oops(NULL
, "add_mime_type: invalid mime_type");
1786 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1787 mime_type
[strlen(mime_type
) - 1] = '\0';
1792 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1793 show_oops(NULL
, "add_mime_type: invalid mime_type");
1797 m
->mt_type
= g_strdup(mime_type
);
1798 m
->mt_action
= g_strdup(l
);
1799 m
->mt_download
= downloadfirst
;
1801 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1802 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1804 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1814 find_mime_type(char *mime_type
)
1816 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1818 TAILQ_FOREACH(m
, &mtl
, entry
) {
1819 if (m
->mt_default
&&
1820 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1823 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1836 walk_mime_type(struct settings
*s
,
1837 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1839 struct mime_type
*m
;
1842 if (s
== NULL
|| cb
== NULL
) {
1843 show_oops(NULL
, "walk_mime_type invalid parameters");
1847 TAILQ_FOREACH(m
, &mtl
, entry
) {
1848 str
= g_strdup_printf("%s%s --> %s",
1850 m
->mt_default
? "*" : "",
1852 cb(s
, str
, cb_args
);
1858 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1863 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1866 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1868 /* treat *.moo.com the same as .moo.com */
1869 if (str
[0] == '*' && str
[1] == '.')
1871 else if (str
[0] == '.')
1876 d
= g_malloc(sizeof *d
);
1878 d
->d
= g_strdup_printf(".%s", str
);
1880 d
->d
= g_strdup(str
);
1883 if (RB_INSERT(domain_list
, wl
, d
))
1886 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1897 add_cookie_wl(struct settings
*s
, char *entry
)
1899 wl_add(entry
, &c_wl
, 1);
1904 walk_cookie_wl(struct settings
*s
,
1905 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1909 if (s
== NULL
|| cb
== NULL
) {
1910 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1914 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1915 cb(s
, d
->d
, cb_args
);
1919 walk_js_wl(struct settings
*s
,
1920 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1924 if (s
== NULL
|| cb
== NULL
) {
1925 show_oops(NULL
, "walk_js_wl invalid parameters");
1929 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1930 cb(s
, d
->d
, cb_args
);
1934 add_js_wl(struct settings
*s
, char *entry
)
1936 wl_add(entry
, &js_wl
, 1 /* persistent */);
1941 wl_find(const gchar
*search
, struct domain_list
*wl
)
1944 struct domain
*d
= NULL
, dfind
;
1947 if (search
== NULL
|| wl
== NULL
)
1949 if (strlen(search
) < 2)
1952 if (search
[0] != '.')
1953 s
= g_strdup_printf(".%s", search
);
1955 s
= g_strdup(search
);
1957 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1960 d
= RB_FIND(domain_list
, wl
, &dfind
);
1974 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1980 if (s
== NULL
|| wl
== NULL
)
1983 if (!strncmp(s
, "http://", strlen("http://")))
1984 s
= &s
[strlen("http://")];
1985 else if (!strncmp(s
, "https://", strlen("https://")))
1986 s
= &s
[strlen("https://")];
1991 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1992 /* chop string at first slash */
1993 if (s
[i
] == '/' || s
[i
] == '\0') {
1996 r
= wl_find(ss
, wl
);
2005 settings_add(char *var
, char *val
)
2012 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2013 if (strcmp(var
, rs
[i
].name
))
2017 if (rs
[i
].s
->set(&rs
[i
], val
))
2018 errx(1, "invalid value for %s: %s", var
, val
);
2022 switch (rs
[i
].type
) {
2031 errx(1, "invalid sval for %s",
2045 errx(1, "invalid type for %s", var
);
2054 config_parse(char *filename
, int runtime
)
2057 char *line
, *cp
, *var
, *val
;
2058 size_t len
, lineno
= 0;
2060 char file
[PATH_MAX
];
2063 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2065 if (filename
== NULL
)
2068 if (runtime
&& runtime_settings
[0] != '\0') {
2069 snprintf(file
, sizeof file
, "%s/%s",
2070 work_dir
, runtime_settings
);
2071 if (stat(file
, &sb
)) {
2072 warnx("runtime file doesn't exist, creating it");
2073 if ((f
= fopen(file
, "w")) == NULL
)
2075 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2079 strlcpy(file
, filename
, sizeof file
);
2081 if ((config
= fopen(file
, "r")) == NULL
) {
2082 warn("config_parse: cannot open %s", filename
);
2087 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2088 if (feof(config
) || ferror(config
))
2092 cp
+= (long)strspn(cp
, WS
);
2093 if (cp
[0] == '\0') {
2099 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2100 errx(1, "invalid config file entry: %s", line
);
2102 cp
+= (long)strspn(cp
, WS
);
2104 if ((val
= strsep(&cp
, "\0")) == NULL
)
2107 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2108 handled
= settings_add(var
, val
);
2110 errx(1, "invalid conf file entry: %s=%s", var
, val
);
2119 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2125 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2129 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2132 JSStringGetUTF8CString(jsref
, s
, l
);
2133 JSStringRelease(jsref
);
2139 disable_hints(struct tab
*t
)
2141 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2142 bzero(t
->hint_num
, sizeof t
->hint_num
);
2143 run_script(t
, "vimprobable_clear()");
2145 t
->hint_mode
= XT_HINT_NONE
;
2149 enable_hints(struct tab
*t
)
2151 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2152 run_script(t
, "vimprobable_show_hints()");
2154 t
->hint_mode
= XT_HINT_NONE
;
2157 #define XT_JS_OPEN ("open;")
2158 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2159 #define XT_JS_FIRE ("fire;")
2160 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2161 #define XT_JS_FOUND ("found;")
2162 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2165 run_script(struct tab
*t
, char *s
)
2167 JSGlobalContextRef ctx
;
2168 WebKitWebFrame
*frame
;
2170 JSValueRef val
, exception
;
2173 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2174 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2176 frame
= webkit_web_view_get_main_frame(t
->wv
);
2177 ctx
= webkit_web_frame_get_global_context(frame
);
2179 str
= JSStringCreateWithUTF8CString(s
);
2180 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2181 NULL
, 0, &exception
);
2182 JSStringRelease(str
);
2184 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2186 es
= js_ref_to_string(ctx
, exception
);
2187 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2191 es
= js_ref_to_string(ctx
, val
);
2192 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2194 /* handle return value right here */
2195 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2198 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2201 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2202 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2203 &es
[XT_JS_FIRE_LEN
]);
2208 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2209 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2220 hint(struct tab
*t
, struct karg
*args
)
2223 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2225 if (t
->hints_on
== 0)
2234 apply_style(struct tab
*t
)
2236 g_object_set(G_OBJECT(t
->settings
),
2237 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2241 userstyle(struct tab
*t
, struct karg
*args
)
2243 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2247 g_object_set(G_OBJECT(t
->settings
),
2248 "user-stylesheet-uri", NULL
, (char *)NULL
);
2257 * Doesn't work fully, due to the following bug:
2258 * https://bugs.webkit.org/show_bug.cgi?id=51747
2261 restore_global_history(void)
2263 char file
[PATH_MAX
];
2268 const char delim
[3] = {'\\', '\\', '\0'};
2270 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2272 if ((f
= fopen(file
, "r")) == NULL
) {
2273 warnx("%s: fopen", __func__
);
2278 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2279 if (feof(f
) || ferror(f
))
2282 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2283 if (feof(f
) || ferror(f
)) {
2285 warnx("%s: broken history file\n", __func__
);
2289 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2290 webkit_web_history_item_new_with_data(uri
, title
);
2291 h
= g_malloc(sizeof(struct history
));
2292 h
->uri
= g_strdup(uri
);
2293 h
->title
= g_strdup(title
);
2294 RB_INSERT(history_list
, &hl
, h
);
2295 completion_add_uri(h
->uri
);
2297 warnx("%s: failed to restore history\n", __func__
);
2313 save_global_history_to_disk(struct tab
*t
)
2315 char file
[PATH_MAX
];
2319 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2321 if ((f
= fopen(file
, "w")) == NULL
) {
2322 show_oops(t
, "%s: global history file: %s",
2323 __func__
, strerror(errno
));
2327 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2328 if (h
->uri
&& h
->title
)
2329 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2338 quit(struct tab
*t
, struct karg
*args
)
2340 if (save_global_history
)
2341 save_global_history_to_disk(t
);
2349 open_tabs(struct tab
*t
, struct karg
*a
)
2351 char file
[PATH_MAX
];
2355 struct tab
*ti
, *tt
;
2360 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2361 if ((f
= fopen(file
, "r")) == NULL
)
2364 ti
= TAILQ_LAST(&tabs
, tab_list
);
2367 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2368 if (feof(f
) || ferror(f
))
2371 /* retrieve session name */
2372 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2373 strlcpy(named_session
,
2374 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2375 sizeof named_session
);
2379 if (uri
&& strlen(uri
))
2380 create_new_tab(uri
, NULL
, 1, -1);
2386 /* close open tabs */
2387 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2389 tt
= TAILQ_FIRST(&tabs
);
2409 restore_saved_tabs(void)
2411 char file
[PATH_MAX
];
2412 int unlink_file
= 0;
2417 snprintf(file
, sizeof file
, "%s/%s",
2418 sessions_dir
, XT_RESTART_TABS_FILE
);
2419 if (stat(file
, &sb
) == -1)
2420 a
.s
= XT_SAVED_TABS_FILE
;
2423 a
.s
= XT_RESTART_TABS_FILE
;
2426 a
.i
= XT_SES_DONOTHING
;
2427 rv
= open_tabs(NULL
, &a
);
2436 save_tabs(struct tab
*t
, struct karg
*a
)
2438 char file
[PATH_MAX
];
2440 int num_tabs
= 0, i
;
2441 struct tab
**stabs
= NULL
;
2446 snprintf(file
, sizeof file
, "%s/%s",
2447 sessions_dir
, named_session
);
2449 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2451 if ((f
= fopen(file
, "w")) == NULL
) {
2452 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2456 /* save session name */
2457 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2459 /* Save tabs, in the order they are arranged in the notebook. */
2460 num_tabs
= sort_tabs_by_page_num(&stabs
);
2462 for (i
= 0; i
< num_tabs
; i
++)
2463 if (stabs
[i
] && get_uri(stabs
[i
]) != NULL
)
2464 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2468 /* try and make sure this gets to disk NOW. XXX Backup first? */
2469 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2470 show_oops(t
, "May not have managed to save session: %s",
2480 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2492 run_page_script(struct tab
*t
, struct karg
*args
)
2495 char *tmp
, script
[PATH_MAX
];
2497 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2498 if (tmp
[0] == '\0') {
2499 show_oops(t
, "no script specified");
2503 if ((uri
= get_uri(t
)) == NULL
) {
2504 show_oops(t
, "tab is empty, not running script");
2509 snprintf(script
, sizeof script
, "%s/%s",
2510 pwd
->pw_dir
, &tmp
[1]);
2512 strlcpy(script
, tmp
, sizeof script
);
2516 show_oops(t
, "can't fork to run script");
2526 execlp(script
, script
, uri
, (void *)NULL
);
2536 yank_uri(struct tab
*t
, struct karg
*args
)
2539 GtkClipboard
*clipboard
;
2541 if ((uri
= get_uri(t
)) == NULL
)
2544 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2545 gtk_clipboard_set_text(clipboard
, uri
, -1);
2551 paste_uri(struct tab
*t
, struct karg
*args
)
2553 GtkClipboard
*clipboard
;
2554 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2556 gchar
*p
= NULL
, *uri
;
2558 /* try primary clipboard first */
2559 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2560 p
= gtk_clipboard_wait_for_text(clipboard
);
2562 /* if it failed get whatever text is in cut_buffer0 */
2564 if (gdk_property_get(gdk_get_default_root_window(),
2566 gdk_atom_intern("STRING", FALSE
),
2568 1024 * 1024 /* picked out of my butt */,
2574 /* yes sir, we need to NUL the string */
2580 while (*uri
&& isspace(*uri
))
2582 if (strlen(uri
) == 0) {
2583 show_oops(t
, "empty paste buffer");
2586 if (guess_search
== 0 && valid_url_type(uri
)) {
2587 /* we can be clever and paste this in search box */
2588 show_oops(t
, "not a valid URL");
2592 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2594 else if (args
->i
== XT_PASTE_NEW_TAB
)
2595 create_new_tab(uri
, NULL
, 1, -1);
2606 find_domain(const gchar
*s
, int toplevel
)
2614 uri
= soup_uri_new(s
);
2616 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2620 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2621 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2622 while(--p
>= uri
->host
&& *p
!= '.');
2629 if (uri
->port
== 80)
2630 ret
= g_strdup_printf(".%s", p
);
2632 ret
= g_strdup_printf(".%s:%d", p
, uri
->port
);
2640 toggle_cwl(struct tab
*t
, struct karg
*args
)
2651 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2653 if (uri
== NULL
|| dom
== NULL
||
2654 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2655 show_oops(t
, "Can't toggle domain in cookie white list");
2658 d
= wl_find(dom
, &c_wl
);
2665 if (args
->i
& XT_WL_TOGGLE
)
2667 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2669 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2673 /* enable cookies for domain */
2674 wl_add(dom
, &c_wl
, 0);
2676 /* disable cookies for domain */
2677 RB_REMOVE(domain_list
, &c_wl
, d
);
2679 if (args
->i
& XT_WL_RELOAD
)
2680 webkit_web_view_reload(t
->wv
);
2688 toggle_js(struct tab
*t
, struct karg
*args
)
2698 g_object_get(G_OBJECT(t
->settings
),
2699 "enable-scripts", &es
, (char *)NULL
);
2700 if (args
->i
& XT_WL_TOGGLE
)
2702 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2704 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2710 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2712 if (uri
== NULL
|| dom
== NULL
||
2713 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2714 show_oops(t
, "Can't toggle domain in JavaScript white list");
2719 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2720 wl_add(dom
, &js_wl
, 0 /* session */);
2722 d
= wl_find(dom
, &js_wl
);
2724 RB_REMOVE(domain_list
, &js_wl
, d
);
2725 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2727 g_object_set(G_OBJECT(t
->settings
),
2728 "enable-scripts", es
, (char *)NULL
);
2729 g_object_set(G_OBJECT(t
->settings
),
2730 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2731 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2733 if (args
->i
& XT_WL_RELOAD
)
2734 webkit_web_view_reload(t
->wv
);
2742 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2746 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2749 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2754 toggle_src(struct tab
*t
, struct karg
*args
)
2761 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2762 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2763 webkit_web_view_reload(t
->wv
);
2769 focus_webview(struct tab
*t
)
2774 /* only grab focus if we are visible */
2775 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2776 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2780 focus(struct tab
*t
, struct karg
*args
)
2782 if (t
== NULL
|| args
== NULL
)
2788 if (args
->i
== XT_FOCUS_URI
)
2789 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2790 else if (args
->i
== XT_FOCUS_SEARCH
)
2791 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2797 stats(struct tab
*t
, struct karg
*args
)
2799 char *page
, *body
, *s
, line
[64 * 1024];
2800 uint64_t line_count
= 0;
2804 show_oops(NULL
, "stats invalid parameters");
2807 if (save_rejected_cookies
) {
2808 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2810 s
= fgets(line
, sizeof line
, r_cookie_f
);
2811 if (s
== NULL
|| feof(r_cookie_f
) ||
2817 snprintf(line
, sizeof line
,
2818 "<br/>Cookies blocked(*) total: %llu", line_count
);
2820 show_oops(t
, "Can't open blocked cookies file: %s",
2824 body
= g_strdup_printf(
2825 "Cookies blocked(*) this session: %llu"
2827 "<p><small><b>*</b> results vary based on settings</small></p>",
2831 page
= get_html_page("Statistics", body
, "", 0);
2834 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2841 marco(struct tab
*t
, struct karg
*args
)
2843 char *page
, line
[64 * 1024];
2847 show_oops(NULL
, "marco invalid parameters");
2850 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2852 page
= get_html_page("Marco Sez...", line
, "", 0);
2854 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2861 blank(struct tab
*t
, struct karg
*args
)
2864 show_oops(NULL
, "blank invalid parameters");
2866 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2871 about(struct tab
*t
, struct karg
*args
)
2876 show_oops(NULL
, "about invalid parameters");
2878 body
= g_strdup_printf("<b>Version: %s</b><p>"
2881 "<li>Marco Peereboom <marco@peereboom.us></li>"
2882 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2883 "<li>Edd Barrett <vext01@gmail.com> </li>"
2884 "<li>Todd T. Fries <todd@fries.net> </li>"
2885 "<li>Raphael Graf <r@undefined.ch> </li>"
2887 "Copyrights and licenses can be found on the XXXTerm "
2888 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2892 page
= get_html_page("About", body
, "", 0);
2895 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2902 help(struct tab
*t
, struct karg
*args
)
2904 char *page
, *head
, *body
;
2907 show_oops(NULL
, "help invalid parameters");
2909 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2910 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2912 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
2913 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2914 "cgi-bin/man-cgi?xxxterm</a>";
2916 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
2918 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2925 * update all favorite tabs apart from one. Pass NULL if
2926 * you want to update all.
2929 update_favorite_tabs(struct tab
*apart_from
)
2932 if (!updating_fl_tabs
) {
2933 updating_fl_tabs
= 1; /* stop infinite recursion */
2934 TAILQ_FOREACH(t
, &tabs
, entry
)
2935 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2936 && (t
!= apart_from
))
2937 xtp_page_fl(t
, NULL
);
2938 updating_fl_tabs
= 0;
2942 /* show a list of favorites (bookmarks) */
2944 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2946 char file
[PATH_MAX
];
2948 char *uri
= NULL
, *title
= NULL
;
2949 size_t len
, lineno
= 0;
2951 char *body
, *tmp
, *page
= NULL
;
2952 const char delim
[3] = {'\\', '\\', '\0'};
2954 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2957 warn("%s: bad param", __func__
);
2959 /* new session key */
2960 if (!updating_fl_tabs
)
2961 generate_xtp_session_key(&fl_session_key
);
2963 /* open favorites */
2964 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2965 if ((f
= fopen(file
, "r")) == NULL
) {
2966 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2971 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2972 "<th style='width: 40px'>#</th><th>Link</th>"
2973 "<th style='width: 40px'>Rm</th></tr>\n");
2976 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2977 if (feof(f
) || ferror(f
))
2985 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2986 if (feof(f
) || ferror(f
)) {
2987 show_oops(t
, "favorites file corrupt");
2993 body
= g_strdup_printf("%s<tr>"
2995 "<td><a href='%s'>%s</a></td>"
2996 "<td style='text-align: center'>"
2997 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2999 body
, i
, uri
, title
,
3000 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3012 /* if none, say so */
3015 body
= g_strdup_printf("%s<tr>"
3016 "<td colspan='3' style='text-align: center'>"
3017 "No favorites - To add one use the 'favadd' command."
3018 "</td></tr>", body
);
3023 body
= g_strdup_printf("%s</table>", body
);
3033 page
= get_html_page("Favorites", body
, "", 1);
3034 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3038 update_favorite_tabs(t
);
3047 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3048 size_t cert_count
, char *title
)
3050 gnutls_datum_t cinfo
;
3054 body
= g_strdup("");
3056 for (i
= 0; i
< cert_count
; i
++) {
3057 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3062 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3063 body
, i
, cinfo
.data
);
3064 gnutls_free(cinfo
.data
);
3068 tmp
= get_html_page(title
, body
, "", 0);
3071 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3076 ca_cmd(struct tab
*t
, struct karg
*args
)
3079 int rv
= 1, certs
= 0, certs_read
;
3082 gnutls_x509_crt_t
*c
= NULL
;
3083 char *certs_buf
= NULL
, *s
;
3085 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3086 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3090 if (fstat(fileno(f
), &sb
) == -1) {
3091 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3095 certs_buf
= g_malloc(sb
.st_size
+ 1);
3096 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3097 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3100 certs_buf
[sb
.st_size
] = '\0';
3103 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3105 s
+= strlen("BEGIN CERTIFICATE");
3108 bzero(&dt
, sizeof dt
);
3109 dt
.data
= (unsigned char *)certs_buf
;
3110 dt
.size
= sb
.st_size
;
3111 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3112 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3113 GNUTLS_X509_FMT_PEM
, 0);
3114 if (certs_read
<= 0) {
3115 show_oops(t
, "No cert(s) available");
3118 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3131 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
3134 struct addrinfo hints
, *res
= NULL
, *ai
;
3138 if (uri
&& !g_str_has_prefix(uri
, "https://"))
3141 su
= soup_uri_new(uri
);
3144 if (!SOUP_URI_VALID_FOR_HTTP(su
))
3147 snprintf(port
, sizeof port
, "%d", su
->port
);
3148 bzero(&hints
, sizeof(struct addrinfo
));
3149 hints
.ai_flags
= AI_CANONNAME
;
3150 hints
.ai_family
= AF_UNSPEC
;
3151 hints
.ai_socktype
= SOCK_STREAM
;
3153 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
3156 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3157 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3160 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3163 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3167 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
3172 strlcpy(domain
, su
->host
, domain_sz
);
3183 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3186 gnutls_deinit(gsession
);
3188 gnutls_certificate_free_credentials(xcred
);
3194 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3195 gnutls_certificate_credentials_t
*xc
)
3197 gnutls_certificate_credentials_t xcred
;
3198 gnutls_session_t gsession
;
3201 if (gs
== NULL
|| xc
== NULL
)
3207 gnutls_certificate_allocate_credentials(&xcred
);
3208 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3209 GNUTLS_X509_FMT_PEM
);
3210 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3211 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3212 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3213 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3214 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3215 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3217 gnutls_error_is_fatal(rv
),
3218 gnutls_strerror_name(rv
));
3219 stop_tls(gsession
, xcred
);
3223 gnutls_credentials_type_t cred
;
3224 cred
= gnutls_auth_get_type(gsession
);
3225 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3226 stop_tls(gsession
, xcred
);
3238 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3242 const gnutls_datum_t
*cl
;
3243 gnutls_x509_crt_t
*all_certs
;
3246 if (certs
== NULL
|| cert_count
== NULL
)
3248 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3250 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3254 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3255 for (i
= 0; i
< len
; i
++) {
3256 gnutls_x509_crt_init(&all_certs
[i
]);
3257 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3258 GNUTLS_X509_FMT_PEM
< 0)) {
3272 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3276 for (i
= 0; i
< cert_count
; i
++)
3277 gnutls_x509_crt_deinit(certs
[i
]);
3282 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3284 GdkColor c_text
, c_base
;
3286 gdk_color_parse(text
, &c_text
);
3287 gdk_color_parse(base
, &c_base
);
3289 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3290 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3291 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3292 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3294 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3295 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3296 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3297 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3301 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3302 size_t cert_count
, char *domain
)
3305 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3310 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3313 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3314 if ((f
= fopen(file
, "w")) == NULL
) {
3315 show_oops(t
, "Can't create cert file %s %s",
3316 file
, strerror(errno
));
3320 for (i
= 0; i
< cert_count
; i
++) {
3321 cert_buf_sz
= sizeof cert_buf
;
3322 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3323 cert_buf
, &cert_buf_sz
)) {
3324 show_oops(t
, "gnutls_x509_crt_export failed");
3327 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3328 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3333 /* not the best spot but oh well */
3334 gdk_color_parse("lightblue", &color
);
3335 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3336 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3342 load_compare_cert(struct tab
*t
, struct karg
*args
)
3345 char domain
[8182], file
[PATH_MAX
];
3346 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3347 int s
= -1, rv
= 1, i
;
3351 gnutls_session_t gsession
;
3352 gnutls_x509_crt_t
*certs
;
3353 gnutls_certificate_credentials_t xcred
;
3358 if ((uri
= get_uri(t
)) == NULL
)
3361 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3365 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3366 show_oops(t
, "Start TLS failed");
3371 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3372 show_oops(t
, "Can't get connection certificates");
3376 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3377 if ((f
= fopen(file
, "r")) == NULL
)
3380 for (i
= 0; i
< cert_count
; i
++) {
3381 cert_buf_sz
= sizeof cert_buf
;
3382 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3383 cert_buf
, &cert_buf_sz
)) {
3386 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3387 rv
= -1; /* critical */
3390 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3391 rv
= -1; /* critical */
3400 free_connection_certs(certs
, cert_count
);
3402 /* we close the socket first for speed */
3405 stop_tls(gsession
, xcred
);
3411 cert_cmd(struct tab
*t
, struct karg
*args
)
3417 gnutls_session_t gsession
;
3418 gnutls_x509_crt_t
*certs
;
3419 gnutls_certificate_credentials_t xcred
;
3424 if (ssl_ca_file
== NULL
) {
3425 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3429 if ((uri
= get_uri(t
)) == NULL
) {
3430 show_oops(t
, "Invalid URI");
3434 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3435 show_oops(t
, "Invalid certificate URI: %s", uri
);
3440 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3441 show_oops(t
, "Start TLS failed");
3446 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3447 show_oops(t
, "get_connection_certs failed");
3451 if (args
->i
& XT_SHOW
)
3452 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3453 else if (args
->i
& XT_SAVE
)
3454 save_certs(t
, certs
, cert_count
, domain
);
3456 free_connection_certs(certs
, cert_count
);
3458 /* we close the socket first for speed */
3461 stop_tls(gsession
, xcred
);
3467 remove_cookie(int index
)
3473 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3475 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3477 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3481 print_cookie("remove cookie", c
);
3482 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3487 soup_cookies_free(cf
);
3493 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3498 body
= g_strdup("");
3501 if (args
->i
& XT_WL_PERSISTENT
) {
3503 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3505 RB_FOREACH(d
, domain_list
, wl
) {
3509 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3515 if (args
->i
& XT_WL_SESSION
) {
3517 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3519 RB_FOREACH(d
, domain_list
, wl
) {
3523 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3528 tmp
= get_html_page(title
, body
, "", 0);
3531 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3533 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3539 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3541 char file
[PATH_MAX
];
3543 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3551 if (t
== NULL
|| args
== NULL
)
3554 if (runtime_settings
[0] == '\0')
3557 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3558 if ((f
= fopen(file
, "r+")) == NULL
)
3562 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3563 if (uri
== NULL
|| dom
== NULL
||
3564 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3565 show_oops(t
, "Can't add domain to %s white list",
3566 js
? "JavaScript" : "cookie");
3570 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3573 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3576 if (!strcmp(line
, lt
))
3582 fprintf(f
, "%s\n", lt
);
3587 d
= wl_find(dom
, &js_wl
);
3589 settings_add("js_wl", dom
);
3590 d
= wl_find(dom
, &js_wl
);
3594 d
= wl_find(dom
, &c_wl
);
3596 settings_add("cookie_wl", dom
);
3597 d
= wl_find(dom
, &c_wl
);
3601 /* find and add to persistent jar */
3602 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3603 for (;cf
; cf
= cf
->next
) {
3605 if (!strcmp(dom
, ci
->domain
) ||
3606 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3607 c
= soup_cookie_copy(ci
);
3608 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3611 soup_cookies_free(cf
);
3629 js_show_wl(struct tab
*t
, struct karg
*args
)
3631 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3632 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3638 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3640 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3641 wl_show(t
, args
, "Cookie White List", &c_wl
);
3647 cookie_cmd(struct tab
*t
, struct karg
*args
)
3649 if (args
->i
& XT_SHOW
)
3650 wl_show(t
, args
, "Cookie White List", &c_wl
);
3651 else if (args
->i
& XT_WL_TOGGLE
) {
3652 args
->i
|= XT_WL_RELOAD
;
3653 toggle_cwl(t
, args
);
3654 } else if (args
->i
& XT_SAVE
) {
3655 args
->i
|= XT_WL_RELOAD
;
3656 wl_save(t
, args
, 0);
3657 } else if (args
->i
& XT_DELETE
)
3658 show_oops(t
, "'cookie delete' currently unimplemented");
3664 js_cmd(struct tab
*t
, struct karg
*args
)
3666 if (args
->i
& XT_SHOW
)
3667 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3668 else if (args
->i
& XT_SAVE
) {
3669 args
->i
|= XT_WL_RELOAD
;
3670 wl_save(t
, args
, 1);
3671 } else if (args
->i
& XT_WL_TOGGLE
) {
3672 args
->i
|= XT_WL_RELOAD
;
3674 } else if (args
->i
& XT_DELETE
)
3675 show_oops(t
, "'js delete' currently unimplemented");
3681 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3683 js_toggle_cb(t
->js_toggle
, t
);
3689 add_favorite(struct tab
*t
, struct karg
*args
)
3691 char file
[PATH_MAX
];
3694 size_t urilen
, linelen
;
3695 const gchar
*uri
, *title
;
3700 /* don't allow adding of xtp pages to favorites */
3701 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3702 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3706 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3707 if ((f
= fopen(file
, "r+")) == NULL
) {
3708 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3712 title
= get_title(t
, FALSE
);
3715 if (title
== NULL
|| uri
== NULL
) {
3716 show_oops(t
, "can't add page to favorites");
3720 urilen
= strlen(uri
);
3723 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3724 if (feof(f
) || ferror(f
))
3727 if (linelen
== urilen
&& !strcmp(line
, uri
))
3734 fprintf(f
, "\n%s\n%s", title
, uri
);
3740 update_favorite_tabs(NULL
);
3746 navaction(struct tab
*t
, struct karg
*args
)
3748 WebKitWebHistoryItem
*item
;
3750 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3751 t
->tab_id
, args
->i
);
3753 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3756 if (args
->i
== XT_NAV_BACK
)
3757 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3759 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3761 return (XT_CB_PASSTHROUGH
);
3762 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3764 return (XT_CB_PASSTHROUGH
);
3770 webkit_web_view_go_back(t
->wv
);
3772 case XT_NAV_FORWARD
:
3774 webkit_web_view_go_forward(t
->wv
);
3777 webkit_web_view_reload(t
->wv
);
3779 case XT_NAV_RELOAD_CACHE
:
3780 webkit_web_view_reload_bypass_cache(t
->wv
);
3783 return (XT_CB_PASSTHROUGH
);
3787 move(struct tab
*t
, struct karg
*args
)
3789 GtkAdjustment
*adjust
;
3790 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3795 case XT_MOVE_BOTTOM
:
3797 case XT_MOVE_PAGEDOWN
:
3798 case XT_MOVE_PAGEUP
:
3799 case XT_MOVE_HALFDOWN
:
3800 case XT_MOVE_HALFUP
:
3801 case XT_MOVE_PERCENT
:
3802 adjust
= t
->adjust_v
;
3805 adjust
= t
->adjust_h
;
3809 pos
= gtk_adjustment_get_value(adjust
);
3810 ps
= gtk_adjustment_get_page_size(adjust
);
3811 upper
= gtk_adjustment_get_upper(adjust
);
3812 lower
= gtk_adjustment_get_lower(adjust
);
3813 si
= gtk_adjustment_get_step_increment(adjust
);
3814 pi
= gtk_adjustment_get_page_increment(adjust
);
3817 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3818 "max %f si %f pi %f\n",
3819 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3820 pos
, ps
, upper
, lower
, max
, si
, pi
);
3826 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3831 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3833 case XT_MOVE_BOTTOM
:
3834 case XT_MOVE_FARRIGHT
:
3835 gtk_adjustment_set_value(adjust
, max
);
3838 case XT_MOVE_FARLEFT
:
3839 gtk_adjustment_set_value(adjust
, lower
);
3841 case XT_MOVE_PAGEDOWN
:
3843 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3845 case XT_MOVE_PAGEUP
:
3847 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3849 case XT_MOVE_HALFDOWN
:
3851 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3853 case XT_MOVE_HALFUP
:
3855 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3857 case XT_MOVE_PERCENT
:
3861 percent
= atoi(args
->s
) / 100.0;
3862 pos
= max
* percent
;
3863 if (pos
< 0.0 || pos
> max
)
3866 gtk_adjustment_set_value(adjust
, pos
);
3870 return (XT_CB_PASSTHROUGH
);
3873 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3875 return (XT_CB_HANDLED
);
3879 url_set_visibility(void)
3883 TAILQ_FOREACH(t
, &tabs
, entry
)
3884 if (show_url
== 0) {
3885 gtk_widget_hide(t
->toolbar
);
3888 gtk_widget_show(t
->toolbar
);
3892 notebook_tab_set_visibility()
3894 if (show_tabs
== 0) {
3895 gtk_widget_hide(tab_bar
);
3896 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3898 if (tab_style
== XT_TABS_NORMAL
) {
3899 gtk_widget_hide(tab_bar
);
3900 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3901 } else if (tab_style
== XT_TABS_COMPACT
) {
3902 gtk_widget_show(tab_bar
);
3903 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3909 statusbar_set_visibility(void)
3913 TAILQ_FOREACH(t
, &tabs
, entry
)
3914 if (show_statusbar
== 0) {
3915 gtk_widget_hide(t
->statusbar_box
);
3918 gtk_widget_show(t
->statusbar_box
);
3922 url_set(struct tab
*t
, int enable_url_entry
)
3927 show_url
= enable_url_entry
;
3929 if (enable_url_entry
) {
3930 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3931 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3932 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
3934 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3935 GTK_ENTRY_ICON_PRIMARY
);
3937 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3938 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
3939 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3940 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
3946 fullscreen(struct tab
*t
, struct karg
*args
)
3948 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3951 return (XT_CB_PASSTHROUGH
);
3953 if (show_url
== 0) {
3961 url_set_visibility();
3962 notebook_tab_set_visibility();
3964 return (XT_CB_HANDLED
);
3968 statustoggle(struct tab
*t
, struct karg
*args
)
3970 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3972 if (show_statusbar
== 1) {
3974 statusbar_set_visibility();
3975 } else if (show_statusbar
== 0) {
3977 statusbar_set_visibility();
3979 return (XT_CB_HANDLED
);
3983 urlaction(struct tab
*t
, struct karg
*args
)
3985 int rv
= XT_CB_HANDLED
;
3987 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3990 return (XT_CB_PASSTHROUGH
);
3994 if (show_url
== 0) {
3996 url_set_visibility();
4000 if (show_url
== 1) {
4002 url_set_visibility();
4010 tabaction(struct tab
*t
, struct karg
*args
)
4012 int rv
= XT_CB_HANDLED
;
4013 char *url
= args
->s
;
4017 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4020 return (XT_CB_PASSTHROUGH
);
4024 if (strlen(url
) > 0)
4025 create_new_tab(url
, NULL
, 1, args
->p
);
4027 create_new_tab(NULL
, NULL
, 1, args
->p
);
4033 TAILQ_FOREACH(tt
, &tabs
, entry
)
4034 if (tt
->tab_id
== args
->p
- 1) {
4039 case XT_TAB_DELQUIT
:
4040 if (gtk_notebook_get_n_pages(notebook
) > 1)
4046 if (strlen(url
) > 0)
4049 rv
= XT_CB_PASSTHROUGH
;
4055 if (show_tabs
== 0) {
4057 notebook_tab_set_visibility();
4061 if (show_tabs
== 1) {
4063 notebook_tab_set_visibility();
4066 case XT_TAB_NEXTSTYLE
:
4067 if (tab_style
== XT_TABS_NORMAL
) {
4068 tab_style
= XT_TABS_COMPACT
;
4069 recolor_compact_tabs();
4072 tab_style
= XT_TABS_NORMAL
;
4073 notebook_tab_set_visibility();
4075 case XT_TAB_UNDO_CLOSE
:
4076 if (undo_count
== 0) {
4077 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4082 u
= TAILQ_FIRST(&undos
);
4083 create_new_tab(u
->uri
, u
, 1, -1);
4085 TAILQ_REMOVE(&undos
, u
, entry
);
4087 /* u->history is freed in create_new_tab() */
4092 rv
= XT_CB_PASSTHROUGH
;
4106 resizetab(struct tab
*t
, struct karg
*args
)
4108 if (t
== NULL
|| args
== NULL
) {
4109 show_oops(NULL
, "resizetab invalid parameters");
4110 return (XT_CB_PASSTHROUGH
);
4113 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4114 t
->tab_id
, args
->i
);
4116 setzoom_webkit(t
, args
->i
);
4118 return (XT_CB_HANDLED
);
4122 movetab(struct tab
*t
, struct karg
*args
)
4126 if (t
== NULL
|| args
== NULL
) {
4127 show_oops(NULL
, "movetab invalid parameters");
4128 return (XT_CB_PASSTHROUGH
);
4131 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4132 t
->tab_id
, args
->i
);
4134 if (args
->i
>= XT_TAB_INVALID
)
4135 return (XT_CB_PASSTHROUGH
);
4137 if (TAILQ_EMPTY(&tabs
))
4138 return (XT_CB_PASSTHROUGH
);
4140 n
= gtk_notebook_get_n_pages(notebook
);
4141 dest
= gtk_notebook_get_current_page(notebook
);
4146 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4155 dest
-= args
->p
% n
;
4168 return (XT_CB_PASSTHROUGH
);
4171 if (dest
< 0 || dest
>= n
)
4172 return (XT_CB_PASSTHROUGH
);
4173 if (t
->tab_id
== dest
) {
4174 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4175 return (XT_CB_HANDLED
);
4178 set_current_tab(dest
);
4180 return (XT_CB_HANDLED
);
4186 command(struct tab
*t
, struct karg
*args
)
4188 char *s
= NULL
, *ss
= NULL
;
4192 if (t
== NULL
|| args
== NULL
) {
4193 show_oops(NULL
, "command invalid parameters");
4194 return (XT_CB_PASSTHROUGH
);
4205 if (cmd_prefix
== 0)
4208 ss
= g_strdup_printf(":%d", cmd_prefix
);
4219 case XT_CMD_OPEN_CURRENT
:
4222 case XT_CMD_TABNEW_CURRENT
:
4223 if (!s
) /* FALL THROUGH? */
4225 if ((uri
= get_uri(t
)) != NULL
) {
4226 ss
= g_strdup_printf("%s%s", s
, uri
);
4231 show_oops(t
, "command: invalid opcode %d", args
->i
);
4232 return (XT_CB_PASSTHROUGH
);
4235 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4237 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4238 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4239 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4241 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4242 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4247 return (XT_CB_HANDLED
);
4251 * Return a new string with a download row (in html)
4252 * appended. Old string is freed.
4255 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4258 WebKitDownloadStatus stat
;
4259 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4261 char cur_sz
[FMT_SCALED_STRSIZE
];
4262 char tot_sz
[FMT_SCALED_STRSIZE
];
4265 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4267 /* All actions wil take this form:
4268 * xxxt://class/seskey
4270 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4271 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4273 stat
= webkit_download_get_status(dl
->download
);
4276 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4277 status_html
= g_strdup_printf("Finished");
4278 cmd_html
= g_strdup_printf(
4279 "<a href='%s%d/%d'>Remove</a>",
4280 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4282 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4283 /* gather size info */
4284 progress
= 100 * webkit_download_get_progress(dl
->download
);
4287 webkit_download_get_current_size(dl
->download
), cur_sz
);
4289 webkit_download_get_total_size(dl
->download
), tot_sz
);
4291 status_html
= g_strdup_printf(
4292 "<div style='width: 100%%' align='center'>"
4293 "<div class='progress-outer'>"
4294 "<div class='progress-inner' style='width: %.2f%%'>"
4295 "</div></div></div>"
4296 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4297 progress
, cur_sz
, tot_sz
, progress
);
4299 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4300 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4304 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4305 status_html
= g_strdup_printf("Cancelled");
4306 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4307 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4309 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4310 status_html
= g_strdup_printf("Error!");
4311 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4312 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4314 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4315 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4316 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4317 status_html
= g_strdup_printf("Starting");
4320 show_oops(t
, "%s: unknown download status", __func__
);
4323 new_html
= g_strdup_printf(
4324 "%s\n<tr><td>%s</td><td>%s</td>"
4325 "<td style='text-align:center'>%s</td></tr>\n",
4326 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4327 status_html
, cmd_html
);
4331 g_free(status_html
);
4342 * update all download tabs apart from one. Pass NULL if
4343 * you want to update all.
4346 update_download_tabs(struct tab
*apart_from
)
4349 if (!updating_dl_tabs
) {
4350 updating_dl_tabs
= 1; /* stop infinite recursion */
4351 TAILQ_FOREACH(t
, &tabs
, entry
)
4352 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4353 && (t
!= apart_from
))
4354 xtp_page_dl(t
, NULL
);
4355 updating_dl_tabs
= 0;
4360 * update all cookie tabs apart from one. Pass NULL if
4361 * you want to update all.
4364 update_cookie_tabs(struct tab
*apart_from
)
4367 if (!updating_cl_tabs
) {
4368 updating_cl_tabs
= 1; /* stop infinite recursion */
4369 TAILQ_FOREACH(t
, &tabs
, entry
)
4370 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4371 && (t
!= apart_from
))
4372 xtp_page_cl(t
, NULL
);
4373 updating_cl_tabs
= 0;
4378 * update all history tabs apart from one. Pass NULL if
4379 * you want to update all.
4382 update_history_tabs(struct tab
*apart_from
)
4386 if (!updating_hl_tabs
) {
4387 updating_hl_tabs
= 1; /* stop infinite recursion */
4388 TAILQ_FOREACH(t
, &tabs
, entry
)
4389 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4390 && (t
!= apart_from
))
4391 xtp_page_hl(t
, NULL
);
4392 updating_hl_tabs
= 0;
4396 /* cookie management XTP page */
4398 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4400 char *body
, *page
, *tmp
;
4401 int i
= 1; /* all ids start 1 */
4402 GSList
*sc
, *pc
, *pc_start
;
4404 char *type
, *table_headers
, *last_domain
;
4406 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4409 show_oops(NULL
, "%s invalid parameters", __func__
);
4413 /* Generate a new session key */
4414 if (!updating_cl_tabs
)
4415 generate_xtp_session_key(&cl_session_key
);
4418 table_headers
= g_strdup_printf("<table><tr>"
4421 "<th style='width:200px'>Value</th>"
4425 "<th>HTTP<br />only</th>"
4426 "<th style='width:40px'>Rm</th></tr>\n");
4428 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4429 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4433 last_domain
= strdup("");
4434 for (; sc
; sc
= sc
->next
) {
4437 if (strcmp(last_domain
, c
->domain
) != 0) {
4440 last_domain
= strdup(c
->domain
);
4444 body
= g_strdup_printf("%s</table>"
4446 body
, c
->domain
, table_headers
);
4450 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4451 c
->domain
, table_headers
);
4456 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4457 if (soup_cookie_equal(pc
->data
, c
)) {
4458 type
= "Session + Persistent";
4463 body
= g_strdup_printf(
4466 "<td style='word-wrap:normal'>%s</td>"
4468 " <textarea rows='4'>%s</textarea>"
4474 "<td style='text-align:center'>"
4475 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4482 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4497 soup_cookies_free(sc
);
4498 soup_cookies_free(pc
);
4500 /* small message if there are none */
4502 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4503 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4506 body
= g_strdup_printf("%s</table>", body
);
4509 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4511 g_free(table_headers
);
4512 g_free(last_domain
);
4514 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4515 update_cookie_tabs(t
);
4523 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4525 char *body
, *page
, *tmp
;
4527 int i
= 1; /* all ids start 1 */
4529 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4532 show_oops(NULL
, "%s invalid parameters", __func__
);
4536 /* Generate a new session key */
4537 if (!updating_hl_tabs
)
4538 generate_xtp_session_key(&hl_session_key
);
4541 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4542 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4544 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4546 body
= g_strdup_printf(
4548 "<td><a href='%s'>%s</a></td>"
4550 "<td style='text-align: center'>"
4551 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4552 body
, h
->uri
, h
->uri
, h
->title
,
4553 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4554 XT_XTP_HL_REMOVE
, i
);
4560 /* small message if there are none */
4563 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4564 "colspan='3'>No History</td></tr>\n", body
);
4569 body
= g_strdup_printf("%s</table>", body
);
4572 page
= get_html_page("History", body
, "", TRUE
);
4576 * update all history manager tabs as the xtp session
4577 * key has now changed. No need to update the current tab.
4578 * Already did that above.
4580 update_history_tabs(t
);
4582 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4589 * Generate a web page detailing the status of any downloads
4592 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4594 struct download
*dl
;
4595 char *body
, *page
, *tmp
;
4599 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4602 show_oops(NULL
, "%s invalid parameters", __func__
);
4607 * Generate a new session key for next page instance.
4608 * This only happens for the top level call to xtp_page_dl()
4609 * in which case updating_dl_tabs is 0.
4611 if (!updating_dl_tabs
)
4612 generate_xtp_session_key(&dl_session_key
);
4614 /* header - with refresh so as to update */
4615 if (refresh_interval
>= 1)
4616 ref
= g_strdup_printf(
4617 "<meta http-equiv='refresh' content='%u"
4618 ";url=%s%d/%s/%d' />\n",
4627 body
= g_strdup_printf("<div align='center'>"
4628 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4629 "</p><table><tr><th style='width: 60%%'>"
4630 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4631 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4633 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4634 body
= xtp_page_dl_row(t
, body
, dl
);
4638 /* message if no downloads in list */
4641 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4642 " style='text-align: center'>"
4643 "No downloads</td></tr>\n", body
);
4648 body
= g_strdup_printf("%s</table></div>", body
);
4651 page
= get_html_page("Downloads", body
, ref
, 1);
4656 * update all download manager tabs as the xtp session
4657 * key has now changed. No need to update the current tab.
4658 * Already did that above.
4660 update_download_tabs(t
);
4662 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4669 search(struct tab
*t
, struct karg
*args
)
4673 if (t
== NULL
|| args
== NULL
) {
4674 show_oops(NULL
, "search invalid parameters");
4677 if (t
->search_text
== NULL
) {
4678 if (global_search
== NULL
)
4679 return (XT_CB_PASSTHROUGH
);
4681 t
->search_text
= g_strdup(global_search
);
4682 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4683 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4687 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4688 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4691 case XT_SEARCH_NEXT
:
4692 d
= t
->search_forward
;
4694 case XT_SEARCH_PREV
:
4695 d
= !t
->search_forward
;
4698 return (XT_CB_PASSTHROUGH
);
4701 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4703 return (XT_CB_HANDLED
);
4706 struct settings_args
{
4712 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4715 struct settings_args
*sa
= cb_args
;
4720 if (s
->flags
& XT_SF_RUNTIME
)
4726 *sa
->body
= g_strdup_printf(
4728 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4729 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4741 set(struct tab
*t
, struct karg
*args
)
4743 char *body
, *page
, *tmp
;
4745 struct settings_args sa
;
4747 bzero(&sa
, sizeof sa
);
4751 body
= g_strdup_printf("<div align='center'><table><tr>"
4752 "<th align='left'>Setting</th>"
4753 "<th align='left'>Value</th></tr>\n");
4755 settings_walk(print_setting
, &sa
);
4758 /* small message if there are none */
4761 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4762 "colspan='2'>No settings</td></tr>\n", body
);
4767 body
= g_strdup_printf("%s</table></div>", body
);
4770 page
= get_html_page("Settings", body
, "", 0);
4774 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4778 return (XT_CB_PASSTHROUGH
);
4782 session_save(struct tab
*t
, char *filename
)
4787 if (strlen(filename
) == 0)
4790 if (filename
[0] == '.' || filename
[0] == '/')
4794 if (save_tabs(t
, &a
))
4796 strlcpy(named_session
, filename
, sizeof named_session
);
4804 session_open(struct tab
*t
, char *filename
)
4809 if (strlen(filename
) == 0)
4812 if (filename
[0] == '.' || filename
[0] == '/')
4816 a
.i
= XT_SES_CLOSETABS
;
4817 if (open_tabs(t
, &a
))
4820 strlcpy(named_session
, filename
, sizeof named_session
);
4828 session_delete(struct tab
*t
, char *filename
)
4830 char file
[PATH_MAX
];
4833 if (strlen(filename
) == 0)
4836 if (filename
[0] == '.' || filename
[0] == '/')
4839 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4843 if (!strcmp(filename
, named_session
))
4844 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4845 sizeof named_session
);
4853 session_cmd(struct tab
*t
, struct karg
*args
)
4855 char *filename
= args
->s
;
4860 if (args
->i
& XT_SHOW
)
4861 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4862 XT_SAVED_TABS_FILE
: named_session
);
4863 else if (args
->i
& XT_SAVE
) {
4864 if (session_save(t
, filename
)) {
4865 show_oops(t
, "Can't save session: %s",
4866 filename
? filename
: "INVALID");
4869 } else if (args
->i
& XT_OPEN
) {
4870 if (session_open(t
, filename
)) {
4871 show_oops(t
, "Can't open session: %s",
4872 filename
? filename
: "INVALID");
4875 } else if (args
->i
& XT_DELETE
) {
4876 if (session_delete(t
, filename
)) {
4877 show_oops(t
, "Can't delete session: %s",
4878 filename
? filename
: "INVALID");
4883 return (XT_CB_PASSTHROUGH
);
4887 * Make a hardcopy of the page
4890 print_page(struct tab
*t
, struct karg
*args
)
4892 WebKitWebFrame
*frame
;
4894 GtkPrintOperation
*op
;
4895 GtkPrintOperationAction action
;
4896 GtkPrintOperationResult print_res
;
4897 GError
*g_err
= NULL
;
4898 int marg_l
, marg_r
, marg_t
, marg_b
;
4900 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4902 ps
= gtk_page_setup_new();
4903 op
= gtk_print_operation_new();
4904 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4905 frame
= webkit_web_view_get_main_frame(t
->wv
);
4907 /* the default margins are too small, so we will bump them */
4908 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4909 XT_PRINT_EXTRA_MARGIN
;
4910 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4911 XT_PRINT_EXTRA_MARGIN
;
4912 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4913 XT_PRINT_EXTRA_MARGIN
;
4914 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4915 XT_PRINT_EXTRA_MARGIN
;
4918 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4919 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4920 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4921 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4923 gtk_print_operation_set_default_page_setup(op
, ps
);
4925 /* this appears to free 'op' and 'ps' */
4926 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4928 /* check it worked */
4929 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4930 show_oops(NULL
, "can't print: %s", g_err
->message
);
4931 g_error_free (g_err
);
4939 go_home(struct tab
*t
, struct karg
*args
)
4946 restart(struct tab
*t
, struct karg
*args
)
4950 a
.s
= XT_RESTART_TABS_FILE
;
4952 execvp(start_argv
[0], start_argv
);
4958 #define CTRL GDK_CONTROL_MASK
4959 #define MOD1 GDK_MOD1_MASK
4960 #define SHFT GDK_SHIFT_MASK
4962 /* inherent to GTK not all keys will be caught at all times */
4963 /* XXX sort key bindings */
4964 struct key_binding
{
4969 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4971 { "cookiejar", MOD1
, 0, GDK_j
},
4972 { "downloadmgr", MOD1
, 0, GDK_d
},
4973 { "history", MOD1
, 0, GDK_h
},
4974 { "print", CTRL
, 0, GDK_p
},
4975 { "search", 0, 0, GDK_slash
},
4976 { "searchb", 0, 0, GDK_question
},
4977 { "statustoggle", CTRL
, 0, GDK_n
},
4978 { "command", 0, 0, GDK_colon
},
4979 { "qa", CTRL
, 0, GDK_q
},
4980 { "restart", MOD1
, 0, GDK_q
},
4981 { "js toggle", CTRL
, 0, GDK_j
},
4982 { "cookie toggle", MOD1
, 0, GDK_c
},
4983 { "togglesrc", CTRL
, 0, GDK_s
},
4984 { "yankuri", 0, 0, GDK_y
},
4985 { "pasteuricur", 0, 0, GDK_p
},
4986 { "pasteurinew", 0, 0, GDK_P
},
4987 { "toplevel toggle", 0, 0, GDK_F4
},
4988 { "help", 0, 0, GDK_F1
},
4989 { "run_script", MOD1
, 0, GDK_r
},
4992 { "searchnext", 0, 0, GDK_n
},
4993 { "searchprevious", 0, 0, GDK_N
},
4996 { "focusaddress", 0, 0, GDK_F6
},
4997 { "focussearch", 0, 0, GDK_F7
},
5000 { "hinting", 0, 0, GDK_f
},
5002 /* custom stylesheet */
5003 { "userstyle", 0, 0, GDK_i
},
5006 { "goback", 0, 0, GDK_BackSpace
},
5007 { "goback", MOD1
, 0, GDK_Left
},
5008 { "goforward", SHFT
, 0, GDK_BackSpace
},
5009 { "goforward", MOD1
, 0, GDK_Right
},
5010 { "reload", 0, 0, GDK_F5
},
5011 { "reload", CTRL
, 0, GDK_r
},
5012 { "reloadforce", CTRL
, 0, GDK_R
},
5013 { "reload", CTRL
, 0, GDK_l
},
5014 { "favorites", MOD1
, 1, GDK_f
},
5016 /* vertical movement */
5017 { "scrolldown", 0, 0, GDK_j
},
5018 { "scrolldown", 0, 0, GDK_Down
},
5019 { "scrollup", 0, 0, GDK_Up
},
5020 { "scrollup", 0, 0, GDK_k
},
5021 { "scrollbottom", 0, 0, GDK_G
},
5022 { "scrollbottom", 0, 0, GDK_End
},
5023 { "scrolltop", 0, 0, GDK_Home
},
5024 { "scrollpagedown", 0, 0, GDK_space
},
5025 { "scrollpagedown", CTRL
, 0, GDK_f
},
5026 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5027 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5028 { "scrollpageup", 0, 0, GDK_Page_Up
},
5029 { "scrollpageup", CTRL
, 0, GDK_b
},
5030 { "scrollhalfup", CTRL
, 0, GDK_u
},
5031 /* horizontal movement */
5032 { "scrollright", 0, 0, GDK_l
},
5033 { "scrollright", 0, 0, GDK_Right
},
5034 { "scrollleft", 0, 0, GDK_Left
},
5035 { "scrollleft", 0, 0, GDK_h
},
5036 { "scrollfarright", 0, 0, GDK_dollar
},
5037 { "scrollfarleft", 0, 0, GDK_0
},
5040 { "tabnew", CTRL
, 0, GDK_t
},
5041 { "999tabnew", CTRL
, 0, GDK_T
},
5042 { "tabclose", CTRL
, 1, GDK_w
},
5043 { "tabundoclose", 0, 0, GDK_U
},
5044 { "tabnext 1", CTRL
, 0, GDK_1
},
5045 { "tabnext 2", CTRL
, 0, GDK_2
},
5046 { "tabnext 3", CTRL
, 0, GDK_3
},
5047 { "tabnext 4", CTRL
, 0, GDK_4
},
5048 { "tabnext 5", CTRL
, 0, GDK_5
},
5049 { "tabnext 6", CTRL
, 0, GDK_6
},
5050 { "tabnext 7", CTRL
, 0, GDK_7
},
5051 { "tabnext 8", CTRL
, 0, GDK_8
},
5052 { "tabnext 9", CTRL
, 0, GDK_9
},
5053 { "tabfirst", CTRL
, 0, GDK_less
},
5054 { "tablast", CTRL
, 0, GDK_greater
},
5055 { "tabprevious", CTRL
, 0, GDK_Left
},
5056 { "tabnext", CTRL
, 0, GDK_Right
},
5057 { "focusout", CTRL
, 0, GDK_minus
},
5058 { "focusin", CTRL
, 0, GDK_plus
},
5059 { "focusin", CTRL
, 0, GDK_equal
},
5060 { "focusreset", CTRL
, 0, GDK_0
},
5062 /* command aliases (handy when -S flag is used) */
5063 { "promptopen", 0, 0, GDK_F9
},
5064 { "promptopencurrent", 0, 0, GDK_F10
},
5065 { "prompttabnew", 0, 0, GDK_F11
},
5066 { "prompttabnewcurrent",0, 0, GDK_F12
},
5068 TAILQ_HEAD(keybinding_list
, key_binding
);
5071 walk_kb(struct settings
*s
,
5072 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5074 struct key_binding
*k
;
5077 if (s
== NULL
|| cb
== NULL
) {
5078 show_oops(NULL
, "walk_kb invalid parameters");
5082 TAILQ_FOREACH(k
, &kbl
, entry
) {
5088 if (gdk_keyval_name(k
->key
) == NULL
)
5091 strlcat(str
, k
->cmd
, sizeof str
);
5092 strlcat(str
, ",", sizeof str
);
5094 if (k
->mask
& GDK_SHIFT_MASK
)
5095 strlcat(str
, "S-", sizeof str
);
5096 if (k
->mask
& GDK_CONTROL_MASK
)
5097 strlcat(str
, "C-", sizeof str
);
5098 if (k
->mask
& GDK_MOD1_MASK
)
5099 strlcat(str
, "M1-", sizeof str
);
5100 if (k
->mask
& GDK_MOD2_MASK
)
5101 strlcat(str
, "M2-", sizeof str
);
5102 if (k
->mask
& GDK_MOD3_MASK
)
5103 strlcat(str
, "M3-", sizeof str
);
5104 if (k
->mask
& GDK_MOD4_MASK
)
5105 strlcat(str
, "M4-", sizeof str
);
5106 if (k
->mask
& GDK_MOD5_MASK
)
5107 strlcat(str
, "M5-", sizeof str
);
5109 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5110 cb(s
, str
, cb_args
);
5115 init_keybindings(void)
5118 struct key_binding
*k
;
5120 for (i
= 0; i
< LENGTH(keys
); i
++) {
5121 k
= g_malloc0(sizeof *k
);
5122 k
->cmd
= keys
[i
].cmd
;
5123 k
->mask
= keys
[i
].mask
;
5124 k
->use_in_entry
= keys
[i
].use_in_entry
;
5125 k
->key
= keys
[i
].key
;
5126 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5128 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5129 k
->cmd
? k
->cmd
: "unnamed key");
5134 keybinding_clearall(void)
5136 struct key_binding
*k
, *next
;
5138 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5139 next
= TAILQ_NEXT(k
, entry
);
5143 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5144 k
->cmd
? k
->cmd
: "unnamed key");
5145 TAILQ_REMOVE(&kbl
, k
, entry
);
5151 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5153 struct key_binding
*k
;
5154 guint keyval
, mask
= 0;
5157 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5159 /* Keys which are to be used in entry have been prefixed with an
5160 * exclamation mark. */
5164 /* find modifier keys */
5165 if (strstr(key
, "S-"))
5166 mask
|= GDK_SHIFT_MASK
;
5167 if (strstr(key
, "C-"))
5168 mask
|= GDK_CONTROL_MASK
;
5169 if (strstr(key
, "M1-"))
5170 mask
|= GDK_MOD1_MASK
;
5171 if (strstr(key
, "M2-"))
5172 mask
|= GDK_MOD2_MASK
;
5173 if (strstr(key
, "M3-"))
5174 mask
|= GDK_MOD3_MASK
;
5175 if (strstr(key
, "M4-"))
5176 mask
|= GDK_MOD4_MASK
;
5177 if (strstr(key
, "M5-"))
5178 mask
|= GDK_MOD5_MASK
;
5181 for (i
= strlen(key
) - 1; i
> 0; i
--)
5185 /* validate keyname */
5186 keyval
= gdk_keyval_from_name(key
);
5187 if (keyval
== GDK_VoidSymbol
) {
5188 warnx("invalid keybinding name %s", key
);
5191 /* must run this test too, gtk+ doesn't handle 10 for example */
5192 if (gdk_keyval_name(keyval
) == NULL
) {
5193 warnx("invalid keybinding name %s", key
);
5197 /* Remove eventual dupes. */
5198 TAILQ_FOREACH(k
, &kbl
, entry
)
5199 if (k
->key
== keyval
&& k
->mask
== mask
) {
5200 TAILQ_REMOVE(&kbl
, k
, entry
);
5206 k
= g_malloc0(sizeof *k
);
5207 k
->cmd
= g_strdup(cmd
);
5209 k
->use_in_entry
= use_in_entry
;
5212 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5217 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5218 k
->cmd
, gdk_keyval_name(keyval
));
5220 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5226 add_kb(struct settings
*s
, char *entry
)
5230 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5232 /* clearall is special */
5233 if (!strcmp(entry
, "clearall")) {
5234 keybinding_clearall();
5238 kb
= strstr(entry
, ",");
5244 return (keybinding_add(entry
, key
, key
[0] == '!'));
5250 int (*func
)(struct tab
*, struct karg
*);
5254 { "command", 0, command
, ':', 0 },
5255 { "search", 0, command
, '/', 0 },
5256 { "searchb", 0, command
, '?', 0 },
5257 { "togglesrc", 0, toggle_src
, 0, 0 },
5259 /* yanking and pasting */
5260 { "yankuri", 0, yank_uri
, 0, 0 },
5261 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5262 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5263 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5266 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5267 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5270 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5271 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5274 { "hinting", 0, hint
, 0, 0 },
5276 /* custom stylesheet */
5277 { "userstyle", 0, userstyle
, 0, 0 },
5280 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5281 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5282 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5283 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
5285 /* vertical movement */
5286 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5287 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5288 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5289 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5290 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5291 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5292 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5293 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5294 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5295 /* horizontal movement */
5296 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5297 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5298 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5299 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5301 { "favorites", 0, xtp_page_fl
, 0, 0 },
5302 { "fav", 0, xtp_page_fl
, 0, 0 },
5303 { "favadd", 0, add_favorite
, 0, 0 },
5305 { "qall", 0, quit
, 0, 0 },
5306 { "quitall", 0, quit
, 0, 0 },
5307 { "w", 0, save_tabs
, 0, 0 },
5308 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5309 { "help", 0, help
, 0, 0 },
5310 { "about", 0, about
, 0, 0 },
5311 { "stats", 0, stats
, 0, 0 },
5312 { "version", 0, about
, 0, 0 },
5315 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5316 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5317 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5318 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5319 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5320 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5321 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5322 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5323 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5324 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5325 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5327 /* cookie command */
5328 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5329 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5330 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5331 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5332 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5333 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5334 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5335 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5336 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5337 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5338 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5340 /* toplevel (domain) command */
5341 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5342 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5345 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5348 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5349 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5350 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5352 { "ca", 0, ca_cmd
, 0, 0 },
5353 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5354 { "dl", 0, xtp_page_dl
, 0, 0 },
5355 { "h", 0, xtp_page_hl
, 0, 0 },
5356 { "history", 0, xtp_page_hl
, 0, 0 },
5357 { "home", 0, go_home
, 0, 0 },
5358 { "restart", 0, restart
, 0, 0 },
5359 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5360 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5361 { "statustoggle", 0, statustoggle
, 0, 0 },
5362 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5364 { "print", 0, print_page
, 0, 0 },
5367 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5368 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5369 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5370 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5371 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5372 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5373 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5374 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5375 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5376 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5377 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5378 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5379 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5380 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5381 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5382 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5383 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5384 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5385 { "buffers", 0, buffers
, 0, 0 },
5386 { "ls", 0, buffers
, 0, 0 },
5387 { "tabs", 0, buffers
, 0, 0 },
5389 /* command aliases (handy when -S flag is used) */
5390 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5391 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5392 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5393 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5396 { "set", 0, set
, 0, 0 },
5397 { "fullscreen", 0, fullscreen
, 0, 0 },
5398 { "f", 0, fullscreen
, 0, 0 },
5401 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5402 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5403 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5404 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5405 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5412 } cmd_status
= {-1, 0};
5415 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5418 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5425 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5432 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5434 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5440 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5442 a
.i
= XT_NAV_FORWARD
;
5452 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5454 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5456 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5463 * cancel, remove, etc. downloads
5466 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5468 struct download find
, *d
= NULL
;
5470 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5472 /* some commands require a valid download id */
5473 if (cmd
!= XT_XTP_DL_LIST
) {
5474 /* lookup download in question */
5476 d
= RB_FIND(download_list
, &downloads
, &find
);
5479 show_oops(t
, "%s: no such download", __func__
);
5484 /* decide what to do */
5486 case XT_XTP_DL_CANCEL
:
5487 webkit_download_cancel(d
->download
);
5489 case XT_XTP_DL_REMOVE
:
5490 webkit_download_cancel(d
->download
); /* just incase */
5491 g_object_unref(d
->download
);
5492 RB_REMOVE(download_list
, &downloads
, d
);
5494 case XT_XTP_DL_LIST
:
5498 show_oops(t
, "%s: unknown command", __func__
);
5501 xtp_page_dl(t
, NULL
);
5505 * Actions on history, only does one thing for now, but
5506 * we provide the function for future actions
5509 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5511 struct history
*h
, *next
;
5515 case XT_XTP_HL_REMOVE
:
5516 /* walk backwards, as listed in reverse */
5517 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5518 next
= RB_PREV(history_list
, &hl
, h
);
5520 RB_REMOVE(history_list
, &hl
, h
);
5521 g_free((gpointer
) h
->title
);
5522 g_free((gpointer
) h
->uri
);
5529 case XT_XTP_HL_LIST
:
5530 /* Nothing - just xtp_page_hl() below */
5533 show_oops(t
, "%s: unknown command", __func__
);
5537 xtp_page_hl(t
, NULL
);
5540 /* remove a favorite */
5542 remove_favorite(struct tab
*t
, int index
)
5544 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5545 char *new_favs
, *tmp
;
5550 /* open favorites */
5551 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5553 if ((f
= fopen(file
, "r")) == NULL
) {
5554 show_oops(t
, "%s: can't open favorites: %s",
5555 __func__
, strerror(errno
));
5559 /* build a string which will become the new favroites file */
5560 new_favs
= g_strdup("");
5563 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5564 if (feof(f
) || ferror(f
))
5566 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5573 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5574 if (feof(f
) || ferror(f
)) {
5575 show_oops(t
, "%s: can't parse favorites %s",
5576 __func__
, strerror(errno
));
5581 /* as long as this isn't the one we are deleting add to file */
5584 new_favs
= g_strdup_printf("%s%s\n%s\n",
5585 new_favs
, title
, uri
);
5597 /* write back new favorites file */
5598 if ((f
= fopen(file
, "w")) == NULL
) {
5599 show_oops(t
, "%s: can't open favorites: %s",
5600 __func__
, strerror(errno
));
5604 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5617 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5620 case XT_XTP_FL_LIST
:
5621 /* nothing, just the below call to xtp_page_fl() */
5623 case XT_XTP_FL_REMOVE
:
5624 remove_favorite(t
, arg
);
5627 show_oops(t
, "%s: invalid favorites command", __func__
);
5631 xtp_page_fl(t
, NULL
);
5635 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5638 case XT_XTP_CL_LIST
:
5639 /* nothing, just xtp_page_cl() */
5641 case XT_XTP_CL_REMOVE
:
5645 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5649 xtp_page_cl(t
, NULL
);
5652 /* link an XTP class to it's session key and handler function */
5653 struct xtp_despatch
{
5656 void (*handle_func
)(struct tab
*, uint8_t, int);
5659 struct xtp_despatch xtp_despatches
[] = {
5660 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5661 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5662 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5663 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5664 { XT_XTP_INVALID
, NULL
, NULL
}
5668 * is the url xtp protocol? (xxxt://)
5669 * if so, parse and despatch correct bahvior
5672 parse_xtp_url(struct tab
*t
, const char *url
)
5674 char *dup
= NULL
, *p
, *last
;
5675 uint8_t n_tokens
= 0;
5676 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5677 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5682 * tokens array meaning:
5684 * tokens[1] = session key
5685 * tokens[2] = action
5686 * tokens[3] = optional argument
5689 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5691 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5694 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5696 /* split out the url */
5697 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5698 (p
= strtok_r(NULL
, "/", &last
))) {
5700 tokens
[n_tokens
++] = p
;
5703 /* should be atleast three fields 'class/seskey/command/arg' */
5707 dsp
= xtp_despatches
;
5708 req_class
= atoi(tokens
[0]);
5709 while (dsp
->xtp_class
) {
5710 if (dsp
->xtp_class
== req_class
) {
5717 /* did we find one atall? */
5718 if (dsp_match
== NULL
) {
5719 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5723 /* check session key and call despatch function */
5724 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5725 ret
= TRUE
; /* all is well, this was a valid xtp request */
5726 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5739 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5741 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5743 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5746 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5751 show_oops(t
, "activate_uri_entry_cb no uri");
5755 uri
+= strspn(uri
, "\t ");
5757 /* if xxxt:// treat specially */
5758 if (parse_xtp_url(t
, uri
))
5761 /* otherwise continue to load page normally */
5762 load_uri(t
, (gchar
*)uri
);
5767 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5769 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5770 char *newuri
= NULL
;
5773 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5776 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5780 if (search_string
== NULL
) {
5781 show_oops(t
, "no search_string");
5785 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5787 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5788 newuri
= g_strdup_printf(search_string
, enc_search
);
5792 webkit_web_view_load_uri(t
->wv
, newuri
);
5800 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5802 struct domain
*d
= NULL
;
5805 if (uri
== NULL
|| t
== NULL
)
5808 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5813 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5814 es
? "enable" : "disable", uri
);
5816 g_object_set(G_OBJECT(t
->settings
),
5817 "enable-scripts", es
, (char *)NULL
);
5818 g_object_set(G_OBJECT(t
->settings
),
5819 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5820 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5822 button_set_stockid(t
->js_toggle
,
5823 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5827 show_ca_status(struct tab
*t
, const char *uri
)
5829 WebKitWebFrame
*frame
;
5830 WebKitWebDataSource
*source
;
5831 WebKitNetworkRequest
*request
;
5832 SoupMessage
*message
;
5834 gchar
*col_str
= XT_COLOR_WHITE
;
5837 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5838 ssl_strict_certs
, ssl_ca_file
, uri
);
5842 if (ssl_ca_file
== NULL
) {
5843 if (g_str_has_prefix(uri
, "http://"))
5845 if (g_str_has_prefix(uri
, "https://")) {
5846 col_str
= XT_COLOR_RED
;
5851 if (g_str_has_prefix(uri
, "http://") ||
5852 !g_str_has_prefix(uri
, "https://"))
5855 frame
= webkit_web_view_get_main_frame(t
->wv
);
5856 source
= webkit_web_frame_get_data_source(frame
);
5857 request
= webkit_web_data_source_get_request(source
);
5858 message
= webkit_network_request_get_message(request
);
5860 if (message
&& (soup_message_get_flags(message
) &
5861 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5862 col_str
= XT_COLOR_GREEN
;
5865 r
= load_compare_cert(t
, NULL
);
5867 col_str
= XT_COLOR_BLUE
;
5869 col_str
= XT_COLOR_YELLOW
;
5871 col_str
= XT_COLOR_RED
;
5876 gdk_color_parse(col_str
, &color
);
5877 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5879 if (!strcmp(col_str
, XT_COLOR_WHITE
))
5880 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
5882 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
5887 free_favicon(struct tab
*t
)
5889 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5890 __func__
, t
->icon_download
, t
->icon_request
);
5892 if (t
->icon_request
)
5893 g_object_unref(t
->icon_request
);
5894 if (t
->icon_dest_uri
)
5895 g_free(t
->icon_dest_uri
);
5897 t
->icon_request
= NULL
;
5898 t
->icon_dest_uri
= NULL
;
5902 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5904 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5905 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5907 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5908 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5910 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5911 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5915 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5917 GdkPixbuf
*pb_scaled
;
5919 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5920 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
5921 GDK_INTERP_BILINEAR
);
5925 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5926 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5928 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
5929 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5931 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5932 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5934 if (pb_scaled
!= pb
)
5935 g_object_unref(pb_scaled
);
5939 xt_icon_from_file(struct tab
*t
, char *file
)
5943 if (g_str_has_prefix(file
, "file://"))
5944 file
+= strlen("file://");
5946 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5948 xt_icon_from_pixbuf(t
, pb
);
5951 xt_icon_from_name(t
, "text-html");
5955 is_valid_icon(char *file
)
5958 const char *mime_type
;
5962 gf
= g_file_new_for_path(file
);
5963 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5965 mime_type
= g_file_info_get_content_type(fi
);
5966 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5967 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5968 g_strcmp0(mime_type
, "image/png") == 0 ||
5969 g_strcmp0(mime_type
, "image/gif") == 0 ||
5970 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5978 set_favicon_from_file(struct tab
*t
, char *file
)
5982 if (t
== NULL
|| file
== NULL
)
5985 if (g_str_has_prefix(file
, "file://"))
5986 file
+= strlen("file://");
5987 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5989 if (!stat(file
, &sb
)) {
5990 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5991 /* corrupt icon so trash it */
5992 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5995 /* no need to set icon to default here */
5999 xt_icon_from_file(t
, file
);
6003 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6006 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6007 struct tab
*tt
= NULL
, *t
= NULL
;
6010 * find the webview instead of passing in the tab as it could have been
6011 * deleted from underneath us.
6013 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6022 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6023 __func__
, t
->tab_id
, status
);
6026 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6028 t
->icon_download
= NULL
;
6031 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6034 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6037 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6039 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6040 __func__
, t
->tab_id
);
6041 t
->icon_download
= NULL
;
6044 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6047 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6048 __func__
, t
->icon_dest_uri
);
6049 set_favicon_from_file(t
, t
->icon_dest_uri
);
6050 /* these will be freed post callback */
6051 t
->icon_request
= NULL
;
6052 t
->icon_download
= NULL
;
6060 abort_favicon_download(struct tab
*t
)
6062 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6064 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6065 if (t
->icon_download
) {
6066 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6067 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6068 webkit_download_cancel(t
->icon_download
);
6069 t
->icon_download
= NULL
;
6074 xt_icon_from_name(t
, "text-html");
6078 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6080 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6082 if (uri
== NULL
|| t
== NULL
)
6085 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6086 /* take icon from WebKitIconDatabase */
6089 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6091 xt_icon_from_pixbuf(t
, pb
);
6094 xt_icon_from_name(t
, "text-html");
6095 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6096 /* download icon to cache dir */
6097 gchar
*name_hash
, file
[PATH_MAX
];
6100 if (t
->icon_request
) {
6101 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6105 /* check to see if we got the icon in cache */
6106 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6107 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6110 if (!stat(file
, &sb
)) {
6111 if (sb
.st_size
> 0) {
6112 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6114 set_favicon_from_file(t
, file
);
6118 /* corrupt icon so trash it */
6119 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6124 /* create download for icon */
6125 t
->icon_request
= webkit_network_request_new(uri
);
6126 if (t
->icon_request
== NULL
) {
6127 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6132 t
->icon_download
= webkit_download_new(t
->icon_request
);
6133 if (t
->icon_download
== NULL
)
6136 /* we have to free icon_dest_uri later */
6137 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6138 webkit_download_set_destination_uri(t
->icon_download
,
6141 if (webkit_download_get_status(t
->icon_download
) ==
6142 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6143 g_object_unref(t
->icon_request
);
6144 g_free(t
->icon_dest_uri
);
6145 t
->icon_request
= NULL
;
6146 t
->icon_dest_uri
= NULL
;
6150 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6151 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6153 webkit_download_start(t
->icon_download
);
6158 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6160 const gchar
*uri
= NULL
, *title
= NULL
;
6161 struct history
*h
, find
;
6164 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6165 webkit_web_view_get_load_status(wview
),
6166 get_uri(t
) ? get_uri(t
) : "NOTHING");
6169 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6173 switch (webkit_web_view_get_load_status(wview
)) {
6174 case WEBKIT_LOAD_PROVISIONAL
:
6176 abort_favicon_download(t
);
6177 #if GTK_CHECK_VERSION(2, 20, 0)
6178 gtk_widget_show(t
->spinner
);
6179 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6181 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6183 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6185 /* take focus if we are visible */
6191 case WEBKIT_LOAD_COMMITTED
:
6196 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6202 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6204 /* check if js white listing is enabled */
6205 if (enable_js_whitelist
) {
6206 check_and_set_js(uri
, t
);
6212 show_ca_status(t
, uri
);
6214 /* we know enough to autosave the session */
6215 if (session_autosave
) {
6221 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6225 case WEBKIT_LOAD_FINISHED
:
6231 if (!strncmp(uri
, "http://", strlen("http://")) ||
6232 !strncmp(uri
, "https://", strlen("https://")) ||
6233 !strncmp(uri
, "file://", strlen("file://"))) {
6235 h
= RB_FIND(history_list
, &hl
, &find
);
6237 title
= get_title(t
, FALSE
);
6238 h
= g_malloc(sizeof *h
);
6239 h
->uri
= g_strdup(uri
);
6240 h
->title
= g_strdup(title
);
6241 RB_INSERT(history_list
, &hl
, h
);
6242 completion_add_uri(h
->uri
);
6243 update_history_tabs(NULL
);
6247 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6248 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6249 case WEBKIT_LOAD_FAILED
:
6252 #if GTK_CHECK_VERSION(2, 20, 0)
6253 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6254 gtk_widget_hide(t
->spinner
);
6257 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6262 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6264 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6265 webkit_web_view_can_go_back(wview
));
6267 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6268 webkit_web_view_can_go_forward(wview
));
6272 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6273 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6277 t
->tmp_uri
= g_strdup(uri
);
6278 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6279 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6280 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6281 set_status(t
, uri
, XT_STATUS_NOTHING
);
6287 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6289 const gchar
*title
= NULL
, *win_title
= NULL
;
6291 title
= get_title(t
, FALSE
);
6292 win_title
= get_title(t
, TRUE
);
6293 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6294 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6295 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6296 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6300 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6302 run_script(t
, JS_HINTING
);
6306 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6308 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6309 progress
== 100 ? 0 : (double)progress
/ 100);
6310 if (show_url
== 0) {
6311 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6312 progress
== 100 ? 0 : (double)progress
/ 100);
6315 update_statusbar_position(NULL
, NULL
);
6319 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6320 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6321 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6324 WebKitWebNavigationReason reason
;
6325 struct domain
*d
= NULL
;
6328 show_oops(NULL
, "webview_npd_cb invalid parameters");
6332 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6334 webkit_network_request_get_uri(request
));
6336 uri
= (char *)webkit_network_request_get_uri(request
);
6338 /* if this is an xtp url, we don't load anything else */
6339 if (parse_xtp_url(t
, uri
))
6342 if (t
->ctrl_click
) {
6344 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6345 webkit_web_policy_decision_ignore(pd
);
6346 return (TRUE
); /* we made the decission */
6350 * This is a little hairy but it comes down to this:
6351 * when we run in whitelist mode we have to assist the browser in
6352 * opening the URL that it would have opened in a new tab.
6354 reason
= webkit_web_navigation_action_get_reason(na
);
6355 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6356 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6357 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6358 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6360 webkit_web_policy_decision_use(pd
);
6361 return (TRUE
); /* we made the decision */
6368 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6371 struct domain
*d
= NULL
;
6373 WebKitWebView
*webview
= NULL
;
6375 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6376 webkit_web_view_get_uri(wv
));
6379 /* open in current tab */
6381 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6382 uri
= webkit_web_view_get_uri(wv
);
6383 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6386 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6388 } else if (enable_scripts
== 1) {
6389 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6397 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6400 struct domain
*d
= NULL
;
6402 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6404 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6405 uri
= webkit_web_view_get_uri(wv
);
6406 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6410 } else if (enable_scripts
== 1)
6417 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6419 /* we can not eat the event without throwing gtk off so defer it */
6421 /* catch middle click */
6422 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6427 /* catch ctrl click */
6428 if (e
->type
== GDK_BUTTON_RELEASE
&&
6429 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6434 return (XT_CB_PASSTHROUGH
);
6438 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6440 struct mime_type
*m
;
6442 m
= find_mime_type(mime_type
);
6450 show_oops(t
, "can't fork mime handler");
6460 execlp(m
->mt_action
, m
->mt_action
,
6461 webkit_network_request_get_uri(request
), (void *)NULL
);
6470 get_mime_type(char *file
)
6472 const char *mime_type
;
6476 if (g_str_has_prefix(file
, "file://"))
6477 file
+= strlen("file://");
6479 gf
= g_file_new_for_path(file
);
6480 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6482 mime_type
= g_file_info_get_content_type(fi
);
6490 run_download_mimehandler(char *mime_type
, char *file
)
6492 struct mime_type
*m
;
6494 m
= find_mime_type(mime_type
);
6500 show_oops(NULL
, "can't fork download mime handler");
6510 if (g_str_has_prefix(file
, "file://"))
6511 file
+= strlen("file://");
6512 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6521 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6524 WebKitDownloadStatus status
;
6525 const gchar
*file
= NULL
, *mime
= NULL
;
6527 if (download
== NULL
)
6529 status
= webkit_download_get_status(download
);
6530 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6533 file
= webkit_download_get_destination_uri(download
);
6536 mime
= get_mime_type((char *)file
);
6540 run_download_mimehandler((char *)mime
, (char *)file
);
6544 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6545 WebKitNetworkRequest
*request
, char *mime_type
,
6546 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6549 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6553 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6554 t
->tab_id
, mime_type
);
6556 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6557 webkit_web_policy_decision_ignore(decision
);
6562 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6563 webkit_web_policy_decision_download(decision
);
6571 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6575 const gchar
*suggested_name
;
6576 gchar
*filename
= NULL
;
6578 struct download
*download_entry
;
6581 if (wk_download
== NULL
|| t
== NULL
) {
6582 show_oops(NULL
, "%s invalid parameters", __func__
);
6586 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6587 if (suggested_name
== NULL
)
6588 return (FALSE
); /* abort download */
6599 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6601 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6602 filename
: suggested_name
);
6604 } while (!stat(uri
+ strlen("file://"), &sb
));
6606 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6607 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6609 webkit_download_set_destination_uri(wk_download
, uri
);
6611 if (webkit_download_get_status(wk_download
) ==
6612 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6613 show_oops(t
, "%s: download failed to start", __func__
);
6615 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6617 /* connect "download first" mime handler */
6618 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6619 G_CALLBACK(download_status_changed_cb
), NULL
);
6621 download_entry
= g_malloc(sizeof(struct download
));
6622 download_entry
->download
= wk_download
;
6623 download_entry
->tab
= t
;
6624 download_entry
->id
= next_download_id
++;
6625 RB_INSERT(download_list
, &downloads
, download_entry
);
6626 /* get from history */
6627 g_object_ref(wk_download
);
6628 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6629 show_oops(t
, "Download of '%s' started...",
6630 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6639 /* sync other download manager tabs */
6640 update_download_tabs(NULL
);
6643 * NOTE: never redirect/render the current tab before this
6644 * function returns. This will cause the download to never start.
6646 return (ret
); /* start download */
6650 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6652 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6655 show_oops(NULL
, "webview_hover_cb");
6660 set_status(t
, uri
, XT_STATUS_LINK
);
6663 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6668 mark(struct tab
*t
, struct karg
*arg
)
6674 if ((index
= marktoindex(mark
)) == -1)
6677 if (arg
->i
== XT_MARK_SET
)
6678 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6679 else if (arg
->i
== XT_MARK_GOTO
) {
6680 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6681 show_oops(t
, "mark '%c' does not exist", mark
);
6684 /* XXX t->mark[index] can be bigger than the maximum if ajax or
6685 something changes the document size */
6686 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
6693 marks_clear(struct tab
*t
)
6697 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
6698 t
->mark
[i
] = XT_INVALID_MARK
;
6704 char file
[PATH_MAX
];
6705 char *line
= NULL
, *p
, mark
;
6710 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6711 if ((f
= fopen(file
, "r+")) == NULL
) {
6712 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6716 for (i
= 0; ; i
++) {
6717 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
6718 if (feof(f
) || ferror(f
))
6720 p
= strtok(line
, " \t");
6722 if (p
== NULL
|| strlen(p
) != 1 || (index
= marktoindex(*p
)) == -1) {
6723 warnx("corrupt quickmarks file, line %d", i
);
6728 p
= strtok(NULL
, " \t");
6729 if (qmarks
[index
] != NULL
)
6730 g_free(qmarks
[index
]);
6731 qmarks
[index
] = g_strdup(p
);
6742 char file
[PATH_MAX
];
6746 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6747 if ((f
= fopen(file
, "r+")) == NULL
) {
6748 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6752 for (i
= 0; i
< XT_NOMARKS
; i
++)
6753 if (qmarks
[i
] != NULL
)
6754 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
6762 qmark(struct tab
*t
, struct karg
*arg
)
6767 mark
= arg
->s
[strlen(arg
->s
)-1];
6768 index
= marktoindex(mark
);
6774 if (qmarks
[index
] != NULL
)
6775 g_free(qmarks
[index
]);
6777 qmarks_load(); /* sync if multiple instances */
6778 qmarks
[index
] = g_strdup(get_uri(t
));
6782 if (qmarks
[index
] != NULL
)
6783 load_uri(t
, qmarks
[index
]);
6788 if (qmarks
[index
] != NULL
)
6789 create_new_tab(qmarks
[index
], NULL
, 1, -1);
6799 go_up(struct tab
*t
, struct karg
*args
)
6805 levels
= atoi(args
->s
);
6809 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
6810 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
6812 tmp
+= strlen(XT_PROTO_DELIM
);
6814 /* if an uri starts with a slash, leave it alone (for file:///) */
6821 p
= strrchr(tmp
, '/');
6835 gototab(struct tab
*t
, struct karg
*args
)
6838 struct karg arg
= {0, NULL
, -1};
6840 tab
= atoi(args
->s
);
6842 arg
.i
= XT_TAB_NEXT
;
6851 zoom_amount(struct tab
*t
, struct karg
*arg
)
6853 struct karg narg
= {0, NULL
, -1};
6855 narg
.i
= atoi(arg
->s
);
6856 resizetab(t
, &narg
);
6861 /* buffer commands receive the regex that triggered them in arg.s */
6865 int (*func
)(struct tab
*, struct karg
*);
6869 { "^[0-9]*gu$", go_up
, 0 },
6870 { "^gg$", move
, XT_MOVE_TOP
},
6871 { "^gG$", move
, XT_MOVE_BOTTOM
},
6872 { "^[0-9]+%$", move
, XT_MOVE_PERCENT
},
6873 { "^gh$", go_home
, 0 },
6874 { "^m[a-zA-Z0-9]$", mark
, XT_MARK_SET
},
6875 { "^[`'][a-zA-Z0-9]$", mark
, XT_MARK_GOTO
},
6876 { "^[0-9]+t$", gototab
, 0 },
6877 { "^M[a-zA-Z0-9]$", qmark
, XT_QMARK_SET
},
6878 { "^go[a-zA-Z0-9]$", qmark
, XT_QMARK_OPEN
},
6879 { "^gn[a-zA-Z0-9]$", qmark
, XT_QMARK_TAB
},
6880 { "^ZR$", restart
, 0 },
6881 { "^ZZ$", quit
, 0 },
6882 { "^zi$", resizetab
, XT_ZOOM_IN
},
6883 { "^zo$", resizetab
, XT_ZOOM_OUT
},
6884 { "^z0$", resizetab
, XT_ZOOM_NORMAL
},
6885 { "^[0-9]+Z$", zoom_amount
, 0 },
6893 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
6894 regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
6899 buffercmd_abort(struct tab
*t
)
6903 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
6904 for (i
= 0; i
< LENGTH(bcmd
); i
++)
6907 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
6908 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
6912 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
6914 struct karg arg
= {0, NULL
, -1};
6917 arg
.s
= g_strdup(bcmd
);
6919 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
6920 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
6930 buffercmd_addkey(struct tab
*t
, guint keyval
)
6934 if (keyval
== GDK_Escape
) {
6936 return (XT_CB_HANDLED
);
6939 /* key with modifier or non-ascii character */
6940 if (!isascii(keyval
))
6941 return (XT_CB_PASSTHROUGH
);
6943 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
6944 "to buffer \"%s\"\n", keyval
, bcmd
);
6946 for (i
= 0; i
< LENGTH(bcmd
); i
++)
6947 if (bcmd
[i
] == '\0') {
6952 /* buffer full, ignore input */
6953 if (i
== LENGTH(bcmd
)) {
6954 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
6955 return (XT_CB_HANDLED
);
6958 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
6960 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
6961 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
6962 (size_t) 0, NULL
, 0) == 0) {
6963 buffercmd_execute(t
, &buffercmds
[i
]);
6967 return (XT_CB_HANDLED
);
6971 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6973 struct key_binding
*k
;
6975 /* handle keybindings if buffercmd is empty.
6976 if not empty, allow commands like C-n */
6977 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
6978 TAILQ_FOREACH(k
, &kbl
, entry
)
6979 if (e
->keyval
== k
->key
6980 && (entry
? k
->use_in_entry
: 1)) {
6982 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6983 return (cmd_execute(t
, k
->cmd
));
6984 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6985 return (cmd_execute(t
, k
->cmd
));
6989 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
6990 return buffercmd_addkey(t
, e
->keyval
);
6992 return (XT_CB_PASSTHROUGH
);
6996 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6998 char s
[2], buf
[128];
6999 const char *errstr
= NULL
;
7001 /* don't use w directly; use t->whatever instead */
7004 show_oops(NULL
, "wv_keypress_after_cb");
7005 return (XT_CB_PASSTHROUGH
);
7008 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7009 e
->keyval
, e
->state
, t
);
7013 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7015 return (XT_CB_HANDLED
);
7019 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7021 /* we have a string */
7023 /* we have a number */
7024 snprintf(buf
, sizeof buf
,
7025 "vimprobable_fire(%s)", t
->hint_num
);
7032 /* XXX unfuck this */
7033 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7034 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7035 /* last input was numerical */
7037 l
= strlen(t
->hint_num
);
7044 t
->hint_num
[l
] = '\0';
7048 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7049 /* last input was alphanumerical */
7051 l
= strlen(t
->hint_buf
);
7058 t
->hint_buf
[l
] = '\0';
7068 /* numerical input */
7069 if (CLEAN(e
->state
) == 0 &&
7070 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7071 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7072 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7073 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7074 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7078 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7079 "invalid link number\n");
7082 snprintf(buf
, sizeof buf
,
7083 "vimprobable_update_hints(%s)",
7085 t
->hint_mode
= XT_HINT_NUMERICAL
;
7089 /* empty the counter buffer */
7090 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7091 return (XT_CB_HANDLED
);
7094 /* alphanumerical input */
7095 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7096 e
->keyval
<= GDK_z
) ||
7097 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7098 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7099 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7100 e
->keyval
<= GDK_9
) ||
7101 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7102 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7103 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7104 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7105 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7106 " %s\n", t
->hint_buf
);
7108 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7111 snprintf(buf
, sizeof buf
,
7112 "vimprobable_show_hints('%s')", t
->hint_buf
);
7113 t
->hint_mode
= XT_HINT_ALPHANUM
;
7116 /* empty the counter buffer */
7117 bzero(t
->hint_num
, sizeof t
->hint_num
);
7118 return (XT_CB_HANDLED
);
7121 return (XT_CB_HANDLED
);
7124 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7125 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7126 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7129 return (handle_keypress(t
, e
, 0));
7133 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7137 /* Hide buffers, if they are visible, with escape. */
7138 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7139 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7140 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7142 return (XT_CB_HANDLED
);
7145 return (XT_CB_PASSTHROUGH
);
7149 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7151 const gchar
*c
= gtk_entry_get_text(w
);
7155 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7156 e
->keyval
, e
->state
, t
);
7159 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7160 return (XT_CB_PASSTHROUGH
);
7163 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7164 e
->keyval
, e
->state
, t
);
7168 if (strlen(c
) == 1) {
7169 webkit_web_view_unmark_text_matches(t
->wv
);
7175 else if (c
[0] == '?')
7181 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
7183 /* not found, mark red */
7184 gdk_color_parse(XT_COLOR_RED
, &color
);
7185 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7186 /* unmark and remove selection */
7187 webkit_web_view_unmark_text_matches(t
->wv
);
7188 /* my kingdom for a way to unselect text in webview */
7190 /* found, highlight all */
7191 webkit_web_view_unmark_text_matches(t
->wv
);
7192 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7193 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7194 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7195 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7198 return (XT_CB_PASSTHROUGH
);
7202 match_uri(const gchar
*uri
, const gchar
*key
) {
7205 gboolean match
= FALSE
;
7209 if (!strncmp(key
, uri
, len
))
7212 voffset
= strstr(uri
, "/") + 2;
7213 if (!strncmp(key
, voffset
, len
))
7215 else if (g_str_has_prefix(voffset
, "www.")) {
7216 voffset
= voffset
+ strlen("www.");
7217 if (!strncmp(key
, voffset
, len
))
7226 cmd_getlist(int id
, char *key
)
7231 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7232 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7233 if (match_uri(h
->uri
, key
)) {
7234 cmd_status
.list
[c
] = (char *)h
->uri
;
7243 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7245 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7246 if (cmds
[i
].level
< dep
)
7248 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7250 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7258 cmd_getnext(int dir
)
7260 cmd_status
.index
+= dir
;
7262 if (cmd_status
.index
< 0)
7263 cmd_status
.index
= cmd_status
.len
- 1;
7264 else if (cmd_status
.index
>= cmd_status
.len
)
7265 cmd_status
.index
= 0;
7267 return cmd_status
.list
[cmd_status
.index
];
7271 cmd_tokenize(char *s
, char *tokens
[])
7275 size_t len
= strlen(s
);
7278 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7279 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7280 tok
= strtok_r(NULL
, " ", &last
), i
++)
7290 cmd_complete(struct tab
*t
, char *str
, int dir
)
7292 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7293 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7295 char *tok
, *match
, *s
= g_strdup(str
);
7297 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7300 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7303 for (i
= 0; isdigit(s
[i
]); i
++)
7306 for (; isspace(s
[i
]); i
++)
7311 levels
= cmd_tokenize(s
, tokens
);
7313 for (i
= 0; i
< levels
- 1; i
++) {
7316 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7317 if (cmds
[j
].level
< dep
)
7319 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7323 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7330 if (matchcount
== 1) {
7331 strlcat(res
, tok
, sizeof res
);
7332 strlcat(res
, " ", sizeof res
);
7342 if (cmd_status
.index
== -1)
7343 cmd_getlist(parent
, tokens
[i
]);
7345 if (cmd_status
.len
> 0) {
7346 match
= cmd_getnext(dir
);
7347 strlcat(res
, match
, sizeof res
);
7348 gtk_entry_set_text(w
, res
);
7349 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7356 cmd_execute(struct tab
*t
, char *str
)
7358 struct cmd
*cmd
= NULL
;
7359 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7361 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7362 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7363 struct karg arg
= {0, NULL
, -1};
7368 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7374 while (isspace(s
[0]))
7377 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7378 prefix
= atoi(prefixstr
);
7382 for (tok
= strtok_r(s
, " ", &last
); tok
;
7383 tok
= strtok_r(NULL
, " ", &last
)) {
7385 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7386 if (cmds
[j
].level
< dep
)
7388 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7390 if (cmds
[j
].level
== dep
&&
7391 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7395 if (len
== strlen(cmds
[j
].cmd
)) {
7401 if (matchcount
== 1) {
7406 show_oops(t
, "Invalid command: %s", str
);
7415 else if (cmd_prefix
> 0)
7418 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
7419 show_oops(t
, "No prefix allowed: %s", str
);
7423 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7424 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7425 arg
.p
= atoi(arg
.s
);
7428 show_oops(t
, "Zero count");
7430 show_oops(t
, "Trailing characters");
7435 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
7451 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7454 show_oops(NULL
, "entry_key_cb invalid parameters");
7455 return (XT_CB_PASSTHROUGH
);
7458 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7459 e
->keyval
, e
->state
, t
);
7463 if (e
->keyval
== GDK_Escape
) {
7464 /* don't use focus_webview(t) because we want to type :cmds */
7465 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7468 return (handle_keypress(t
, e
, 1));
7472 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7474 int rv
= XT_CB_HANDLED
;
7475 const gchar
*c
= gtk_entry_get_text(w
);
7478 show_oops(NULL
, "cmd_keypress_cb parameters");
7479 return (XT_CB_PASSTHROUGH
);
7482 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7483 e
->keyval
, e
->state
, t
);
7487 e
->keyval
= GDK_Escape
;
7488 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7489 e
->keyval
= GDK_Escape
;
7491 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7492 e
->keyval
!= GDK_ISO_Left_Tab
)
7493 cmd_status
.index
= -1;
7495 switch (e
->keyval
) {
7498 cmd_complete(t
, (char *)&c
[1], 1);
7500 case GDK_ISO_Left_Tab
:
7502 cmd_complete(t
, (char *)&c
[1], -1);
7506 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7514 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7515 webkit_web_view_unmark_text_matches(t
->wv
);
7519 rv
= XT_CB_PASSTHROUGH
;
7525 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7528 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7529 return (XT_CB_PASSTHROUGH
);
7531 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7536 if (show_url
== 0 || t
->focus_wv
)
7539 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7541 return (XT_CB_PASSTHROUGH
);
7545 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7548 const gchar
*c
= gtk_entry_get_text(entry
);
7551 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7555 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7562 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7568 if (c
[0] == '/' || c
[0] == '?') {
7569 if (t
->search_text
) {
7570 g_free(t
->search_text
);
7571 t
->search_text
= NULL
;
7574 t
->search_text
= g_strdup(s
);
7576 g_free(global_search
);
7577 global_search
= g_strdup(s
);
7578 t
->search_forward
= c
[0] == '/';
7590 backward_cb(GtkWidget
*w
, struct tab
*t
)
7595 show_oops(NULL
, "backward_cb invalid parameters");
7599 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7606 forward_cb(GtkWidget
*w
, struct tab
*t
)
7611 show_oops(NULL
, "forward_cb invalid parameters");
7615 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7617 a
.i
= XT_NAV_FORWARD
;
7622 home_cb(GtkWidget
*w
, struct tab
*t
)
7625 show_oops(NULL
, "home_cb invalid parameters");
7629 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7635 stop_cb(GtkWidget
*w
, struct tab
*t
)
7637 WebKitWebFrame
*frame
;
7640 show_oops(NULL
, "stop_cb invalid parameters");
7644 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7646 frame
= webkit_web_view_get_main_frame(t
->wv
);
7647 if (frame
== NULL
) {
7648 show_oops(t
, "stop_cb: no frame");
7652 webkit_web_frame_stop_loading(frame
);
7653 abort_favicon_download(t
);
7657 setup_webkit(struct tab
*t
)
7659 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7660 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7661 FALSE
, (char *)NULL
);
7663 warnx("webkit does not have \"enable-dns-prefetching\" property");
7664 g_object_set(G_OBJECT(t
->settings
),
7665 "user-agent", t
->user_agent
, (char *)NULL
);
7666 g_object_set(G_OBJECT(t
->settings
),
7667 "enable-scripts", enable_scripts
, (char *)NULL
);
7668 g_object_set(G_OBJECT(t
->settings
),
7669 "enable-plugins", enable_plugins
, (char *)NULL
);
7670 g_object_set(G_OBJECT(t
->settings
),
7671 "javascript-can-open-windows-automatically", enable_scripts
,
7673 g_object_set(G_OBJECT(t
->settings
),
7674 "enable-html5-database", FALSE
, (char *)NULL
);
7675 g_object_set(G_OBJECT(t
->settings
),
7676 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7677 g_object_set(G_OBJECT(t
->settings
),
7678 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7679 g_object_set(G_OBJECT(t
->settings
),
7680 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7681 g_object_set(G_OBJECT(t
->wv
),
7682 "full-content-zoom", TRUE
, (char *)NULL
);
7684 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7688 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
7690 struct tab
*ti
, *t
= NULL
;
7691 gdouble view_size
, value
, max
;
7694 TAILQ_FOREACH(ti
, &tabs
, entry
)
7695 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
7703 if (adjustment
== NULL
)
7704 adjustment
= gtk_scrolled_window_get_vadjustment(
7705 GTK_SCROLLED_WINDOW(t
->browser_win
));
7707 view_size
= gtk_adjustment_get_page_size(adjustment
);
7708 value
= gtk_adjustment_get_value(adjustment
);
7709 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
7712 position
= g_strdup("All");
7713 else if (value
== max
)
7714 position
= g_strdup("Bot");
7715 else if (value
== 0)
7716 position
= g_strdup("Top");
7718 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
7720 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
7727 create_browser(struct tab
*t
)
7731 GtkAdjustment
*adjustment
;
7734 show_oops(NULL
, "create_browser invalid parameters");
7738 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7739 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7740 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7741 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7743 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7744 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7745 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7747 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7748 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7751 t
->settings
= webkit_web_settings_new();
7753 if (user_agent
== NULL
) {
7754 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7756 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7759 t
->user_agent
= g_strdup(user_agent
);
7761 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7764 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
7765 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
7766 G_CALLBACK(update_statusbar_position
), NULL
);
7778 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7779 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7780 gtk_widget_set_name(w
, "xxxterm");
7781 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7782 g_signal_connect(G_OBJECT(w
), "delete_event",
7783 G_CALLBACK (gtk_main_quit
), NULL
);
7789 create_kiosk_toolbar(struct tab
*t
)
7791 GtkWidget
*toolbar
= NULL
, *b
;
7793 b
= gtk_hbox_new(FALSE
, 0);
7795 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7797 /* backward button */
7798 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7799 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7800 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7801 G_CALLBACK(backward_cb
), t
);
7802 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7804 /* forward button */
7805 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7806 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7807 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7808 G_CALLBACK(forward_cb
), t
);
7809 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7812 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7813 gtk_widget_set_sensitive(t
->gohome
, true);
7814 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7815 G_CALLBACK(home_cb
), t
);
7816 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7818 /* create widgets but don't use them */
7819 t
->uri_entry
= gtk_entry_new();
7820 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7821 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7822 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7828 create_toolbar(struct tab
*t
)
7830 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7832 b
= gtk_hbox_new(FALSE
, 0);
7834 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7837 /* backward button */
7838 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7839 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7840 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7841 G_CALLBACK(backward_cb
), t
);
7842 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7844 /* forward button */
7845 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7846 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7847 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7848 G_CALLBACK(forward_cb
), t
);
7849 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7853 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7854 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7855 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7856 G_CALLBACK(stop_cb
), t
);
7857 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7861 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7862 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7863 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7864 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7865 G_CALLBACK(js_toggle_cb
), t
);
7866 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7869 t
->uri_entry
= gtk_entry_new();
7870 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7871 G_CALLBACK(activate_uri_entry_cb
), t
);
7872 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7873 G_CALLBACK(entry_key_cb
), t
);
7875 eb1
= gtk_hbox_new(FALSE
, 0);
7876 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7877 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7878 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7881 if (fancy_bar
&& search_string
) {
7883 t
->search_entry
= gtk_entry_new();
7884 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7885 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7886 G_CALLBACK(activate_search_entry_cb
), t
);
7887 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7888 G_CALLBACK(entry_key_cb
), t
);
7889 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7890 eb2
= gtk_hbox_new(FALSE
, 0);
7891 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7892 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7894 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7900 create_buffers(struct tab
*t
)
7902 GtkCellRenderer
*renderer
;
7905 view
= gtk_tree_view_new();
7907 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7909 renderer
= gtk_cell_renderer_text_new();
7910 gtk_tree_view_insert_column_with_attributes
7911 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7913 renderer
= gtk_cell_renderer_text_new();
7914 gtk_tree_view_insert_column_with_attributes
7915 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
7918 gtk_tree_view_set_model
7919 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7925 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7926 GtkTreeViewColumn
*col
, struct tab
*t
)
7931 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7933 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
7936 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7937 set_current_tab(id
- 1);
7943 /* after tab reordering/creation/removal */
7950 TAILQ_FOREACH(t
, &tabs
, entry
) {
7951 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7952 if (t
->tab_id
> maxid
)
7955 gtk_widget_show(t
->tab_elems
.sep
);
7958 TAILQ_FOREACH(t
, &tabs
, entry
) {
7959 if (t
->tab_id
== maxid
) {
7960 gtk_widget_hide(t
->tab_elems
.sep
);
7966 /* after active tab change */
7968 recolor_compact_tabs(void)
7974 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7975 TAILQ_FOREACH(t
, &tabs
, entry
)
7976 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
7979 curid
= gtk_notebook_get_current_page(notebook
);
7980 TAILQ_FOREACH(t
, &tabs
, entry
)
7981 if (t
->tab_id
== curid
) {
7982 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
7983 gtk_widget_modify_fg(t
->tab_elems
.label
,
7984 GTK_STATE_NORMAL
, &color
);
7990 set_current_tab(int page_num
)
7992 buffercmd_abort(get_current_tab());
7993 gtk_notebook_set_current_page(notebook
, page_num
);
7994 recolor_compact_tabs();
7998 undo_close_tab_save(struct tab
*t
)
8002 struct undo
*u1
, *u2
;
8004 WebKitWebHistoryItem
*item
;
8006 if ((uri
= get_uri(t
)) == NULL
)
8009 u1
= g_malloc0(sizeof(struct undo
));
8010 u1
->uri
= g_strdup(uri
);
8012 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8014 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8015 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8018 /* forward history */
8019 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8023 u1
->history
= g_list_prepend(u1
->history
,
8024 webkit_web_history_item_copy(item
));
8025 items
= g_list_next(items
);
8030 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8031 u1
->history
= g_list_prepend(u1
->history
,
8032 webkit_web_history_item_copy(item
));
8036 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8040 u1
->history
= g_list_prepend(u1
->history
,
8041 webkit_web_history_item_copy(item
));
8042 items
= g_list_next(items
);
8045 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8047 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8048 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8049 TAILQ_REMOVE(&undos
, u2
, entry
);
8051 g_list_free(u2
->history
);
8060 delete_tab(struct tab
*t
)
8064 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8069 TAILQ_REMOVE(&tabs
, t
, entry
);
8071 /* Halt all webkit activity. */
8072 abort_favicon_download(t
);
8073 webkit_web_view_stop_loading(t
->wv
);
8075 /* Save the tab, so we can undo the close. */
8076 undo_close_tab_save(t
);
8078 if (browser_mode
== XT_BM_KIOSK
) {
8079 gtk_widget_destroy(t
->uri_entry
);
8080 gtk_widget_destroy(t
->stop
);
8081 gtk_widget_destroy(t
->js_toggle
);
8084 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8085 gtk_widget_destroy(t
->vbox
);
8087 g_free(t
->user_agent
);
8088 g_free(t
->stylesheet
);
8092 if (TAILQ_EMPTY(&tabs
)) {
8093 if (browser_mode
== XT_BM_KIOSK
)
8094 create_new_tab(home
, NULL
, 1, -1);
8096 create_new_tab(NULL
, NULL
, 1, -1);
8099 /* recreate session */
8100 if (session_autosave
) {
8106 recolor_compact_tabs();
8110 update_statusbar_zoom(struct tab
*t
)
8113 char s
[16] = { '\0' };
8115 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8116 if ((zoom
<= 0.99 || zoom
>= 1.01))
8117 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8118 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8122 setzoom_webkit(struct tab
*t
, int adjust
)
8124 #define XT_ZOOMPERCENT 0.04
8129 show_oops(NULL
, "setzoom_webkit invalid parameters");
8133 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8134 if (adjust
== XT_ZOOM_IN
)
8135 zoom
+= XT_ZOOMPERCENT
;
8136 else if (adjust
== XT_ZOOM_OUT
)
8137 zoom
-= XT_ZOOMPERCENT
;
8138 else if (adjust
> 0)
8139 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8141 show_oops(t
, "setzoom_webkit invalid zoom value");
8145 if (zoom
< XT_ZOOMPERCENT
)
8146 zoom
= XT_ZOOMPERCENT
;
8147 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8148 update_statusbar_zoom(t
);
8152 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8154 struct tab
*t
= (struct tab
*) data
;
8156 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8158 switch (event
->button
) {
8160 set_current_tab(t
->tab_id
);
8171 append_tab(struct tab
*t
)
8176 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8177 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8181 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8186 WebKitWebHistoryItem
*item
;
8190 int sbe_p
= 0, sbe_b
= 0,
8193 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8195 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8196 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8200 t
= g_malloc0(sizeof *t
);
8202 if (title
== NULL
) {
8203 title
= "(untitled)";
8207 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8209 /* label + button for tab */
8210 b
= gtk_hbox_new(FALSE
, 0);
8213 #if GTK_CHECK_VERSION(2, 20, 0)
8214 t
->spinner
= gtk_spinner_new();
8216 t
->label
= gtk_label_new(title
);
8217 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8218 gtk_widget_set_size_request(t
->label
, 100, 0);
8219 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8220 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8221 gtk_widget_set_size_request(b
, 130, 0);
8223 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8224 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8225 #if GTK_CHECK_VERSION(2, 20, 0)
8226 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8230 if (browser_mode
== XT_BM_KIOSK
)
8231 t
->toolbar
= create_kiosk_toolbar(t
);
8233 t
->toolbar
= create_toolbar(t
);
8235 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
8241 t
->browser_win
= create_browser(t
);
8242 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8244 /* oops message for user feedback */
8245 t
->oops
= gtk_entry_new();
8246 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8247 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8248 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8249 gdk_color_parse(XT_COLOR_RED
, &color
);
8250 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8251 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8252 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8255 t
->cmd
= gtk_entry_new();
8256 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8257 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8258 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8259 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8262 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8264 t
->sbe
.statusbar
= gtk_entry_new();
8265 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8266 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8267 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8268 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8270 /* XXX create these widgets only if specified in statusbar_elems */
8271 t
->sbe
.position
= gtk_entry_new();
8272 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.position
), NULL
);
8273 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.position
), FALSE
);
8274 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.position
), FALSE
);
8275 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.position
), statusbar_font
);
8276 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.position
), 1.0);
8277 gtk_widget_set_size_request(t
->sbe
.position
, 40, -1);
8279 t
->sbe
.zoom
= gtk_entry_new();
8280 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.zoom
), NULL
);
8281 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.zoom
), FALSE
);
8282 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.zoom
), FALSE
);
8283 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.zoom
), statusbar_font
);
8284 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.zoom
), 1.0);
8285 gtk_widget_set_size_request(t
->sbe
.zoom
, 40, -1);
8287 t
->sbe
.buffercmd
= gtk_entry_new();
8288 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.buffercmd
), NULL
);
8289 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.buffercmd
), FALSE
);
8290 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.buffercmd
), FALSE
);
8291 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.buffercmd
), statusbar_font
);
8292 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.buffercmd
), 1.0);
8293 gtk_widget_set_size_request(t
->sbe
.buffercmd
, 60, -1);
8295 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8297 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8300 /* gtk widgets cannot be added to a box twice. sbe_* variables
8301 make sure of this */
8302 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8306 GtkWidget
*sep
= gtk_vseparator_new();
8308 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8309 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8310 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8311 FALSE
, FALSE
, FALSE
);
8316 warnx("flag \"%c\" specified more than "
8317 "once in statusbar_elems\n", *p
);
8321 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8322 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8326 warnx("flag \"%c\" specified more than "
8327 "once in statusbar_elems\n", *p
);
8331 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8332 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8336 warnx("flag \"%c\" specified more than "
8337 "once in statusbar_elems\n", *p
);
8341 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8342 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8345 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8350 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8353 t
->buffers
= create_buffers(t
);
8354 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8356 /* xtp meaning is normal by default */
8357 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8359 /* set empty favicon */
8360 xt_icon_from_name(t
, "text-html");
8362 /* and show it all */
8363 gtk_widget_show_all(b
);
8364 gtk_widget_show_all(t
->vbox
);
8366 /* compact tab bar */
8367 t
->tab_elems
.label
= gtk_label_new(title
);
8368 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8369 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8370 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8371 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8373 t
->tab_elems
.eventbox
= gtk_event_box_new();
8374 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8375 t
->tab_elems
.sep
= gtk_vseparator_new();
8377 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8378 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8379 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8380 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8381 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8382 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8384 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8386 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8388 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8391 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8393 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8395 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8398 id
= position
>= 0 ? position
:
8399 gtk_notebook_get_current_page(notebook
) + 1;
8400 if (id
> gtk_notebook_get_n_pages(notebook
))
8403 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8404 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8405 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8406 t
->tab_elems
.eventbox
, id
);
8411 #if GTK_CHECK_VERSION(2, 20, 0)
8412 /* turn spinner off if we are a new tab without uri */
8414 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8415 gtk_widget_hide(t
->spinner
);
8418 /* make notebook tabs reorderable */
8419 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8421 /* compact tabs clickable */
8422 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8423 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8425 g_object_connect(G_OBJECT(t
->cmd
),
8426 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8427 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8428 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8429 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8432 /* reuse wv_button_cb to hide oops */
8433 g_object_connect(G_OBJECT(t
->oops
),
8434 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8437 g_signal_connect(t
->buffers
,
8438 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8439 g_object_connect(G_OBJECT(t
->buffers
),
8440 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8442 g_object_connect(G_OBJECT(t
->wv
),
8443 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8444 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8445 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8446 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8447 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8448 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8449 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8450 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8451 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8452 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8453 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8454 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8455 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8456 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8457 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8459 g_signal_connect(t
->wv
,
8460 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8461 g_signal_connect(t
->wv
,
8462 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8463 g_signal_connect(t
->wv
,
8464 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8466 /* hijack the unused keys as if we were the browser */
8467 g_object_connect(G_OBJECT(t
->toolbar
),
8468 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8471 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8472 G_CALLBACK(tab_close_cb
), t
);
8478 url_set_visibility();
8479 statusbar_set_visibility();
8482 set_current_tab(t
->tab_id
);
8483 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8488 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8492 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8497 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8498 /* restore the tab's history */
8499 if (u
&& u
->history
) {
8503 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8504 items
= g_list_next(items
);
8507 item
= g_list_nth_data(u
->history
, u
->back
);
8509 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8512 g_list_free(u
->history
);
8514 webkit_web_back_forward_list_clear(t
->bfl
);
8516 recolor_compact_tabs();
8521 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8527 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8529 if (gtk_notebook_get_current_page(notebook
) == -1)
8532 TAILQ_FOREACH(t
, &tabs
, entry
) {
8533 if (t
->tab_id
== pn
) {
8534 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
8537 uri
= get_title(t
, TRUE
);
8538 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8544 /* can't use focus_webview here */
8545 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8552 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8555 struct tab
*t
= NULL
, *tt
;
8559 TAILQ_FOREACH(tt
, &tabs
, entry
)
8560 if (tt
->tab_id
== pn
) {
8565 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
8567 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
8572 menuitem_response(struct tab
*t
)
8574 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
8578 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
8580 GtkWidget
*menu
, *menu_items
;
8581 GdkEventButton
*bevent
;
8585 if (event
->type
== GDK_BUTTON_PRESS
) {
8586 bevent
= (GdkEventButton
*) event
;
8587 menu
= gtk_menu_new();
8589 TAILQ_FOREACH(ti
, &tabs
, entry
) {
8590 if ((uri
= get_uri(ti
)) == NULL
)
8591 /* XXX make sure there is something to print */
8592 /* XXX add gui pages in here to look purdy */
8594 menu_items
= gtk_menu_item_new_with_label(uri
);
8595 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
8596 gtk_widget_show(menu_items
);
8598 g_signal_connect_swapped((menu_items
),
8599 "activate", G_CALLBACK(menuitem_response
),
8603 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
8604 bevent
->button
, bevent
->time
);
8606 /* unref object so it'll free itself when popped down */
8607 #if !GTK_CHECK_VERSION(3, 0, 0)
8608 /* XXX does not need unref with gtk+3? */
8609 g_object_ref_sink(menu
);
8610 g_object_unref(menu
);
8613 return (TRUE
/* eat event */);
8616 return (FALSE
/* propagate */);
8620 icon_size_map(int icon_size
)
8622 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
8623 icon_size
> GTK_ICON_SIZE_DIALOG
)
8624 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
8630 create_button(char *name
, char *stockid
, int size
)
8632 GtkWidget
*button
, *image
;
8636 rcstring
= g_strdup_printf(
8637 "style \"%s-style\"\n"
8639 " GtkWidget::focus-padding = 0\n"
8640 " GtkWidget::focus-line-width = 0\n"
8644 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8645 gtk_rc_parse_string(rcstring
);
8647 button
= gtk_button_new();
8648 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8649 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8651 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8652 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8653 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8654 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8655 gtk_widget_set_name(button
, name
);
8656 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8662 button_set_stockid(GtkWidget
*button
, char *stockid
)
8666 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8667 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8668 gtk_button_set_image(GTK_BUTTON(button
), image
);
8672 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8675 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
8679 * xterm doesn't play nice with clipboards because it clears the
8680 * primary when clicked. We rely on primary being set to properly
8681 * handle middle mouse button clicks (paste). So when someone clears
8682 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
8683 * other application behavior (as in DON'T clear primary).
8686 p
= gtk_clipboard_wait_for_text(primary
);
8688 if (gdk_property_get(gdk_get_default_root_window(),
8690 gdk_atom_intern("STRING", FALSE
),
8692 1024 * 1024 /* picked out of my butt */,
8698 /* yes sir, we need to NUL the string */
8700 gtk_clipboard_set_text(primary
, p
, -1);
8714 char file
[PATH_MAX
];
8717 vbox
= gtk_vbox_new(FALSE
, 0);
8718 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8719 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8720 #if !GTK_CHECK_VERSION(3, 0, 0)
8721 /* XXX seems to be needed with gtk+2 */
8722 gtk_notebook_set_tab_hborder(notebook
, 0);
8723 gtk_notebook_set_tab_vborder(notebook
, 0);
8725 gtk_notebook_set_scrollable(notebook
, TRUE
);
8726 gtk_notebook_set_show_border(notebook
, FALSE
);
8727 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8729 abtn
= gtk_button_new();
8730 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8731 gtk_widget_set_size_request(arrow
, -1, -1);
8732 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8733 gtk_widget_set_size_request(abtn
, -1, 20);
8735 #if GTK_CHECK_VERSION(2, 20, 0)
8736 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8738 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8740 /* compact tab bar */
8741 tab_bar
= gtk_hbox_new(TRUE
, 0);
8743 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8744 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8745 gtk_widget_set_size_request(vbox
, -1, -1);
8747 g_object_connect(G_OBJECT(notebook
),
8748 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8750 g_object_connect(G_OBJECT(notebook
),
8751 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
8752 NULL
, (char *)NULL
);
8753 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8754 G_CALLBACK(arrow_cb
), NULL
);
8756 main_window
= create_window();
8757 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8760 for (i
= 0; i
< LENGTH(icons
); i
++) {
8761 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8762 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8763 l
= g_list_append(l
, pb
);
8765 gtk_window_set_default_icon_list(l
);
8768 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8769 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8771 gtk_widget_show_all(abtn
);
8772 gtk_widget_show_all(main_window
);
8773 notebook_tab_set_visibility();
8777 set_hook(void **hook
, char *name
)
8780 errx(1, "set_hook");
8782 if (*hook
== NULL
) {
8783 *hook
= dlsym(RTLD_NEXT
, name
);
8785 errx(1, "can't hook %s", name
);
8789 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8791 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8793 g_return_val_if_fail(cookie1
, FALSE
);
8794 g_return_val_if_fail(cookie2
, FALSE
);
8796 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8797 !strcmp (cookie1
->value
, cookie2
->value
) &&
8798 !strcmp (cookie1
->path
, cookie2
->path
) &&
8799 !strcmp (cookie1
->domain
, cookie2
->domain
));
8803 transfer_cookies(void)
8806 SoupCookie
*sc
, *pc
;
8808 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8810 for (;cf
; cf
= cf
->next
) {
8812 sc
= soup_cookie_copy(pc
);
8813 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8816 soup_cookies_free(cf
);
8820 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8825 print_cookie("soup_cookie_jar_delete_cookie", c
);
8827 if (cookies_enabled
== 0)
8830 if (jar
== NULL
|| c
== NULL
)
8833 /* find and remove from persistent jar */
8834 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8836 for (;cf
; cf
= cf
->next
) {
8838 if (soup_cookie_equal(ci
, c
)) {
8839 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8844 soup_cookies_free(cf
);
8846 /* delete from session jar */
8847 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8851 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8853 struct domain
*d
= NULL
;
8857 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8858 jar
, p_cookiejar
, s_cookiejar
);
8860 if (cookies_enabled
== 0)
8863 /* see if we are up and running */
8864 if (p_cookiejar
== NULL
) {
8865 _soup_cookie_jar_add_cookie(jar
, cookie
);
8868 /* disallow p_cookiejar adds, shouldn't happen */
8869 if (jar
== p_cookiejar
)
8873 if (jar
== NULL
|| cookie
== NULL
)
8876 if (enable_cookie_whitelist
&&
8877 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8879 DNPRINTF(XT_D_COOKIE
,
8880 "soup_cookie_jar_add_cookie: reject %s\n",
8882 if (save_rejected_cookies
) {
8883 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8884 show_oops(NULL
, "can't open reject cookie file");
8887 fseek(r_cookie_f
, 0, SEEK_END
);
8888 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8889 cookie
->http_only
? "#HttpOnly_" : "",
8891 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8893 cookie
->secure
? "TRUE" : "FALSE",
8895 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8902 if (!allow_volatile_cookies
)
8906 if (cookie
->expires
== NULL
&& session_timeout
) {
8907 soup_cookie_set_expires(cookie
,
8908 soup_date_new_from_now(session_timeout
));
8909 print_cookie("modified add cookie", cookie
);
8912 /* see if we are white listed for persistence */
8913 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8914 /* add to persistent jar */
8915 c
= soup_cookie_copy(cookie
);
8916 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8917 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8920 /* add to session jar */
8921 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8922 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8928 char file
[PATH_MAX
];
8930 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8931 "soup_cookie_jar_add_cookie");
8932 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8933 "soup_cookie_jar_delete_cookie");
8935 if (cookies_enabled
== 0)
8939 * the following code is intricate due to overriding several libsoup
8941 * do not alter order of these operations.
8944 /* rejected cookies */
8945 if (save_rejected_cookies
)
8946 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
8949 /* persistent cookies */
8950 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8951 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8953 /* session cookies */
8954 s_cookiejar
= soup_cookie_jar_new();
8955 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8956 cookie_policy
, (void *)NULL
);
8959 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8963 setup_proxy(char *uri
)
8966 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8967 soup_uri_free(proxy_uri
);
8971 if (http_proxy
!= uri
) {
8978 http_proxy
= g_strdup(uri
);
8979 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8980 proxy_uri
= soup_uri_new(http_proxy
);
8981 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
8986 send_cmd_to_socket(char *cmd
)
8989 struct sockaddr_un sa
;
8991 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8992 warnx("%s: socket", __func__
);
8996 sa
.sun_family
= AF_UNIX
;
8997 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8998 work_dir
, XT_SOCKET_FILE
);
9001 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9002 warnx("%s: connect", __func__
);
9006 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9007 warnx("%s: send", __func__
);
9018 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9021 char str
[XT_MAX_URL_LENGTH
];
9022 socklen_t t
= sizeof(struct sockaddr_un
);
9023 struct sockaddr_un sa
;
9028 gint fd
= g_io_channel_unix_get_fd(source
);
9030 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9035 if (getpeereid(s
, &uid
, &gid
) == -1) {
9039 if (uid
!= getuid() || gid
!= getgid()) {
9040 warnx("unauthorized user");
9046 warnx("not a valid user");
9050 n
= recv(s
, str
, sizeof(str
), 0);
9054 tt
= TAILQ_LAST(&tabs
, tab_list
);
9055 cmd_execute(tt
, str
);
9063 struct sockaddr_un sa
;
9065 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9066 warn("is_running: socket");
9070 sa
.sun_family
= AF_UNIX
;
9071 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9072 work_dir
, XT_SOCKET_FILE
);
9075 /* connect to see if there is a listener */
9076 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9077 rv
= 0; /* not running */
9079 rv
= 1; /* already running */
9090 struct sockaddr_un sa
;
9092 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9093 warn("build_socket: socket");
9097 sa
.sun_family
= AF_UNIX
;
9098 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9099 work_dir
, XT_SOCKET_FILE
);
9102 /* connect to see if there is a listener */
9103 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9104 /* no listener so we will */
9105 unlink(sa
.sun_path
);
9107 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9108 warn("build_socket: bind");
9112 if (listen(s
, 1) == -1) {
9113 warn("build_socket: listen");
9126 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9127 GtkTreeIter
*iter
, struct tab
*t
)
9131 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9139 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9140 GtkTreeIter
*iter
, struct tab
*t
)
9144 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9145 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9146 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9153 completion_add_uri(const gchar
*uri
)
9157 /* add uri to list_store */
9158 gtk_list_store_append(completion_model
, &iter
);
9159 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9163 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9164 GtkTreeIter
*iter
, gpointer user_data
)
9167 gboolean match
= FALSE
;
9169 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9175 match
= match_uri(value
, key
);
9182 completion_add(struct tab
*t
)
9184 /* enable completion for tab */
9185 t
->completion
= gtk_entry_completion_new();
9186 gtk_entry_completion_set_text_column(t
->completion
, 0);
9187 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9188 gtk_entry_completion_set_model(t
->completion
,
9189 GTK_TREE_MODEL(completion_model
));
9190 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9192 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9193 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9194 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9195 G_CALLBACK(completion_select_cb
), t
);
9196 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9197 G_CALLBACK(completion_hover_cb
), t
);
9205 if (stat(dir
, &sb
)) {
9206 if (mkdir(dir
, S_IRWXU
) == -1)
9207 err(1, "mkdir %s", dir
);
9209 err(1, "stat %s", dir
);
9211 if (S_ISDIR(sb
.st_mode
) == 0)
9212 errx(1, "%s not a dir", dir
);
9213 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9214 warnx("fixing invalid permissions on %s", dir
);
9215 if (chmod(dir
, S_IRWXU
) == -1)
9216 err(1, "chmod %s", dir
);
9224 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9230 main(int argc
, char *argv
[])
9233 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9234 char conf
[PATH_MAX
] = { '\0' };
9235 char file
[PATH_MAX
];
9236 char *env_proxy
= NULL
;
9239 struct sigaction sact
;
9240 GIOChannel
*channel
;
9245 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9247 /* fiddle with ulimits */
9248 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9251 /* just use them all */
9252 rlp
.rlim_cur
= rlp
.rlim_max
;
9253 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9255 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9257 else if (rlp
.rlim_cur
<= 256)
9258 warnx("%s requires at least 256 file descriptors",
9262 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9271 errx(0 , "Version: %s", version
);
9274 strlcpy(conf
, optarg
, sizeof(conf
));
9277 strlcpy(named_session
, optarg
, sizeof(named_session
));
9298 RB_INIT(&downloads
);
9302 TAILQ_INIT(&aliases
);
9308 gnutls_global_init();
9310 /* generate session keys for xtp pages */
9311 generate_xtp_session_key(&dl_session_key
);
9312 generate_xtp_session_key(&hl_session_key
);
9313 generate_xtp_session_key(&cl_session_key
);
9314 generate_xtp_session_key(&fl_session_key
);
9317 gtk_init(&argc
, &argv
);
9318 if (!g_thread_supported())
9319 g_thread_init(NULL
);
9322 bzero(&sact
, sizeof(sact
));
9323 sigemptyset(&sact
.sa_mask
);
9324 sact
.sa_handler
= sigchild
;
9325 sact
.sa_flags
= SA_NOCLDSTOP
;
9326 sigaction(SIGCHLD
, &sact
, NULL
);
9328 /* set download dir */
9329 pwd
= getpwuid(getuid());
9331 errx(1, "invalid user %d", getuid());
9332 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9334 /* compile buffer command regexes */
9337 /* set default string settings */
9338 home
= g_strdup("https://www.cyphertite.com");
9339 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9340 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9341 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9342 cmd_font_name
= g_strdup("monospace normal 9");
9343 oops_font_name
= g_strdup("monospace normal 9");
9344 statusbar_font_name
= g_strdup("monospace normal 9");
9345 tabbar_font_name
= g_strdup("monospace normal 9");
9346 statusbar_elems
= g_strdup("BP");
9348 /* read config file */
9349 if (strlen(conf
) == 0)
9350 snprintf(conf
, sizeof conf
, "%s/.%s",
9351 pwd
->pw_dir
, XT_CONF_FILE
);
9352 config_parse(conf
, 0);
9355 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9356 oops_font
= pango_font_description_from_string(oops_font_name
);
9357 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9358 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9360 /* working directory */
9361 if (strlen(work_dir
) == 0)
9362 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9363 pwd
->pw_dir
, XT_DIR
);
9366 /* icon cache dir */
9367 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9371 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9375 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9376 work_dir
, XT_SESSIONS_DIR
);
9377 xxx_dir(sessions_dir
);
9379 /* runtime settings that can override config file */
9380 if (runtime_settings
[0] != '\0')
9381 config_parse(runtime_settings
, 1);
9384 if (!strcmp(download_dir
, pwd
->pw_dir
))
9385 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9386 xxx_dir(download_dir
);
9388 /* favorites file */
9389 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9390 if (stat(file
, &sb
)) {
9391 warnx("favorites file doesn't exist, creating it");
9392 if ((f
= fopen(file
, "w")) == NULL
)
9393 err(1, "favorites");
9397 /* quickmarks file */
9398 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9399 if (stat(file
, &sb
)) {
9400 warnx("quickmarks file doesn't exist, creating it");
9401 if ((f
= fopen(file
, "w")) == NULL
)
9402 err(1, "quickmarks");
9407 session
= webkit_get_default_session();
9412 if (stat(ssl_ca_file
, &sb
)) {
9413 warnx("no CA file: %s", ssl_ca_file
);
9414 g_free(ssl_ca_file
);
9417 g_object_set(session
,
9418 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9419 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9424 env_proxy
= getenv("http_proxy");
9426 setup_proxy(env_proxy
);
9428 setup_proxy(http_proxy
);
9431 send_cmd_to_socket(argv
[0]);
9435 /* set some connection parameters */
9436 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9437 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9440 /* see if there is already an xxxterm running */
9441 if (single_instance
&& is_running()) {
9443 warnx("already running");
9449 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9450 send_cmd_to_socket(cmd
);
9460 /* uri completion */
9461 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9464 buffers_store
= gtk_list_store_new
9465 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9471 notebook_tab_set_visibility();
9473 if (save_global_history
)
9474 restore_global_history();
9476 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9477 restore_saved_tabs();
9479 a
.s
= named_session
;
9480 a
.i
= XT_SES_DONOTHING
;
9481 open_tabs(NULL
, &a
);
9485 create_new_tab(argv
[0], NULL
, focus
, -1);
9492 if (TAILQ_EMPTY(&tabs
))
9493 create_new_tab(home
, NULL
, 1, -1);
9496 if ((s
= build_socket()) != -1) {
9497 channel
= g_io_channel_unix_new(s
);
9498 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
9503 gnutls_global_deinit();