3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
8 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * create privacy browsing
26 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #elif defined(__FreeBSD__)
50 #include "freebsd/util.h"
56 #include <sys/queue.h>
57 #include <sys/resource.h>
58 #include <sys/socket.h>
64 #include <gdk/gdkkeysyms.h>
66 #if GTK_CHECK_VERSION(3,0,0)
67 /* we still use GDK_* instead of GDK_KEY_* */
68 #include <gdk/gdkkeysyms-compat.h>
71 #include <webkit/webkit.h>
72 #include <libsoup/soup.h>
73 #include <gnutls/gnutls.h>
74 #include <JavaScriptCore/JavaScript.h>
75 #include <gnutls/x509.h>
77 #include "javascript.h"
80 javascript.h borrowed from vimprobable2 under the following license:
82 Copyright (c) 2009 Leon Winter
83 Copyright (c) 2009 Hannes Schueller
84 Copyright (c) 2009 Matto Fransen
86 Permission is hereby granted, free of charge, to any person obtaining a copy
87 of this software and associated documentation files (the "Software"), to deal
88 in the Software without restriction, including without limitation the rights
89 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
90 copies of the Software, and to permit persons to whom the Software is
91 furnished to do so, subject to the following conditions:
93 The above copyright notice and this permission notice shall be included in
94 all copies or substantial portions of the Software.
96 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
97 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
98 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
99 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
100 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
101 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
105 static char *version
= "$xxxterm$";
107 /* hooked functions */
108 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
109 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
114 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
115 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
116 #define XT_D_MOVE 0x0001
117 #define XT_D_KEY 0x0002
118 #define XT_D_TAB 0x0004
119 #define XT_D_URL 0x0008
120 #define XT_D_CMD 0x0010
121 #define XT_D_NAV 0x0020
122 #define XT_D_DOWNLOAD 0x0040
123 #define XT_D_CONFIG 0x0080
124 #define XT_D_JS 0x0100
125 #define XT_D_FAVORITE 0x0200
126 #define XT_D_PRINTING 0x0400
127 #define XT_D_COOKIE 0x0800
128 #define XT_D_KEYBINDING 0x1000
129 #define XT_D_CLIP 0x2000
130 #define XT_D_BUFFERCMD 0x4000
131 u_int32_t swm_debug
= 0
149 #define DPRINTF(x...)
150 #define DNPRINTF(n,x...)
153 #define LENGTH(x) (sizeof x / sizeof x[0])
154 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
155 ~(GDK_BUTTON1_MASK) & \
156 ~(GDK_BUTTON2_MASK) & \
157 ~(GDK_BUTTON3_MASK) & \
158 ~(GDK_BUTTON4_MASK) & \
161 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
172 TAILQ_ENTRY(tab
) entry
;
174 GtkWidget
*tab_content
;
183 GtkWidget
*uri_entry
;
184 GtkWidget
*search_entry
;
186 GtkWidget
*browser_win
;
187 GtkWidget
*statusbar_box
;
189 GtkWidget
*statusbar
;
190 GtkWidget
*buffercmd
;
201 GtkWidget
*js_toggle
;
202 GtkEntryCompletion
*completion
;
206 WebKitWebHistoryItem
*item
;
207 WebKitWebBackForwardList
*bfl
;
210 WebKitNetworkRequest
*icon_request
;
211 WebKitDownload
*icon_download
;
212 gchar
*icon_dest_uri
;
214 /* adjustments for browser */
217 GtkAdjustment
*adjust_h
;
218 GtkAdjustment
*adjust_v
;
224 int xtp_meaning
; /* identifies dls/favorites */
230 #define XT_HINT_NONE (0)
231 #define XT_HINT_NUMERICAL (1)
232 #define XT_HINT_ALPHANUM (2)
236 /* custom stylesheet */
245 WebKitWebSettings
*settings
;
249 double mark
[XT_NOMARKS
];
251 TAILQ_HEAD(tab_list
, tab
);
254 RB_ENTRY(history
) entry
;
258 RB_HEAD(history_list
, history
);
261 RB_ENTRY(download
) entry
;
263 WebKitDownload
*download
;
266 RB_HEAD(download_list
, download
);
269 RB_ENTRY(domain
) entry
;
271 int handy
; /* app use */
273 RB_HEAD(domain_list
, domain
);
276 TAILQ_ENTRY(undo
) entry
;
279 int back
; /* Keeps track of how many back
280 * history items there are. */
282 TAILQ_HEAD(undo_tailq
, undo
);
284 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
285 int next_download_id
= 1;
294 #define XT_NAME ("XXXTerm")
295 #define XT_DIR (".xxxterm")
296 #define XT_CACHE_DIR ("cache")
297 #define XT_CERT_DIR ("certs/")
298 #define XT_SESSIONS_DIR ("sessions/")
299 #define XT_CONF_FILE ("xxxterm.conf")
300 #define XT_FAVS_FILE ("favorites")
301 #define XT_QMARKS_FILE ("quickmarks")
302 #define XT_SAVED_TABS_FILE ("main_session")
303 #define XT_RESTART_TABS_FILE ("restart_tabs")
304 #define XT_SOCKET_FILE ("socket")
305 #define XT_HISTORY_FILE ("history")
306 #define XT_REJECT_FILE ("rejected.txt")
307 #define XT_COOKIE_FILE ("cookies.txt")
308 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
309 #define XT_CB_HANDLED (TRUE)
310 #define XT_CB_PASSTHROUGH (FALSE)
311 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
312 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
313 #define XT_DLMAN_REFRESH "10"
314 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
315 "td{overflow: hidden;" \
316 " padding: 2px 2px 2px 2px;" \
317 " border: 1px solid black;" \
318 " vertical-align:top;" \
319 " word-wrap: break-word}\n" \
320 "tr:hover{background: #ffff99}\n" \
321 "th{background-color: #cccccc;" \
322 " border: 1px solid black}\n" \
323 "table{width: 100%%;" \
324 " border: 1px black solid;" \
325 " border-collapse:collapse}\n" \
327 "border: 1px solid black;" \
330 ".progress-inner{float: left;" \
332 " background: green}\n" \
333 ".dlstatus{font-size: small;" \
334 " text-align: center}\n" \
336 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
337 #define XT_MAX_UNDO_CLOSE_TAB (32)
338 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
339 #define XT_PRINT_EXTRA_MARGIN 10
340 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
343 #define XT_COLOR_RED "#cc0000"
344 #define XT_COLOR_YELLOW "#ffff66"
345 #define XT_COLOR_BLUE "lightblue"
346 #define XT_COLOR_GREEN "#99ff66"
347 #define XT_COLOR_WHITE "white"
348 #define XT_COLOR_BLACK "black"
350 #define XT_COLOR_CT_BACKGROUND "#000000"
351 #define XT_COLOR_CT_INACTIVE "#dddddd"
352 #define XT_COLOR_CT_ACTIVE "#bbbb00"
353 #define XT_COLOR_CT_SEPARATOR "#555555"
355 #define XT_COLOR_SB_SEPARATOR "#555555"
357 #define XT_PROTO_DELIM "://"
360 * xxxterm "protocol" (xtp)
361 * We use this for managing stuff like downloads and favorites. They
362 * make magical HTML pages in memory which have xxxt:// links in order
363 * to communicate with xxxterm's internals. These links take the format:
364 * xxxt://class/session_key/action/arg
366 * Don't begin xtp class/actions as 0. atoi returns that on error.
368 * Typically we have not put addition of items in this framework, as
369 * adding items is either done via an ex-command or via a keybinding instead.
372 #define XT_XTP_STR "xxxt://"
374 /* XTP classes (xxxt://<class>) */
375 #define XT_XTP_INVALID 0 /* invalid */
376 #define XT_XTP_DL 1 /* downloads */
377 #define XT_XTP_HL 2 /* history */
378 #define XT_XTP_CL 3 /* cookies */
379 #define XT_XTP_FL 4 /* favorites */
381 /* XTP download actions */
382 #define XT_XTP_DL_LIST 1
383 #define XT_XTP_DL_CANCEL 2
384 #define XT_XTP_DL_REMOVE 3
386 /* XTP history actions */
387 #define XT_XTP_HL_LIST 1
388 #define XT_XTP_HL_REMOVE 2
390 /* XTP cookie actions */
391 #define XT_XTP_CL_LIST 1
392 #define XT_XTP_CL_REMOVE 2
394 /* XTP cookie actions */
395 #define XT_XTP_FL_LIST 1
396 #define XT_XTP_FL_REMOVE 2
399 #define XT_MOVE_INVALID (0)
400 #define XT_MOVE_DOWN (1)
401 #define XT_MOVE_UP (2)
402 #define XT_MOVE_BOTTOM (3)
403 #define XT_MOVE_TOP (4)
404 #define XT_MOVE_PAGEDOWN (5)
405 #define XT_MOVE_PAGEUP (6)
406 #define XT_MOVE_HALFDOWN (7)
407 #define XT_MOVE_HALFUP (8)
408 #define XT_MOVE_LEFT (9)
409 #define XT_MOVE_FARLEFT (10)
410 #define XT_MOVE_RIGHT (11)
411 #define XT_MOVE_FARRIGHT (12)
412 #define XT_MOVE_PERCENT (13)
414 #define XT_QMARK_SET (0)
415 #define XT_QMARK_OPEN (1)
416 #define XT_QMARK_TAB (2)
418 #define XT_MARK_SET (0)
419 #define XT_MARK_GOTO (1)
421 #define XT_TAB_LAST (-4)
422 #define XT_TAB_FIRST (-3)
423 #define XT_TAB_PREV (-2)
424 #define XT_TAB_NEXT (-1)
425 #define XT_TAB_INVALID (0)
426 #define XT_TAB_NEW (1)
427 #define XT_TAB_DELETE (2)
428 #define XT_TAB_DELQUIT (3)
429 #define XT_TAB_OPEN (4)
430 #define XT_TAB_UNDO_CLOSE (5)
431 #define XT_TAB_SHOW (6)
432 #define XT_TAB_HIDE (7)
433 #define XT_TAB_NEXTSTYLE (8)
435 #define XT_NAV_INVALID (0)
436 #define XT_NAV_BACK (1)
437 #define XT_NAV_FORWARD (2)
438 #define XT_NAV_RELOAD (3)
440 #define XT_FOCUS_INVALID (0)
441 #define XT_FOCUS_URI (1)
442 #define XT_FOCUS_SEARCH (2)
444 #define XT_SEARCH_INVALID (0)
445 #define XT_SEARCH_NEXT (1)
446 #define XT_SEARCH_PREV (2)
448 #define XT_PASTE_CURRENT_TAB (0)
449 #define XT_PASTE_NEW_TAB (1)
451 #define XT_ZOOM_IN (-1)
452 #define XT_ZOOM_OUT (-2)
453 #define XT_ZOOM_NORMAL (100)
455 #define XT_URL_SHOW (1)
456 #define XT_URL_HIDE (2)
458 #define XT_WL_TOGGLE (1<<0)
459 #define XT_WL_ENABLE (1<<1)
460 #define XT_WL_DISABLE (1<<2)
461 #define XT_WL_FQDN (1<<3) /* default */
462 #define XT_WL_TOPLEVEL (1<<4)
463 #define XT_WL_PERSISTENT (1<<5)
464 #define XT_WL_SESSION (1<<6)
465 #define XT_WL_RELOAD (1<<7)
467 #define XT_SHOW (1<<7)
468 #define XT_DELETE (1<<8)
469 #define XT_SAVE (1<<9)
470 #define XT_OPEN (1<<10)
472 #define XT_CMD_OPEN (0)
473 #define XT_CMD_OPEN_CURRENT (1)
474 #define XT_CMD_TABNEW (2)
475 #define XT_CMD_TABNEW_CURRENT (3)
477 #define XT_STATUS_NOTHING (0)
478 #define XT_STATUS_LINK (1)
479 #define XT_STATUS_URI (2)
480 #define XT_STATUS_LOADING (3)
482 #define XT_SES_DONOTHING (0)
483 #define XT_SES_CLOSETABS (1)
485 #define XT_BM_NORMAL (0)
486 #define XT_BM_WHITELIST (1)
487 #define XT_BM_KIOSK (2)
489 #define XT_PREFIX (1<<0)
490 #define XT_USERARG (1<<1)
491 #define XT_URLARG (1<<2)
492 #define XT_INTARG (1<<3)
494 #define XT_TABS_NORMAL 0
495 #define XT_TABS_COMPACT 1
497 #define XT_BUFCMD_SZ (8)
505 TAILQ_ENTRY(mime_type
) entry
;
507 TAILQ_HEAD(mime_type_list
, mime_type
);
513 TAILQ_ENTRY(alias
) entry
;
515 TAILQ_HEAD(alias_list
, alias
);
517 /* settings that require restart */
518 int tabless
= 0; /* allow only 1 tab */
519 int enable_socket
= 0;
520 int single_instance
= 0; /* only allow one xxxterm to run */
521 int fancy_bar
= 1; /* fancy toolbar */
522 int browser_mode
= XT_BM_NORMAL
;
523 int enable_localstorage
= 0;
524 char *statusbar_elems
= NULL
;
526 /* runtime settings */
527 int show_tabs
= 1; /* show tabs on notebook */
528 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
529 int show_url
= 1; /* show url toolbar on notebook */
530 int show_statusbar
= 0; /* vimperator style status bar */
531 int ctrl_click_focus
= 0; /* ctrl click gets focus */
532 int cookies_enabled
= 1; /* enable cookies */
533 int read_only_cookies
= 0; /* enable to not write cookies */
534 int enable_scripts
= 1;
535 int enable_plugins
= 0;
536 gfloat default_zoom_level
= 1.0;
537 char default_script
[PATH_MAX
];
538 int window_height
= 768;
539 int window_width
= 1024;
540 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
541 int refresh_interval
= 10; /* download refresh interval */
542 int enable_cookie_whitelist
= 0;
543 int enable_js_whitelist
= 0;
544 int session_timeout
= 3600; /* cookie session timeout */
545 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
546 char *ssl_ca_file
= NULL
;
547 char *resource_dir
= NULL
;
548 gboolean ssl_strict_certs
= FALSE
;
549 int append_next
= 1; /* append tab after current tab */
551 char *search_string
= NULL
;
552 char *http_proxy
= NULL
;
553 char download_dir
[PATH_MAX
];
554 char runtime_settings
[PATH_MAX
]; /* override of settings */
555 int allow_volatile_cookies
= 0;
556 int save_global_history
= 0; /* save global history to disk */
557 char *user_agent
= NULL
;
558 int save_rejected_cookies
= 0;
559 int session_autosave
= 0;
560 int guess_search
= 0;
561 int dns_prefetch
= FALSE
;
562 gint max_connections
= 25;
563 gint max_host_connections
= 5;
564 gint enable_spell_checking
= 0;
565 char *spell_check_languages
= NULL
;
566 int xterm_workaround
= 0;
568 char *cmd_font_name
= NULL
;
569 char *oops_font_name
= NULL
;
570 char *statusbar_font_name
= NULL
;
571 char *tabbar_font_name
= NULL
;
572 PangoFontDescription
*cmd_font
;
573 PangoFontDescription
*oops_font
;
574 PangoFontDescription
*statusbar_font
;
575 PangoFontDescription
*tabbar_font
;
576 char *qmarks
[XT_NOMARKS
];
578 int btn_down
; /* M1 down in any wv */
582 int set_browser_mode(struct settings
*, char *);
583 int set_cookie_policy(struct settings
*, char *);
584 int set_download_dir(struct settings
*, char *);
585 int set_default_script(struct settings
*, char *);
586 int set_runtime_dir(struct settings
*, char *);
587 int set_tab_style(struct settings
*, char *);
588 int set_work_dir(struct settings
*, char *);
589 int add_alias(struct settings
*, char *);
590 int add_mime_type(struct settings
*, char *);
591 int add_cookie_wl(struct settings
*, char *);
592 int add_js_wl(struct settings
*, char *);
593 int add_kb(struct settings
*, char *);
594 void button_set_stockid(GtkWidget
*, char *);
595 GtkWidget
* create_button(char *, char *, int);
597 char *get_browser_mode(struct settings
*);
598 char *get_cookie_policy(struct settings
*);
599 char *get_download_dir(struct settings
*);
600 char *get_default_script(struct settings
*);
601 char *get_runtime_dir(struct settings
*);
602 char *get_tab_style(struct settings
*);
603 char *get_work_dir(struct settings
*);
605 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
606 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
607 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
608 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
609 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
611 void recalc_tabs(void);
612 void recolor_compact_tabs(void);
613 void set_current_tab(int page_num
);
614 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
615 void marks_clear(struct tab
*t
);
618 int (*set
)(struct settings
*, char *);
619 char *(*get
)(struct settings
*);
620 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
623 struct special s_browser_mode
= {
629 struct special s_cookie
= {
635 struct special s_alias
= {
641 struct special s_mime
= {
647 struct special s_js
= {
653 struct special s_kb
= {
659 struct special s_cookie_wl
= {
665 struct special s_default_script
= {
671 struct special s_download_dir
= {
677 struct special s_work_dir
= {
683 struct special s_tab_style
= {
692 #define XT_S_INVALID (0)
695 #define XT_S_FLOAT (3)
697 #define XT_SF_RESTART (1<<0)
698 #define XT_SF_RUNTIME (1<<1)
704 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
705 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
706 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
707 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
708 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
709 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
710 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
711 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
712 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
713 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
714 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
715 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
716 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
717 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
718 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
719 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
720 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
721 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
722 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
723 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
724 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
725 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
726 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
727 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
728 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
729 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
730 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
731 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
732 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
733 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
734 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
735 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
736 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
737 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
738 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
739 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
740 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
741 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
742 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
743 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
744 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
745 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
746 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
747 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
748 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
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 */
2563 if (p
== NULL
&& xterm_workaround
)
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
))
2979 if (strlen(title
) == 0 || title
[0] == '#') {
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(struct tab
*t
, const gchar
*uri
, char *domain
,
3135 struct addrinfo hints
, *res
= NULL
, *ai
;
3136 int rv
= -1, s
= -1, on
, error
;
3139 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3140 show_oops(t
, "invalid URI");
3144 su
= soup_uri_new(uri
);
3146 show_oops(t
, "invalid soup URI");
3149 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3150 show_oops(t
, "invalid HTTPS URI");
3154 snprintf(port
, sizeof port
, "%d", su
->port
);
3155 bzero(&hints
, sizeof(struct addrinfo
));
3156 hints
.ai_flags
= AI_CANONNAME
;
3157 hints
.ai_family
= AF_UNSPEC
;
3158 hints
.ai_socktype
= SOCK_STREAM
;
3160 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3161 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3165 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3166 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3169 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3171 show_oops(t
, "socket failed: %s", strerror(errno
));
3174 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3175 sizeof(on
)) == -1) {
3176 show_oops(t
, "setsockopt failed: %s", strerror(errno
));
3179 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == -1) {
3180 show_oops(t
, "connect failed: %s", strerror(errno
));
3188 strlcpy(domain
, su
->host
, domain_sz
);
3195 if (rv
== -1 && s
!= -1)
3202 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3205 gnutls_deinit(gsession
);
3207 gnutls_certificate_free_credentials(xcred
);
3213 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3214 gnutls_certificate_credentials_t
*xc
)
3216 gnutls_certificate_credentials_t xcred
;
3217 gnutls_session_t gsession
;
3220 if (gs
== NULL
|| xc
== NULL
)
3226 gnutls_certificate_allocate_credentials(&xcred
);
3227 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3228 GNUTLS_X509_FMT_PEM
);
3230 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3231 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3232 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3233 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3234 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3235 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3237 gnutls_error_is_fatal(rv
),
3238 gnutls_strerror_name(rv
));
3239 stop_tls(gsession
, xcred
);
3243 gnutls_credentials_type_t cred
;
3244 cred
= gnutls_auth_get_type(gsession
);
3245 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3246 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3247 stop_tls(gsession
, xcred
);
3259 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3263 const gnutls_datum_t
*cl
;
3264 gnutls_x509_crt_t
*all_certs
;
3267 if (certs
== NULL
|| cert_count
== NULL
)
3269 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3271 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3275 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3276 for (i
= 0; i
< len
; i
++) {
3277 gnutls_x509_crt_init(&all_certs
[i
]);
3278 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3279 GNUTLS_X509_FMT_PEM
< 0)) {
3293 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3297 for (i
= 0; i
< cert_count
; i
++)
3298 gnutls_x509_crt_deinit(certs
[i
]);
3303 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3305 GdkColor c_text
, c_base
;
3307 gdk_color_parse(text
, &c_text
);
3308 gdk_color_parse(base
, &c_base
);
3310 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3311 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3312 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3313 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3315 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3316 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3317 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3318 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3322 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3323 size_t cert_count
, char *domain
)
3326 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3331 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3334 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3335 if ((f
= fopen(file
, "w")) == NULL
) {
3336 show_oops(t
, "Can't create cert file %s %s",
3337 file
, strerror(errno
));
3341 for (i
= 0; i
< cert_count
; i
++) {
3342 cert_buf_sz
= sizeof cert_buf
;
3343 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3344 cert_buf
, &cert_buf_sz
)) {
3345 show_oops(t
, "gnutls_x509_crt_export failed");
3348 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3349 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3354 /* not the best spot but oh well */
3355 gdk_color_parse("lightblue", &color
);
3356 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3357 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3370 load_compare_cert(struct tab
*t
, struct karg
*args
)
3373 char domain
[8182], file
[PATH_MAX
];
3374 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3375 int s
= -1, i
, error
;
3377 size_t cert_buf_sz
, cert_count
;
3378 enum cert_trust rv
= CERT_UNTRUSTED
;
3380 gnutls_session_t gsession
;
3381 gnutls_x509_crt_t
*certs
;
3382 gnutls_certificate_credentials_t xcred
;
3384 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3389 if ((uri
= get_uri(t
)) == NULL
)
3391 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3393 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3395 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3398 if (start_tls(t
, s
, &gsession
, &xcred
))
3400 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3402 /* verify certs in case cert file doesn't exist */
3403 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3405 show_oops(t
, "Invalid certificates");
3410 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3411 show_oops(t
, "Can't get connection certificates");
3415 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3416 if ((f
= fopen(file
, "r")) == NULL
) {
3422 for (i
= 0; i
< cert_count
; i
++) {
3423 cert_buf_sz
= sizeof cert_buf
;
3424 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3425 cert_buf
, &cert_buf_sz
)) {
3428 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3429 rv
= CERT_BAD
; /* critical */
3432 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3433 rv
= CERT_BAD
; /* critical */
3442 free_connection_certs(certs
, cert_count
);
3444 /* we close the socket first for speed */
3448 /* only complain if we didn't save it locally */
3449 if (error
&& rv
!= CERT_LOCAL
) {
3450 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3451 if (error
& GNUTLS_CERT_INVALID
)
3452 strlcat(serr
, "invalid, ", sizeof serr
);
3453 if (error
& GNUTLS_CERT_REVOKED
)
3454 strlcat(serr
, "revoked, ", sizeof serr
);
3455 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3456 strlcat(serr
, "signer not found, ", sizeof serr
);
3457 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3458 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3459 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3460 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3461 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3462 strlcat(serr
, "not activated, ", sizeof serr
);
3463 if (error
& GNUTLS_CERT_EXPIRED
)
3464 strlcat(serr
, "expired, ", sizeof serr
);
3465 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3466 if (serr
[i
] == ',') {
3473 stop_tls(gsession
, xcred
);
3479 cert_cmd(struct tab
*t
, struct karg
*args
)
3485 gnutls_session_t gsession
;
3486 gnutls_x509_crt_t
*certs
;
3487 gnutls_certificate_credentials_t xcred
;
3492 if (ssl_ca_file
== NULL
) {
3493 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3497 if ((uri
= get_uri(t
)) == NULL
) {
3498 show_oops(t
, "Invalid URI");
3502 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3503 show_oops(t
, "Invalid certificate URI: %s", uri
);
3508 if (start_tls(t
, s
, &gsession
, &xcred
))
3512 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3513 show_oops(t
, "get_connection_certs failed");
3517 if (args
->i
& XT_SHOW
)
3518 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3519 else if (args
->i
& XT_SAVE
)
3520 save_certs(t
, certs
, cert_count
, domain
);
3522 free_connection_certs(certs
, cert_count
);
3524 /* we close the socket first for speed */
3527 stop_tls(gsession
, xcred
);
3533 remove_cookie(int index
)
3539 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3541 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3543 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3547 print_cookie("remove cookie", c
);
3548 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3553 soup_cookies_free(cf
);
3559 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3564 body
= g_strdup("");
3567 if (args
->i
& XT_WL_PERSISTENT
) {
3569 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3571 RB_FOREACH(d
, domain_list
, wl
) {
3575 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3581 if (args
->i
& XT_WL_SESSION
) {
3583 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3585 RB_FOREACH(d
, domain_list
, wl
) {
3589 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3594 tmp
= get_html_page(title
, body
, "", 0);
3597 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3599 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3605 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3607 char file
[PATH_MAX
];
3609 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3617 if (t
== NULL
|| args
== NULL
)
3620 if (runtime_settings
[0] == '\0')
3623 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3624 if ((f
= fopen(file
, "r+")) == NULL
)
3628 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3629 if (uri
== NULL
|| dom
== NULL
||
3630 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3631 show_oops(t
, "Can't add domain to %s white list",
3632 js
? "JavaScript" : "cookie");
3636 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3639 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3642 if (!strcmp(line
, lt
))
3648 fprintf(f
, "%s\n", lt
);
3653 d
= wl_find(dom
, &js_wl
);
3655 settings_add("js_wl", dom
);
3656 d
= wl_find(dom
, &js_wl
);
3660 d
= wl_find(dom
, &c_wl
);
3662 settings_add("cookie_wl", dom
);
3663 d
= wl_find(dom
, &c_wl
);
3667 /* find and add to persistent jar */
3668 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3669 for (;cf
; cf
= cf
->next
) {
3671 if (!strcmp(dom
, ci
->domain
) ||
3672 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3673 c
= soup_cookie_copy(ci
);
3674 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3677 soup_cookies_free(cf
);
3695 js_show_wl(struct tab
*t
, struct karg
*args
)
3697 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3698 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3704 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3706 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3707 wl_show(t
, args
, "Cookie White List", &c_wl
);
3713 cookie_cmd(struct tab
*t
, struct karg
*args
)
3715 if (args
->i
& XT_SHOW
)
3716 wl_show(t
, args
, "Cookie White List", &c_wl
);
3717 else if (args
->i
& XT_WL_TOGGLE
) {
3718 args
->i
|= XT_WL_RELOAD
;
3719 toggle_cwl(t
, args
);
3720 } else if (args
->i
& XT_SAVE
) {
3721 args
->i
|= XT_WL_RELOAD
;
3722 wl_save(t
, args
, 0);
3723 } else if (args
->i
& XT_DELETE
)
3724 show_oops(t
, "'cookie delete' currently unimplemented");
3730 js_cmd(struct tab
*t
, struct karg
*args
)
3732 if (args
->i
& XT_SHOW
)
3733 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3734 else if (args
->i
& XT_SAVE
) {
3735 args
->i
|= XT_WL_RELOAD
;
3736 wl_save(t
, args
, 1);
3737 } else if (args
->i
& XT_WL_TOGGLE
) {
3738 args
->i
|= XT_WL_RELOAD
;
3740 } else if (args
->i
& XT_DELETE
)
3741 show_oops(t
, "'js delete' currently unimplemented");
3747 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3749 js_toggle_cb(t
->js_toggle
, t
);
3755 add_favorite(struct tab
*t
, struct karg
*args
)
3757 char file
[PATH_MAX
];
3760 size_t urilen
, linelen
;
3761 const gchar
*uri
, *title
;
3766 /* don't allow adding of xtp pages to favorites */
3767 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3768 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3772 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3773 if ((f
= fopen(file
, "r+")) == NULL
) {
3774 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3778 title
= get_title(t
, FALSE
);
3781 if (title
== NULL
|| uri
== NULL
) {
3782 show_oops(t
, "can't add page to favorites");
3786 urilen
= strlen(uri
);
3789 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3790 if (feof(f
) || ferror(f
))
3793 if (linelen
== urilen
&& !strcmp(line
, uri
))
3800 fprintf(f
, "\n%s\n%s", title
, uri
);
3806 update_favorite_tabs(NULL
);
3812 navaction(struct tab
*t
, struct karg
*args
)
3814 WebKitWebHistoryItem
*item
;
3816 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3817 t
->tab_id
, args
->i
);
3819 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3822 if (args
->i
== XT_NAV_BACK
)
3823 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3825 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3827 return (XT_CB_PASSTHROUGH
);
3828 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3830 return (XT_CB_PASSTHROUGH
);
3836 item
= webkit_web_back_forward_list_get_back_item(t
->bfl
);
3838 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3840 case XT_NAV_FORWARD
:
3842 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3844 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3847 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3849 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3852 return (XT_CB_PASSTHROUGH
);
3856 move(struct tab
*t
, struct karg
*args
)
3858 GtkAdjustment
*adjust
;
3859 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3865 case XT_MOVE_BOTTOM
:
3867 case XT_MOVE_PAGEDOWN
:
3868 case XT_MOVE_PAGEUP
:
3869 case XT_MOVE_HALFDOWN
:
3870 case XT_MOVE_HALFUP
:
3871 case XT_MOVE_PERCENT
:
3872 adjust
= t
->adjust_v
;
3875 adjust
= t
->adjust_h
;
3879 pos
= gtk_adjustment_get_value(adjust
);
3880 ps
= gtk_adjustment_get_page_size(adjust
);
3881 upper
= gtk_adjustment_get_upper(adjust
);
3882 lower
= gtk_adjustment_get_lower(adjust
);
3883 si
= gtk_adjustment_get_step_increment(adjust
);
3884 pi
= gtk_adjustment_get_page_increment(adjust
);
3887 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3888 "max %f si %f pi %f\n",
3889 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3890 pos
, ps
, upper
, lower
, max
, si
, pi
);
3896 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3901 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3903 case XT_MOVE_BOTTOM
:
3904 case XT_MOVE_FARRIGHT
:
3905 gtk_adjustment_set_value(adjust
, max
);
3908 case XT_MOVE_FARLEFT
:
3909 gtk_adjustment_set_value(adjust
, lower
);
3911 case XT_MOVE_PAGEDOWN
:
3913 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3915 case XT_MOVE_PAGEUP
:
3917 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3919 case XT_MOVE_HALFDOWN
:
3921 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3923 case XT_MOVE_HALFUP
:
3925 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3927 case XT_MOVE_PERCENT
:
3928 percent
= atoi(args
->s
) / 100.0;
3929 pos
= max
* percent
;
3930 if (pos
< 0.0 || pos
> max
)
3932 gtk_adjustment_set_value(adjust
, pos
);
3935 return (XT_CB_PASSTHROUGH
);
3938 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3940 return (XT_CB_HANDLED
);
3944 url_set_visibility(void)
3948 TAILQ_FOREACH(t
, &tabs
, entry
)
3949 if (show_url
== 0) {
3950 gtk_widget_hide(t
->toolbar
);
3953 gtk_widget_show(t
->toolbar
);
3957 notebook_tab_set_visibility(void)
3959 if (show_tabs
== 0) {
3960 gtk_widget_hide(tab_bar
);
3961 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3963 if (tab_style
== XT_TABS_NORMAL
) {
3964 gtk_widget_hide(tab_bar
);
3965 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3966 } else if (tab_style
== XT_TABS_COMPACT
) {
3967 gtk_widget_show(tab_bar
);
3968 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3974 statusbar_set_visibility(void)
3978 TAILQ_FOREACH(t
, &tabs
, entry
)
3979 if (show_statusbar
== 0) {
3980 gtk_widget_hide(t
->statusbar_box
);
3983 gtk_widget_show(t
->statusbar_box
);
3987 url_set(struct tab
*t
, int enable_url_entry
)
3992 show_url
= enable_url_entry
;
3994 if (enable_url_entry
) {
3995 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3996 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3997 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
3999 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4000 GTK_ENTRY_ICON_PRIMARY
);
4002 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4003 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4004 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4005 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4011 fullscreen(struct tab
*t
, struct karg
*args
)
4013 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4016 return (XT_CB_PASSTHROUGH
);
4018 if (show_url
== 0) {
4026 url_set_visibility();
4027 notebook_tab_set_visibility();
4029 return (XT_CB_HANDLED
);
4033 statustoggle(struct tab
*t
, struct karg
*args
)
4035 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4037 if (show_statusbar
== 1) {
4039 statusbar_set_visibility();
4040 } else if (show_statusbar
== 0) {
4042 statusbar_set_visibility();
4044 return (XT_CB_HANDLED
);
4048 urlaction(struct tab
*t
, struct karg
*args
)
4050 int rv
= XT_CB_HANDLED
;
4052 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4055 return (XT_CB_PASSTHROUGH
);
4059 if (show_url
== 0) {
4061 url_set_visibility();
4065 if (show_url
== 1) {
4067 url_set_visibility();
4075 tabaction(struct tab
*t
, struct karg
*args
)
4077 int rv
= XT_CB_HANDLED
;
4078 char *url
= args
->s
;
4082 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4085 return (XT_CB_PASSTHROUGH
);
4089 if (strlen(url
) > 0)
4090 create_new_tab(url
, NULL
, 1, args
->precount
);
4092 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4095 if (args
->precount
< 0)
4098 TAILQ_FOREACH(tt
, &tabs
, entry
)
4099 if (tt
->tab_id
== args
->precount
- 1) {
4104 case XT_TAB_DELQUIT
:
4105 if (gtk_notebook_get_n_pages(notebook
) > 1)
4111 if (strlen(url
) > 0)
4114 rv
= XT_CB_PASSTHROUGH
;
4120 if (show_tabs
== 0) {
4122 notebook_tab_set_visibility();
4126 if (show_tabs
== 1) {
4128 notebook_tab_set_visibility();
4131 case XT_TAB_NEXTSTYLE
:
4132 if (tab_style
== XT_TABS_NORMAL
) {
4133 tab_style
= XT_TABS_COMPACT
;
4134 recolor_compact_tabs();
4137 tab_style
= XT_TABS_NORMAL
;
4138 notebook_tab_set_visibility();
4140 case XT_TAB_UNDO_CLOSE
:
4141 if (undo_count
== 0) {
4142 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4147 u
= TAILQ_FIRST(&undos
);
4148 create_new_tab(u
->uri
, u
, 1, -1);
4150 TAILQ_REMOVE(&undos
, u
, entry
);
4152 /* u->history is freed in create_new_tab() */
4157 rv
= XT_CB_PASSTHROUGH
;
4171 resizetab(struct tab
*t
, struct karg
*args
)
4173 if (t
== NULL
|| args
== NULL
) {
4174 show_oops(NULL
, "resizetab invalid parameters");
4175 return (XT_CB_PASSTHROUGH
);
4178 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4179 t
->tab_id
, args
->i
);
4181 setzoom_webkit(t
, args
->i
);
4183 return (XT_CB_HANDLED
);
4187 movetab(struct tab
*t
, struct karg
*args
)
4191 if (t
== NULL
|| args
== NULL
) {
4192 show_oops(NULL
, "movetab invalid parameters");
4193 return (XT_CB_PASSTHROUGH
);
4196 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4197 t
->tab_id
, args
->i
);
4199 if (args
->i
>= XT_TAB_INVALID
)
4200 return (XT_CB_PASSTHROUGH
);
4202 if (TAILQ_EMPTY(&tabs
))
4203 return (XT_CB_PASSTHROUGH
);
4205 n
= gtk_notebook_get_n_pages(notebook
);
4206 dest
= gtk_notebook_get_current_page(notebook
);
4210 if (args
->precount
< 0)
4211 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4213 dest
= args
->precount
- 1;
4217 if (args
->precount
< 0)
4220 dest
-= args
->precount
% n
;
4233 return (XT_CB_PASSTHROUGH
);
4236 if (dest
< 0 || dest
>= n
)
4237 return (XT_CB_PASSTHROUGH
);
4238 if (t
->tab_id
== dest
) {
4239 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4240 return (XT_CB_HANDLED
);
4243 set_current_tab(dest
);
4245 return (XT_CB_HANDLED
);
4251 command(struct tab
*t
, struct karg
*args
)
4253 char *s
= NULL
, *ss
= NULL
;
4257 if (t
== NULL
|| args
== NULL
) {
4258 show_oops(NULL
, "command invalid parameters");
4259 return (XT_CB_PASSTHROUGH
);
4270 if (cmd_prefix
== 0)
4273 ss
= g_strdup_printf(":%d", cmd_prefix
);
4284 case XT_CMD_OPEN_CURRENT
:
4287 case XT_CMD_TABNEW_CURRENT
:
4288 if (!s
) /* FALL THROUGH? */
4290 if ((uri
= get_uri(t
)) != NULL
) {
4291 ss
= g_strdup_printf("%s%s", s
, uri
);
4296 show_oops(t
, "command: invalid opcode %d", args
->i
);
4297 return (XT_CB_PASSTHROUGH
);
4300 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4302 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4303 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4304 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4306 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4307 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4312 return (XT_CB_HANDLED
);
4316 * Return a new string with a download row (in html)
4317 * appended. Old string is freed.
4320 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4323 WebKitDownloadStatus stat
;
4324 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4326 char cur_sz
[FMT_SCALED_STRSIZE
];
4327 char tot_sz
[FMT_SCALED_STRSIZE
];
4330 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4332 /* All actions wil take this form:
4333 * xxxt://class/seskey
4335 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4336 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4338 stat
= webkit_download_get_status(dl
->download
);
4341 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4342 status_html
= g_strdup_printf("Finished");
4343 cmd_html
= g_strdup_printf(
4344 "<a href='%s%d/%d'>Remove</a>",
4345 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4347 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4348 /* gather size info */
4349 progress
= 100 * webkit_download_get_progress(dl
->download
);
4352 webkit_download_get_current_size(dl
->download
), cur_sz
);
4354 webkit_download_get_total_size(dl
->download
), tot_sz
);
4356 status_html
= g_strdup_printf(
4357 "<div style='width: 100%%' align='center'>"
4358 "<div class='progress-outer'>"
4359 "<div class='progress-inner' style='width: %.2f%%'>"
4360 "</div></div></div>"
4361 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4362 progress
, cur_sz
, tot_sz
, progress
);
4364 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4365 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4369 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4370 status_html
= g_strdup_printf("Cancelled");
4371 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4372 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4374 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4375 status_html
= g_strdup_printf("Error!");
4376 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4377 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4379 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4380 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4381 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4382 status_html
= g_strdup_printf("Starting");
4385 show_oops(t
, "%s: unknown download status", __func__
);
4388 new_html
= g_strdup_printf(
4389 "%s\n<tr><td>%s</td><td>%s</td>"
4390 "<td style='text-align:center'>%s</td></tr>\n",
4391 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4392 status_html
, cmd_html
);
4396 g_free(status_html
);
4407 * update all download tabs apart from one. Pass NULL if
4408 * you want to update all.
4411 update_download_tabs(struct tab
*apart_from
)
4414 if (!updating_dl_tabs
) {
4415 updating_dl_tabs
= 1; /* stop infinite recursion */
4416 TAILQ_FOREACH(t
, &tabs
, entry
)
4417 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4418 && (t
!= apart_from
))
4419 xtp_page_dl(t
, NULL
);
4420 updating_dl_tabs
= 0;
4425 * update all cookie tabs apart from one. Pass NULL if
4426 * you want to update all.
4429 update_cookie_tabs(struct tab
*apart_from
)
4432 if (!updating_cl_tabs
) {
4433 updating_cl_tabs
= 1; /* stop infinite recursion */
4434 TAILQ_FOREACH(t
, &tabs
, entry
)
4435 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4436 && (t
!= apart_from
))
4437 xtp_page_cl(t
, NULL
);
4438 updating_cl_tabs
= 0;
4443 * update all history tabs apart from one. Pass NULL if
4444 * you want to update all.
4447 update_history_tabs(struct tab
*apart_from
)
4451 if (!updating_hl_tabs
) {
4452 updating_hl_tabs
= 1; /* stop infinite recursion */
4453 TAILQ_FOREACH(t
, &tabs
, entry
)
4454 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4455 && (t
!= apart_from
))
4456 xtp_page_hl(t
, NULL
);
4457 updating_hl_tabs
= 0;
4461 /* cookie management XTP page */
4463 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4465 char *body
, *page
, *tmp
;
4466 int i
= 1; /* all ids start 1 */
4467 GSList
*sc
, *pc
, *pc_start
;
4469 char *type
, *table_headers
, *last_domain
;
4471 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4474 show_oops(NULL
, "%s invalid parameters", __func__
);
4478 /* Generate a new session key */
4479 if (!updating_cl_tabs
)
4480 generate_xtp_session_key(&cl_session_key
);
4483 table_headers
= g_strdup_printf("<table><tr>"
4486 "<th style='width:200px'>Value</th>"
4490 "<th>HTTP<br />only</th>"
4491 "<th style='width:40px'>Rm</th></tr>\n");
4493 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4494 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4498 last_domain
= strdup("");
4499 for (; sc
; sc
= sc
->next
) {
4502 if (strcmp(last_domain
, c
->domain
) != 0) {
4505 last_domain
= strdup(c
->domain
);
4509 body
= g_strdup_printf("%s</table>"
4511 body
, c
->domain
, table_headers
);
4515 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4516 c
->domain
, table_headers
);
4521 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4522 if (soup_cookie_equal(pc
->data
, c
)) {
4523 type
= "Session + Persistent";
4528 body
= g_strdup_printf(
4531 "<td style='word-wrap:normal'>%s</td>"
4533 " <textarea rows='4'>%s</textarea>"
4539 "<td style='text-align:center'>"
4540 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4547 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4562 soup_cookies_free(sc
);
4563 soup_cookies_free(pc
);
4565 /* small message if there are none */
4567 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4568 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4571 body
= g_strdup_printf("%s</table>", body
);
4574 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4576 g_free(table_headers
);
4577 g_free(last_domain
);
4579 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4580 update_cookie_tabs(t
);
4588 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4590 char *body
, *page
, *tmp
;
4592 int i
= 1; /* all ids start 1 */
4594 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4597 show_oops(NULL
, "%s invalid parameters", __func__
);
4601 /* Generate a new session key */
4602 if (!updating_hl_tabs
)
4603 generate_xtp_session_key(&hl_session_key
);
4606 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4607 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4609 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4611 body
= g_strdup_printf(
4613 "<td><a href='%s'>%s</a></td>"
4615 "<td style='text-align: center'>"
4616 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4617 body
, h
->uri
, h
->uri
, h
->title
,
4618 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4619 XT_XTP_HL_REMOVE
, i
);
4625 /* small message if there are none */
4628 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4629 "colspan='3'>No History</td></tr>\n", body
);
4634 body
= g_strdup_printf("%s</table>", body
);
4637 page
= get_html_page("History", body
, "", TRUE
);
4641 * update all history manager tabs as the xtp session
4642 * key has now changed. No need to update the current tab.
4643 * Already did that above.
4645 update_history_tabs(t
);
4647 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4654 * Generate a web page detailing the status of any downloads
4657 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4659 struct download
*dl
;
4660 char *body
, *page
, *tmp
;
4664 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4667 show_oops(NULL
, "%s invalid parameters", __func__
);
4672 * Generate a new session key for next page instance.
4673 * This only happens for the top level call to xtp_page_dl()
4674 * in which case updating_dl_tabs is 0.
4676 if (!updating_dl_tabs
)
4677 generate_xtp_session_key(&dl_session_key
);
4679 /* header - with refresh so as to update */
4680 if (refresh_interval
>= 1)
4681 ref
= g_strdup_printf(
4682 "<meta http-equiv='refresh' content='%u"
4683 ";url=%s%d/%s/%d' />\n",
4692 body
= g_strdup_printf("<div align='center'>"
4693 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4694 "</p><table><tr><th style='width: 60%%'>"
4695 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4696 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4698 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4699 body
= xtp_page_dl_row(t
, body
, dl
);
4703 /* message if no downloads in list */
4706 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4707 " style='text-align: center'>"
4708 "No downloads</td></tr>\n", body
);
4713 body
= g_strdup_printf("%s</table></div>", body
);
4716 page
= get_html_page("Downloads", body
, ref
, 1);
4721 * update all download manager tabs as the xtp session
4722 * key has now changed. No need to update the current tab.
4723 * Already did that above.
4725 update_download_tabs(t
);
4727 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4734 search(struct tab
*t
, struct karg
*args
)
4738 if (t
== NULL
|| args
== NULL
) {
4739 show_oops(NULL
, "search invalid parameters");
4742 if (t
->search_text
== NULL
) {
4743 if (global_search
== NULL
)
4744 return (XT_CB_PASSTHROUGH
);
4746 t
->search_text
= g_strdup(global_search
);
4747 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4748 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4752 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4753 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4756 case XT_SEARCH_NEXT
:
4757 d
= t
->search_forward
;
4759 case XT_SEARCH_PREV
:
4760 d
= !t
->search_forward
;
4763 return (XT_CB_PASSTHROUGH
);
4766 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4768 return (XT_CB_HANDLED
);
4771 struct settings_args
{
4777 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4780 struct settings_args
*sa
= cb_args
;
4785 if (s
->flags
& XT_SF_RUNTIME
)
4791 *sa
->body
= g_strdup_printf(
4793 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4794 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4806 set(struct tab
*t
, struct karg
*args
)
4808 char *body
, *page
, *tmp
;
4810 struct settings_args sa
;
4812 bzero(&sa
, sizeof sa
);
4816 body
= g_strdup_printf("<div align='center'><table><tr>"
4817 "<th align='left'>Setting</th>"
4818 "<th align='left'>Value</th></tr>\n");
4820 settings_walk(print_setting
, &sa
);
4823 /* small message if there are none */
4826 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4827 "colspan='2'>No settings</td></tr>\n", body
);
4832 body
= g_strdup_printf("%s</table></div>", body
);
4835 page
= get_html_page("Settings", body
, "", 0);
4839 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4843 return (XT_CB_PASSTHROUGH
);
4847 session_save(struct tab
*t
, char *filename
)
4852 if (strlen(filename
) == 0)
4855 if (filename
[0] == '.' || filename
[0] == '/')
4859 if (save_tabs(t
, &a
))
4861 strlcpy(named_session
, filename
, sizeof named_session
);
4869 session_open(struct tab
*t
, char *filename
)
4874 if (strlen(filename
) == 0)
4877 if (filename
[0] == '.' || filename
[0] == '/')
4881 a
.i
= XT_SES_CLOSETABS
;
4882 if (open_tabs(t
, &a
))
4885 strlcpy(named_session
, filename
, sizeof named_session
);
4893 session_delete(struct tab
*t
, char *filename
)
4895 char file
[PATH_MAX
];
4898 if (strlen(filename
) == 0)
4901 if (filename
[0] == '.' || filename
[0] == '/')
4904 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4908 if (!strcmp(filename
, named_session
))
4909 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4910 sizeof named_session
);
4918 session_cmd(struct tab
*t
, struct karg
*args
)
4920 char *filename
= args
->s
;
4925 if (args
->i
& XT_SHOW
)
4926 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4927 XT_SAVED_TABS_FILE
: named_session
);
4928 else if (args
->i
& XT_SAVE
) {
4929 if (session_save(t
, filename
)) {
4930 show_oops(t
, "Can't save session: %s",
4931 filename
? filename
: "INVALID");
4934 } else if (args
->i
& XT_OPEN
) {
4935 if (session_open(t
, filename
)) {
4936 show_oops(t
, "Can't open session: %s",
4937 filename
? filename
: "INVALID");
4940 } else if (args
->i
& XT_DELETE
) {
4941 if (session_delete(t
, filename
)) {
4942 show_oops(t
, "Can't delete session: %s",
4943 filename
? filename
: "INVALID");
4948 return (XT_CB_PASSTHROUGH
);
4952 * Make a hardcopy of the page
4955 print_page(struct tab
*t
, struct karg
*args
)
4957 WebKitWebFrame
*frame
;
4959 GtkPrintOperation
*op
;
4960 GtkPrintOperationAction action
;
4961 GtkPrintOperationResult print_res
;
4962 GError
*g_err
= NULL
;
4963 int marg_l
, marg_r
, marg_t
, marg_b
;
4965 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4967 ps
= gtk_page_setup_new();
4968 op
= gtk_print_operation_new();
4969 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4970 frame
= webkit_web_view_get_main_frame(t
->wv
);
4972 /* the default margins are too small, so we will bump them */
4973 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4974 XT_PRINT_EXTRA_MARGIN
;
4975 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4976 XT_PRINT_EXTRA_MARGIN
;
4977 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4978 XT_PRINT_EXTRA_MARGIN
;
4979 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4980 XT_PRINT_EXTRA_MARGIN
;
4983 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4984 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4985 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4986 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4988 gtk_print_operation_set_default_page_setup(op
, ps
);
4990 /* this appears to free 'op' and 'ps' */
4991 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4993 /* check it worked */
4994 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4995 show_oops(NULL
, "can't print: %s", g_err
->message
);
4996 g_error_free (g_err
);
5004 go_home(struct tab
*t
, struct karg
*args
)
5011 restart(struct tab
*t
, struct karg
*args
)
5015 a
.s
= XT_RESTART_TABS_FILE
;
5017 execvp(start_argv
[0], start_argv
);
5023 #define CTRL GDK_CONTROL_MASK
5024 #define MOD1 GDK_MOD1_MASK
5025 #define SHFT GDK_SHIFT_MASK
5027 /* inherent to GTK not all keys will be caught at all times */
5028 /* XXX sort key bindings */
5029 struct key_binding
{
5034 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5036 { "cookiejar", MOD1
, 0, GDK_j
},
5037 { "downloadmgr", MOD1
, 0, GDK_d
},
5038 { "history", MOD1
, 0, GDK_h
},
5039 { "print", CTRL
, 0, GDK_p
},
5040 { "search", 0, 0, GDK_slash
},
5041 { "searchb", 0, 0, GDK_question
},
5042 { "statustoggle", CTRL
, 0, GDK_n
},
5043 { "command", 0, 0, GDK_colon
},
5044 { "qa", CTRL
, 0, GDK_q
},
5045 { "restart", MOD1
, 0, GDK_q
},
5046 { "js toggle", CTRL
, 0, GDK_j
},
5047 { "cookie toggle", MOD1
, 0, GDK_c
},
5048 { "togglesrc", CTRL
, 0, GDK_s
},
5049 { "yankuri", 0, 0, GDK_y
},
5050 { "pasteuricur", 0, 0, GDK_p
},
5051 { "pasteurinew", 0, 0, GDK_P
},
5052 { "toplevel toggle", 0, 0, GDK_F4
},
5053 { "help", 0, 0, GDK_F1
},
5054 { "run_script", MOD1
, 0, GDK_r
},
5057 { "searchnext", 0, 0, GDK_n
},
5058 { "searchprevious", 0, 0, GDK_N
},
5061 { "focusaddress", 0, 0, GDK_F6
},
5062 { "focussearch", 0, 0, GDK_F7
},
5065 { "hinting", 0, 0, GDK_f
},
5067 /* custom stylesheet */
5068 { "userstyle", 0, 0, GDK_i
},
5071 { "goback", 0, 0, GDK_BackSpace
},
5072 { "goback", MOD1
, 0, GDK_Left
},
5073 { "goforward", SHFT
, 0, GDK_BackSpace
},
5074 { "goforward", MOD1
, 0, GDK_Right
},
5075 { "reload", 0, 0, GDK_F5
},
5076 { "reload", CTRL
, 0, GDK_r
},
5077 { "reload", CTRL
, 0, GDK_l
},
5078 { "favorites", MOD1
, 1, GDK_f
},
5080 /* vertical movement */
5081 { "scrolldown", 0, 0, GDK_j
},
5082 { "scrolldown", 0, 0, GDK_Down
},
5083 { "scrollup", 0, 0, GDK_Up
},
5084 { "scrollup", 0, 0, GDK_k
},
5085 { "scrollbottom", 0, 0, GDK_G
},
5086 { "scrollbottom", 0, 0, GDK_End
},
5087 { "scrolltop", 0, 0, GDK_Home
},
5088 { "scrollpagedown", 0, 0, GDK_space
},
5089 { "scrollpagedown", CTRL
, 0, GDK_f
},
5090 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5091 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5092 { "scrollpageup", 0, 0, GDK_Page_Up
},
5093 { "scrollpageup", CTRL
, 0, GDK_b
},
5094 { "scrollhalfup", CTRL
, 0, GDK_u
},
5095 /* horizontal movement */
5096 { "scrollright", 0, 0, GDK_l
},
5097 { "scrollright", 0, 0, GDK_Right
},
5098 { "scrollleft", 0, 0, GDK_Left
},
5099 { "scrollleft", 0, 0, GDK_h
},
5100 { "scrollfarright", 0, 0, GDK_dollar
},
5101 { "scrollfarleft", 0, 0, GDK_0
},
5104 { "tabnew", CTRL
, 0, GDK_t
},
5105 { "999tabnew", CTRL
, 0, GDK_T
},
5106 { "tabclose", CTRL
, 1, GDK_w
},
5107 { "tabundoclose", 0, 0, GDK_U
},
5108 { "tabnext 1", CTRL
, 0, GDK_1
},
5109 { "tabnext 2", CTRL
, 0, GDK_2
},
5110 { "tabnext 3", CTRL
, 0, GDK_3
},
5111 { "tabnext 4", CTRL
, 0, GDK_4
},
5112 { "tabnext 5", CTRL
, 0, GDK_5
},
5113 { "tabnext 6", CTRL
, 0, GDK_6
},
5114 { "tabnext 7", CTRL
, 0, GDK_7
},
5115 { "tabnext 8", CTRL
, 0, GDK_8
},
5116 { "tabnext 9", CTRL
, 0, GDK_9
},
5117 { "tabfirst", CTRL
, 0, GDK_less
},
5118 { "tablast", CTRL
, 0, GDK_greater
},
5119 { "tabprevious", CTRL
, 0, GDK_Left
},
5120 { "tabnext", CTRL
, 0, GDK_Right
},
5121 { "focusout", CTRL
, 0, GDK_minus
},
5122 { "focusin", CTRL
, 0, GDK_plus
},
5123 { "focusin", CTRL
, 0, GDK_equal
},
5124 { "focusreset", CTRL
, 0, GDK_0
},
5126 /* command aliases (handy when -S flag is used) */
5127 { "promptopen", 0, 0, GDK_F9
},
5128 { "promptopencurrent", 0, 0, GDK_F10
},
5129 { "prompttabnew", 0, 0, GDK_F11
},
5130 { "prompttabnewcurrent",0, 0, GDK_F12
},
5132 TAILQ_HEAD(keybinding_list
, key_binding
);
5135 walk_kb(struct settings
*s
,
5136 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5138 struct key_binding
*k
;
5141 if (s
== NULL
|| cb
== NULL
) {
5142 show_oops(NULL
, "walk_kb invalid parameters");
5146 TAILQ_FOREACH(k
, &kbl
, entry
) {
5152 if (gdk_keyval_name(k
->key
) == NULL
)
5155 strlcat(str
, k
->cmd
, sizeof str
);
5156 strlcat(str
, ",", sizeof str
);
5158 if (k
->mask
& GDK_SHIFT_MASK
)
5159 strlcat(str
, "S-", sizeof str
);
5160 if (k
->mask
& GDK_CONTROL_MASK
)
5161 strlcat(str
, "C-", sizeof str
);
5162 if (k
->mask
& GDK_MOD1_MASK
)
5163 strlcat(str
, "M1-", sizeof str
);
5164 if (k
->mask
& GDK_MOD2_MASK
)
5165 strlcat(str
, "M2-", sizeof str
);
5166 if (k
->mask
& GDK_MOD3_MASK
)
5167 strlcat(str
, "M3-", sizeof str
);
5168 if (k
->mask
& GDK_MOD4_MASK
)
5169 strlcat(str
, "M4-", sizeof str
);
5170 if (k
->mask
& GDK_MOD5_MASK
)
5171 strlcat(str
, "M5-", sizeof str
);
5173 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5174 cb(s
, str
, cb_args
);
5179 init_keybindings(void)
5182 struct key_binding
*k
;
5184 for (i
= 0; i
< LENGTH(keys
); i
++) {
5185 k
= g_malloc0(sizeof *k
);
5186 k
->cmd
= keys
[i
].cmd
;
5187 k
->mask
= keys
[i
].mask
;
5188 k
->use_in_entry
= keys
[i
].use_in_entry
;
5189 k
->key
= keys
[i
].key
;
5190 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5192 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5193 k
->cmd
? k
->cmd
: "unnamed key");
5198 keybinding_clearall(void)
5200 struct key_binding
*k
, *next
;
5202 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5203 next
= TAILQ_NEXT(k
, entry
);
5207 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5208 k
->cmd
? k
->cmd
: "unnamed key");
5209 TAILQ_REMOVE(&kbl
, k
, entry
);
5215 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5217 struct key_binding
*k
;
5218 guint keyval
, mask
= 0;
5221 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5223 /* Keys which are to be used in entry have been prefixed with an
5224 * exclamation mark. */
5228 /* find modifier keys */
5229 if (strstr(key
, "S-"))
5230 mask
|= GDK_SHIFT_MASK
;
5231 if (strstr(key
, "C-"))
5232 mask
|= GDK_CONTROL_MASK
;
5233 if (strstr(key
, "M1-"))
5234 mask
|= GDK_MOD1_MASK
;
5235 if (strstr(key
, "M2-"))
5236 mask
|= GDK_MOD2_MASK
;
5237 if (strstr(key
, "M3-"))
5238 mask
|= GDK_MOD3_MASK
;
5239 if (strstr(key
, "M4-"))
5240 mask
|= GDK_MOD4_MASK
;
5241 if (strstr(key
, "M5-"))
5242 mask
|= GDK_MOD5_MASK
;
5245 for (i
= strlen(key
) - 1; i
> 0; i
--)
5249 /* validate keyname */
5250 keyval
= gdk_keyval_from_name(key
);
5251 if (keyval
== GDK_VoidSymbol
) {
5252 warnx("invalid keybinding name %s", key
);
5255 /* must run this test too, gtk+ doesn't handle 10 for example */
5256 if (gdk_keyval_name(keyval
) == NULL
) {
5257 warnx("invalid keybinding name %s", key
);
5261 /* Remove eventual dupes. */
5262 TAILQ_FOREACH(k
, &kbl
, entry
)
5263 if (k
->key
== keyval
&& k
->mask
== mask
) {
5264 TAILQ_REMOVE(&kbl
, k
, entry
);
5270 k
= g_malloc0(sizeof *k
);
5271 k
->cmd
= g_strdup(cmd
);
5273 k
->use_in_entry
= use_in_entry
;
5276 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5281 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5282 k
->cmd
, gdk_keyval_name(keyval
));
5284 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5290 add_kb(struct settings
*s
, char *entry
)
5294 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5296 /* clearall is special */
5297 if (!strcmp(entry
, "clearall")) {
5298 keybinding_clearall();
5302 kb
= strstr(entry
, ",");
5308 return (keybinding_add(entry
, key
, key
[0] == '!'));
5314 int (*func
)(struct tab
*, struct karg
*);
5318 { "command", 0, command
, ':', 0 },
5319 { "search", 0, command
, '/', 0 },
5320 { "searchb", 0, command
, '?', 0 },
5321 { "togglesrc", 0, toggle_src
, 0, 0 },
5323 /* yanking and pasting */
5324 { "yankuri", 0, yank_uri
, 0, 0 },
5325 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5326 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5327 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5330 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5331 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5334 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5335 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5338 { "hinting", 0, hint
, 0, 0 },
5340 /* custom stylesheet */
5341 { "userstyle", 0, userstyle
, 0, 0 },
5344 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5345 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5346 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5348 /* vertical movement */
5349 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5350 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5351 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5352 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5353 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5354 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5355 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5356 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5357 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5358 /* horizontal movement */
5359 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5360 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5361 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5362 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5364 { "favorites", 0, xtp_page_fl
, 0, 0 },
5365 { "fav", 0, xtp_page_fl
, 0, 0 },
5366 { "favadd", 0, add_favorite
, 0, 0 },
5368 { "qall", 0, quit
, 0, 0 },
5369 { "quitall", 0, quit
, 0, 0 },
5370 { "w", 0, save_tabs
, 0, 0 },
5371 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5372 { "help", 0, help
, 0, 0 },
5373 { "about", 0, about
, 0, 0 },
5374 { "stats", 0, stats
, 0, 0 },
5375 { "version", 0, about
, 0, 0 },
5378 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5379 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5380 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5381 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5382 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5383 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5384 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5385 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5386 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5387 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5388 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5390 /* cookie command */
5391 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5392 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5393 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5394 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5395 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5396 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5397 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5398 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5399 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5400 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5401 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5403 /* toplevel (domain) command */
5404 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5405 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5408 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5411 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5412 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5413 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5415 { "ca", 0, ca_cmd
, 0, 0 },
5416 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5417 { "dl", 0, xtp_page_dl
, 0, 0 },
5418 { "h", 0, xtp_page_hl
, 0, 0 },
5419 { "history", 0, xtp_page_hl
, 0, 0 },
5420 { "home", 0, go_home
, 0, 0 },
5421 { "restart", 0, restart
, 0, 0 },
5422 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5423 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5424 { "statustoggle", 0, statustoggle
, 0, 0 },
5425 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5427 { "print", 0, print_page
, 0, 0 },
5430 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5431 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5432 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5433 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5434 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5435 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5436 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5437 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5438 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5439 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5440 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5441 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5442 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5443 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5444 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5445 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5446 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5447 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5448 { "buffers", 0, buffers
, 0, 0 },
5449 { "ls", 0, buffers
, 0, 0 },
5450 { "tabs", 0, buffers
, 0, 0 },
5452 /* command aliases (handy when -S flag is used) */
5453 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5454 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5455 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5456 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5459 { "set", 0, set
, 0, 0 },
5460 { "fullscreen", 0, fullscreen
, 0, 0 },
5461 { "f", 0, fullscreen
, 0, 0 },
5464 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5465 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5466 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5467 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5468 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5475 } cmd_status
= {-1, 0};
5478 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5481 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5488 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5495 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5497 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5503 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5505 a
.i
= XT_NAV_FORWARD
;
5515 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5517 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5519 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5526 * cancel, remove, etc. downloads
5529 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5531 struct download find
, *d
= NULL
;
5533 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5535 /* some commands require a valid download id */
5536 if (cmd
!= XT_XTP_DL_LIST
) {
5537 /* lookup download in question */
5539 d
= RB_FIND(download_list
, &downloads
, &find
);
5542 show_oops(t
, "%s: no such download", __func__
);
5547 /* decide what to do */
5549 case XT_XTP_DL_CANCEL
:
5550 webkit_download_cancel(d
->download
);
5552 case XT_XTP_DL_REMOVE
:
5553 webkit_download_cancel(d
->download
); /* just incase */
5554 g_object_unref(d
->download
);
5555 RB_REMOVE(download_list
, &downloads
, d
);
5557 case XT_XTP_DL_LIST
:
5561 show_oops(t
, "%s: unknown command", __func__
);
5564 xtp_page_dl(t
, NULL
);
5568 * Actions on history, only does one thing for now, but
5569 * we provide the function for future actions
5572 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5574 struct history
*h
, *next
;
5578 case XT_XTP_HL_REMOVE
:
5579 /* walk backwards, as listed in reverse */
5580 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5581 next
= RB_PREV(history_list
, &hl
, h
);
5583 RB_REMOVE(history_list
, &hl
, h
);
5584 g_free((gpointer
) h
->title
);
5585 g_free((gpointer
) h
->uri
);
5592 case XT_XTP_HL_LIST
:
5593 /* Nothing - just xtp_page_hl() below */
5596 show_oops(t
, "%s: unknown command", __func__
);
5600 xtp_page_hl(t
, NULL
);
5603 /* remove a favorite */
5605 remove_favorite(struct tab
*t
, int index
)
5607 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5608 char *new_favs
, *tmp
;
5613 /* open favorites */
5614 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5616 if ((f
= fopen(file
, "r")) == NULL
) {
5617 show_oops(t
, "%s: can't open favorites: %s",
5618 __func__
, strerror(errno
));
5622 /* build a string which will become the new favroites file */
5623 new_favs
= g_strdup("");
5626 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5627 if (feof(f
) || ferror(f
))
5629 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5636 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5637 if (feof(f
) || ferror(f
)) {
5638 show_oops(t
, "%s: can't parse favorites %s",
5639 __func__
, strerror(errno
));
5644 /* as long as this isn't the one we are deleting add to file */
5647 new_favs
= g_strdup_printf("%s%s\n%s\n",
5648 new_favs
, title
, uri
);
5660 /* write back new favorites file */
5661 if ((f
= fopen(file
, "w")) == NULL
) {
5662 show_oops(t
, "%s: can't open favorites: %s",
5663 __func__
, strerror(errno
));
5667 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5680 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5683 case XT_XTP_FL_LIST
:
5684 /* nothing, just the below call to xtp_page_fl() */
5686 case XT_XTP_FL_REMOVE
:
5687 remove_favorite(t
, arg
);
5690 show_oops(t
, "%s: invalid favorites command", __func__
);
5694 xtp_page_fl(t
, NULL
);
5698 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5701 case XT_XTP_CL_LIST
:
5702 /* nothing, just xtp_page_cl() */
5704 case XT_XTP_CL_REMOVE
:
5708 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5712 xtp_page_cl(t
, NULL
);
5715 /* link an XTP class to it's session key and handler function */
5716 struct xtp_despatch
{
5719 void (*handle_func
)(struct tab
*, uint8_t, int);
5722 struct xtp_despatch xtp_despatches
[] = {
5723 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5724 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5725 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5726 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5727 { XT_XTP_INVALID
, NULL
, NULL
}
5731 * is the url xtp protocol? (xxxt://)
5732 * if so, parse and despatch correct bahvior
5735 parse_xtp_url(struct tab
*t
, const char *url
)
5737 char *dup
= NULL
, *p
, *last
;
5738 uint8_t n_tokens
= 0;
5739 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5740 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5745 * tokens array meaning:
5747 * tokens[1] = session key
5748 * tokens[2] = action
5749 * tokens[3] = optional argument
5752 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5754 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5757 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5759 /* split out the url */
5760 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5761 (p
= strtok_r(NULL
, "/", &last
))) {
5763 tokens
[n_tokens
++] = p
;
5766 /* should be atleast three fields 'class/seskey/command/arg' */
5770 dsp
= xtp_despatches
;
5771 req_class
= atoi(tokens
[0]);
5772 while (dsp
->xtp_class
) {
5773 if (dsp
->xtp_class
== req_class
) {
5780 /* did we find one atall? */
5781 if (dsp_match
== NULL
) {
5782 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5786 /* check session key and call despatch function */
5787 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5788 ret
= TRUE
; /* all is well, this was a valid xtp request */
5789 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5802 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5804 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5806 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5809 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5814 show_oops(t
, "activate_uri_entry_cb no uri");
5818 uri
+= strspn(uri
, "\t ");
5820 /* if xxxt:// treat specially */
5821 if (parse_xtp_url(t
, uri
))
5824 /* otherwise continue to load page normally */
5825 load_uri(t
, (gchar
*)uri
);
5830 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5832 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5833 char *newuri
= NULL
;
5836 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5839 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5843 if (search_string
== NULL
) {
5844 show_oops(t
, "no search_string");
5848 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5850 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5851 newuri
= g_strdup_printf(search_string
, enc_search
);
5855 webkit_web_view_load_uri(t
->wv
, newuri
);
5863 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5865 struct domain
*d
= NULL
;
5868 if (uri
== NULL
|| t
== NULL
)
5871 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5876 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5877 es
? "enable" : "disable", uri
);
5879 g_object_set(G_OBJECT(t
->settings
),
5880 "enable-scripts", es
, (char *)NULL
);
5881 g_object_set(G_OBJECT(t
->settings
),
5882 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5883 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5885 button_set_stockid(t
->js_toggle
,
5886 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5890 color_address_bar(gpointer p
)
5893 struct tab
*tt
, *t
= p
;
5894 gchar
*col_str
= XT_COLOR_YELLOW
;
5896 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
5898 /* make sure t still exists */
5901 TAILQ_FOREACH(tt
, &tabs
, entry
)
5907 switch (load_compare_cert(t
, NULL
)) {
5909 col_str
= XT_COLOR_BLUE
;
5912 col_str
= XT_COLOR_GREEN
;
5914 case CERT_UNTRUSTED
:
5915 col_str
= XT_COLOR_YELLOW
;
5918 col_str
= XT_COLOR_RED
;
5922 gdk_color_parse(col_str
, &color
);
5923 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5925 if (!strcmp(col_str
, XT_COLOR_WHITE
))
5926 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
5928 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
5932 return (FALSE
/* kill thread */);
5936 show_ca_status(struct tab
*t
, const char *uri
)
5939 gchar
*col_str
= XT_COLOR_WHITE
;
5941 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5942 ssl_strict_certs
, ssl_ca_file
, uri
);
5949 if (ssl_ca_file
== NULL
) {
5950 if (g_str_has_prefix(uri
, "http://"))
5952 if (g_str_has_prefix(uri
, "https://")) {
5953 col_str
= XT_COLOR_RED
;
5958 if (g_str_has_prefix(uri
, "http://") ||
5959 !g_str_has_prefix(uri
, "https://"))
5962 /* thread the coloring of the address bar */
5963 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
,
5964 color_address_bar
, t
, NULL
);
5969 gdk_color_parse(col_str
, &color
);
5970 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5972 if (!strcmp(col_str
, XT_COLOR_WHITE
))
5973 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
5975 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
5980 free_favicon(struct tab
*t
)
5982 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5983 __func__
, t
->icon_download
, t
->icon_request
);
5985 if (t
->icon_request
)
5986 g_object_unref(t
->icon_request
);
5987 if (t
->icon_dest_uri
)
5988 g_free(t
->icon_dest_uri
);
5990 t
->icon_request
= NULL
;
5991 t
->icon_dest_uri
= NULL
;
5995 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5997 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5998 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6000 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6001 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6003 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6004 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6008 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6010 GdkPixbuf
*pb_scaled
;
6012 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6013 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6014 GDK_INTERP_BILINEAR
);
6018 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6019 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6021 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6022 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6024 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6025 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6027 if (pb_scaled
!= pb
)
6028 g_object_unref(pb_scaled
);
6032 xt_icon_from_file(struct tab
*t
, char *file
)
6036 if (g_str_has_prefix(file
, "file://"))
6037 file
+= strlen("file://");
6039 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6041 xt_icon_from_pixbuf(t
, pb
);
6044 xt_icon_from_name(t
, "text-html");
6048 is_valid_icon(char *file
)
6051 const char *mime_type
;
6055 gf
= g_file_new_for_path(file
);
6056 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6058 mime_type
= g_file_info_get_content_type(fi
);
6059 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6060 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6061 g_strcmp0(mime_type
, "image/png") == 0 ||
6062 g_strcmp0(mime_type
, "image/gif") == 0 ||
6063 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6071 set_favicon_from_file(struct tab
*t
, char *file
)
6075 if (t
== NULL
|| file
== NULL
)
6078 if (g_str_has_prefix(file
, "file://"))
6079 file
+= strlen("file://");
6080 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6082 if (!stat(file
, &sb
)) {
6083 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6084 /* corrupt icon so trash it */
6085 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6088 /* no need to set icon to default here */
6092 xt_icon_from_file(t
, file
);
6096 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6099 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6100 struct tab
*tt
= NULL
, *t
= NULL
;
6103 * find the webview instead of passing in the tab as it could have been
6104 * deleted from underneath us.
6106 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6115 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6116 __func__
, t
->tab_id
, status
);
6119 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6121 t
->icon_download
= NULL
;
6124 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6127 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6130 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6132 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6133 __func__
, t
->tab_id
);
6134 t
->icon_download
= NULL
;
6137 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6140 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6141 __func__
, t
->icon_dest_uri
);
6142 set_favicon_from_file(t
, t
->icon_dest_uri
);
6143 /* these will be freed post callback */
6144 t
->icon_request
= NULL
;
6145 t
->icon_download
= NULL
;
6153 abort_favicon_download(struct tab
*t
)
6155 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6157 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6158 if (t
->icon_download
) {
6159 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6160 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6161 webkit_download_cancel(t
->icon_download
);
6162 t
->icon_download
= NULL
;
6167 xt_icon_from_name(t
, "text-html");
6171 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6173 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6175 if (uri
== NULL
|| t
== NULL
)
6178 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6179 /* take icon from WebKitIconDatabase */
6182 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6184 xt_icon_from_pixbuf(t
, pb
);
6187 xt_icon_from_name(t
, "text-html");
6188 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6189 /* download icon to cache dir */
6190 gchar
*name_hash
, file
[PATH_MAX
];
6193 if (t
->icon_request
) {
6194 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6198 /* check to see if we got the icon in cache */
6199 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6200 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6203 if (!stat(file
, &sb
)) {
6204 if (sb
.st_size
> 0) {
6205 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6207 set_favicon_from_file(t
, file
);
6211 /* corrupt icon so trash it */
6212 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6217 /* create download for icon */
6218 t
->icon_request
= webkit_network_request_new(uri
);
6219 if (t
->icon_request
== NULL
) {
6220 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6225 t
->icon_download
= webkit_download_new(t
->icon_request
);
6226 if (t
->icon_download
== NULL
)
6229 /* we have to free icon_dest_uri later */
6230 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6231 webkit_download_set_destination_uri(t
->icon_download
,
6234 if (webkit_download_get_status(t
->icon_download
) ==
6235 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6236 g_object_unref(t
->icon_request
);
6237 g_free(t
->icon_dest_uri
);
6238 t
->icon_request
= NULL
;
6239 t
->icon_dest_uri
= NULL
;
6243 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6244 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6246 webkit_download_start(t
->icon_download
);
6251 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6253 const gchar
*uri
= NULL
, *title
= NULL
;
6254 struct history
*h
, find
;
6258 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6259 webkit_web_view_get_load_status(wview
),
6260 get_uri(t
) ? get_uri(t
) : "NOTHING");
6263 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6267 switch (webkit_web_view_get_load_status(wview
)) {
6268 case WEBKIT_LOAD_PROVISIONAL
:
6270 abort_favicon_download(t
);
6271 #if GTK_CHECK_VERSION(2, 20, 0)
6272 gtk_widget_show(t
->spinner
);
6273 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6275 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6277 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6279 /* assume we are a new address */
6280 gdk_color_parse("white", &color
);
6281 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6282 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6284 /* take focus if we are visible */
6290 case WEBKIT_LOAD_COMMITTED
:
6295 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6301 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6303 /* check if js white listing is enabled */
6304 if (enable_js_whitelist
) {
6305 check_and_set_js(uri
, t
);
6312 /* we know enough to autosave the session */
6313 if (session_autosave
) {
6318 show_ca_status(t
, uri
);
6321 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6325 case WEBKIT_LOAD_FINISHED
:
6331 if (!strncmp(uri
, "http://", strlen("http://")) ||
6332 !strncmp(uri
, "https://", strlen("https://")) ||
6333 !strncmp(uri
, "file://", strlen("file://"))) {
6335 h
= RB_FIND(history_list
, &hl
, &find
);
6337 title
= get_title(t
, FALSE
);
6338 h
= g_malloc(sizeof *h
);
6339 h
->uri
= g_strdup(uri
);
6340 h
->title
= g_strdup(title
);
6341 RB_INSERT(history_list
, &hl
, h
);
6342 completion_add_uri(h
->uri
);
6343 update_history_tabs(NULL
);
6347 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6348 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6349 case WEBKIT_LOAD_FAILED
:
6352 #if GTK_CHECK_VERSION(2, 20, 0)
6353 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6354 gtk_widget_hide(t
->spinner
);
6357 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6362 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6364 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6365 webkit_web_view_can_go_back(wview
));
6367 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6368 webkit_web_view_can_go_forward(wview
));
6373 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6374 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6377 * XXX this function is wrong
6378 * it overwrites perfectly good urls with garbage on load errors
6379 * those happen often when popups fail to resolve dns
6383 t
->tmp_uri
= g_strdup(uri
);
6384 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6385 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6386 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6387 set_status(t
, uri
, XT_STATUS_NOTHING
);
6394 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6396 const gchar
*title
= NULL
, *win_title
= NULL
;
6398 title
= get_title(t
, FALSE
);
6399 win_title
= get_title(t
, TRUE
);
6400 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6401 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6402 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6403 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6407 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6409 run_script(t
, JS_HINTING
);
6413 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6415 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6416 progress
== 100 ? 0 : (double)progress
/ 100);
6417 if (show_url
== 0) {
6418 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6419 progress
== 100 ? 0 : (double)progress
/ 100);
6422 update_statusbar_position(NULL
, NULL
);
6426 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6427 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6428 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6431 WebKitWebNavigationReason reason
;
6432 struct domain
*d
= NULL
;
6435 show_oops(NULL
, "webview_npd_cb invalid parameters");
6439 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6441 webkit_network_request_get_uri(request
));
6443 uri
= (char *)webkit_network_request_get_uri(request
);
6445 /* if this is an xtp url, we don't load anything else */
6446 if (parse_xtp_url(t
, uri
))
6449 if (t
->ctrl_click
) {
6451 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6452 webkit_web_policy_decision_ignore(pd
);
6453 return (TRUE
); /* we made the decission */
6457 * This is a little hairy but it comes down to this:
6458 * when we run in whitelist mode we have to assist the browser in
6459 * opening the URL that it would have opened in a new tab.
6461 reason
= webkit_web_navigation_action_get_reason(na
);
6462 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6463 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6464 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6465 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6467 webkit_web_policy_decision_use(pd
);
6468 return (TRUE
); /* we made the decision */
6475 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6478 struct domain
*d
= NULL
;
6480 WebKitWebView
*webview
= NULL
;
6482 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6483 webkit_web_view_get_uri(wv
));
6486 /* open in current tab */
6488 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6489 uri
= webkit_web_view_get_uri(wv
);
6490 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6493 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6495 } else if (enable_scripts
== 1) {
6496 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6504 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6507 struct domain
*d
= NULL
;
6509 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6511 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6512 uri
= webkit_web_view_get_uri(wv
);
6513 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6517 } else if (enable_scripts
== 1)
6524 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6526 /* we can not eat the event without throwing gtk off so defer it */
6528 /* catch middle click */
6529 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6534 /* catch ctrl click */
6535 if (e
->type
== GDK_BUTTON_RELEASE
&&
6536 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6541 return (XT_CB_PASSTHROUGH
);
6545 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6547 struct mime_type
*m
;
6549 m
= find_mime_type(mime_type
);
6557 show_oops(t
, "can't fork mime handler");
6567 execlp(m
->mt_action
, m
->mt_action
,
6568 webkit_network_request_get_uri(request
), (void *)NULL
);
6577 get_mime_type(char *file
)
6579 const char *mime_type
;
6583 if (g_str_has_prefix(file
, "file://"))
6584 file
+= strlen("file://");
6586 gf
= g_file_new_for_path(file
);
6587 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6589 mime_type
= g_file_info_get_content_type(fi
);
6597 run_download_mimehandler(char *mime_type
, char *file
)
6599 struct mime_type
*m
;
6601 m
= find_mime_type(mime_type
);
6607 show_oops(NULL
, "can't fork download mime handler");
6617 if (g_str_has_prefix(file
, "file://"))
6618 file
+= strlen("file://");
6619 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6628 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6631 WebKitDownloadStatus status
;
6632 const gchar
*file
= NULL
, *mime
= NULL
;
6634 if (download
== NULL
)
6636 status
= webkit_download_get_status(download
);
6637 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6640 file
= webkit_download_get_destination_uri(download
);
6643 mime
= get_mime_type((char *)file
);
6647 run_download_mimehandler((char *)mime
, (char *)file
);
6651 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6652 WebKitNetworkRequest
*request
, char *mime_type
,
6653 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6656 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6660 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6661 t
->tab_id
, mime_type
);
6663 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6664 webkit_web_policy_decision_ignore(decision
);
6669 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6670 webkit_web_policy_decision_download(decision
);
6678 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6682 const gchar
*suggested_name
;
6683 gchar
*filename
= NULL
;
6685 struct download
*download_entry
;
6688 if (wk_download
== NULL
|| t
== NULL
) {
6689 show_oops(NULL
, "%s invalid parameters", __func__
);
6693 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6694 if (suggested_name
== NULL
)
6695 return (FALSE
); /* abort download */
6706 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6708 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6709 filename
: suggested_name
);
6711 } while (!stat(uri
+ strlen("file://"), &sb
));
6713 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6714 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6716 webkit_download_set_destination_uri(wk_download
, uri
);
6718 if (webkit_download_get_status(wk_download
) ==
6719 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6720 show_oops(t
, "%s: download failed to start", __func__
);
6722 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6724 /* connect "download first" mime handler */
6725 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6726 G_CALLBACK(download_status_changed_cb
), NULL
);
6728 download_entry
= g_malloc(sizeof(struct download
));
6729 download_entry
->download
= wk_download
;
6730 download_entry
->tab
= t
;
6731 download_entry
->id
= next_download_id
++;
6732 RB_INSERT(download_list
, &downloads
, download_entry
);
6733 /* get from history */
6734 g_object_ref(wk_download
);
6735 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6736 show_oops(t
, "Download of '%s' started...",
6737 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6746 /* sync other download manager tabs */
6747 update_download_tabs(NULL
);
6750 * NOTE: never redirect/render the current tab before this
6751 * function returns. This will cause the download to never start.
6753 return (ret
); /* start download */
6757 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6759 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6762 show_oops(NULL
, "webview_hover_cb");
6767 set_status(t
, uri
, XT_STATUS_LINK
);
6770 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6775 mark(struct tab
*t
, struct karg
*arg
)
6781 if ((index
= marktoindex(mark
)) == -1)
6784 if (arg
->i
== XT_MARK_SET
)
6785 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6786 else if (arg
->i
== XT_MARK_GOTO
) {
6787 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6788 show_oops(t
, "mark '%c' does not exist", mark
);
6791 /* XXX t->mark[index] can be bigger than the maximum if ajax or
6792 something changes the document size */
6793 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
6800 marks_clear(struct tab
*t
)
6804 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
6805 t
->mark
[i
] = XT_INVALID_MARK
;
6811 char file
[PATH_MAX
];
6812 char *line
= NULL
, *p
, mark
;
6817 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6818 if ((f
= fopen(file
, "r+")) == NULL
) {
6819 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6823 for (i
= 1; ; i
++) {
6824 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
6825 if (feof(f
) || ferror(f
))
6827 if (strlen(line
) == 0 || line
[0] == '#') {
6833 p
= strtok(line
, " \t");
6835 if (p
== NULL
|| strlen(p
) != 1 ||
6836 (index
= marktoindex(*p
)) == -1) {
6837 warnx("corrupt quickmarks file, line %d", i
);
6842 p
= strtok(NULL
, " \t");
6843 if (qmarks
[index
] != NULL
)
6844 g_free(qmarks
[index
]);
6845 qmarks
[index
] = g_strdup(p
);
6856 char file
[PATH_MAX
];
6860 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6861 if ((f
= fopen(file
, "r+")) == NULL
) {
6862 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6866 for (i
= 0; i
< XT_NOMARKS
; i
++)
6867 if (qmarks
[i
] != NULL
)
6868 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
6876 qmark(struct tab
*t
, struct karg
*arg
)
6881 mark
= arg
->s
[strlen(arg
->s
)-1];
6882 index
= marktoindex(mark
);
6888 if (qmarks
[index
] != NULL
)
6889 g_free(qmarks
[index
]);
6891 qmarks_load(); /* sync if multiple instances */
6892 qmarks
[index
] = g_strdup(get_uri(t
));
6896 if (qmarks
[index
] != NULL
)
6897 load_uri(t
, qmarks
[index
]);
6899 show_oops(t
, "quickmark \"%c\" does not exist",
6905 if (qmarks
[index
] != NULL
)
6906 create_new_tab(qmarks
[index
], NULL
, 1, -1);
6908 show_oops(t
, "quickmark \"%c\" does not exist",
6919 go_up(struct tab
*t
, struct karg
*args
)
6925 levels
= atoi(args
->s
);
6929 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
6930 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
6932 tmp
+= strlen(XT_PROTO_DELIM
);
6934 /* if an uri starts with a slash, leave it alone (for file:///) */
6941 p
= strrchr(tmp
, '/');
6955 gototab(struct tab
*t
, struct karg
*args
)
6958 struct karg arg
= {0, NULL
, -1};
6960 tab
= atoi(args
->s
);
6962 arg
.i
= XT_TAB_NEXT
;
6971 zoom_amount(struct tab
*t
, struct karg
*arg
)
6973 struct karg narg
= {0, NULL
, -1};
6975 narg
.i
= atoi(arg
->s
);
6976 resizetab(t
, &narg
);
6982 flip_colon(struct tab
*t
, struct karg
*arg
)
6984 struct karg narg
= {0, NULL
, -1};
6987 if (t
== NULL
|| arg
== NULL
)
6990 p
= strstr(arg
->s
, ":");
7002 /* buffer commands receive the regex that triggered them in arg.s */
7003 char bcmd
[XT_BUFCMD_SZ
];
7007 #define XT_PRE_NO (0)
7008 #define XT_PRE_YES (1)
7009 #define XT_PRE_MAYBE (2)
7011 int (*func
)(struct tab
*, struct karg
*);
7015 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7016 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7017 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7018 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7019 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7020 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7021 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7022 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7023 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7024 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7025 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7026 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7027 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7028 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7029 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7030 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7031 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7032 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7036 buffercmd_init(void)
7040 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7041 regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7046 buffercmd_abort(struct tab
*t
)
7050 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7051 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7054 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7055 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7059 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7061 struct karg arg
= {0, NULL
, -1};
7064 arg
.s
= g_strdup(bcmd
);
7066 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7067 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7077 buffercmd_addkey(struct tab
*t
, guint keyval
)
7080 char s
[XT_BUFCMD_SZ
];
7082 if (keyval
== GDK_Escape
) {
7084 return (XT_CB_HANDLED
);
7087 /* key with modifier or non-ascii character */
7088 if (!isascii(keyval
))
7089 return (XT_CB_PASSTHROUGH
);
7091 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7092 "to buffer \"%s\"\n", keyval
, bcmd
);
7094 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7095 if (bcmd
[i
] == '\0') {
7100 /* buffer full, ignore input */
7101 if (i
>= LENGTH(bcmd
) -1) {
7102 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7104 return (XT_CB_HANDLED
);
7107 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7109 /* find exact match */
7110 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7111 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7112 (size_t) 0, NULL
, 0) == 0) {
7113 buffercmd_execute(t
, &buffercmds
[i
]);
7117 /* find non exact matches to see if we need to abort ot not */
7118 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7119 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7122 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7123 if (isdigit(bcmd
[0])) {
7124 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7128 if (sscanf(bcmd
, "%s", s
) == 0)
7131 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7132 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7135 if (sscanf(bcmd
, "%s", s
) == 0)
7138 if (c
== -1 && buffercmds
[i
].precount
)
7140 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7143 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7144 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7147 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7152 return (XT_CB_HANDLED
);
7156 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7158 struct key_binding
*k
;
7160 /* handle keybindings if buffercmd is empty.
7161 if not empty, allow commands like C-n */
7162 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7163 TAILQ_FOREACH(k
, &kbl
, entry
)
7164 if (e
->keyval
== k
->key
7165 && (entry
? k
->use_in_entry
: 1)) {
7167 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7168 return (cmd_execute(t
, k
->cmd
));
7169 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7170 return (cmd_execute(t
, k
->cmd
));
7174 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7175 return buffercmd_addkey(t
, e
->keyval
);
7177 return (XT_CB_PASSTHROUGH
);
7181 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7183 char s
[2], buf
[128];
7184 const char *errstr
= NULL
;
7186 /* don't use w directly; use t->whatever instead */
7189 show_oops(NULL
, "wv_keypress_after_cb");
7190 return (XT_CB_PASSTHROUGH
);
7193 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7194 e
->keyval
, e
->state
, t
);
7198 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7200 return (XT_CB_HANDLED
);
7204 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7206 /* we have a string */
7208 /* we have a number */
7209 snprintf(buf
, sizeof buf
,
7210 "vimprobable_fire(%s)", t
->hint_num
);
7217 /* XXX unfuck this */
7218 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7219 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7220 /* last input was numerical */
7222 l
= strlen(t
->hint_num
);
7229 t
->hint_num
[l
] = '\0';
7233 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7234 /* last input was alphanumerical */
7236 l
= strlen(t
->hint_buf
);
7243 t
->hint_buf
[l
] = '\0';
7253 /* numerical input */
7254 if (CLEAN(e
->state
) == 0 &&
7255 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7256 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7257 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7258 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7259 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7263 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7264 "invalid link number\n");
7267 snprintf(buf
, sizeof buf
,
7268 "vimprobable_update_hints(%s)",
7270 t
->hint_mode
= XT_HINT_NUMERICAL
;
7274 /* empty the counter buffer */
7275 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7276 return (XT_CB_HANDLED
);
7279 /* alphanumerical input */
7280 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7281 e
->keyval
<= GDK_z
) ||
7282 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7283 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7284 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7285 e
->keyval
<= GDK_9
) ||
7286 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7287 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7288 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7289 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7290 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7291 " %s\n", t
->hint_buf
);
7293 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7296 snprintf(buf
, sizeof buf
,
7297 "vimprobable_show_hints('%s')", t
->hint_buf
);
7298 t
->hint_mode
= XT_HINT_ALPHANUM
;
7301 /* empty the counter buffer */
7302 bzero(t
->hint_num
, sizeof t
->hint_num
);
7303 return (XT_CB_HANDLED
);
7306 return (XT_CB_HANDLED
);
7309 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7310 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7311 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7314 return (handle_keypress(t
, e
, 0));
7318 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7322 /* Hide buffers, if they are visible, with escape. */
7323 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7324 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7325 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7327 return (XT_CB_HANDLED
);
7330 return (XT_CB_PASSTHROUGH
);
7334 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7336 const gchar
*c
= gtk_entry_get_text(w
);
7340 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7341 e
->keyval
, e
->state
, t
);
7344 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7345 return (XT_CB_PASSTHROUGH
);
7348 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7349 e
->keyval
, e
->state
, t
);
7353 if (strlen(c
) == 1) {
7354 webkit_web_view_unmark_text_matches(t
->wv
);
7360 else if (c
[0] == '?')
7366 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
7368 /* not found, mark red */
7369 gdk_color_parse(XT_COLOR_RED
, &color
);
7370 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7371 /* unmark and remove selection */
7372 webkit_web_view_unmark_text_matches(t
->wv
);
7373 /* my kingdom for a way to unselect text in webview */
7375 /* found, highlight all */
7376 webkit_web_view_unmark_text_matches(t
->wv
);
7377 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7378 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7379 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7380 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7383 return (XT_CB_PASSTHROUGH
);
7387 match_uri(const gchar
*uri
, const gchar
*key
) {
7390 gboolean match
= FALSE
;
7394 if (!strncmp(key
, uri
, len
))
7397 voffset
= strstr(uri
, "/") + 2;
7398 if (!strncmp(key
, voffset
, len
))
7400 else if (g_str_has_prefix(voffset
, "www.")) {
7401 voffset
= voffset
+ strlen("www.");
7402 if (!strncmp(key
, voffset
, len
))
7411 cmd_getlist(int id
, char *key
)
7416 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7417 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7418 if (match_uri(h
->uri
, key
)) {
7419 cmd_status
.list
[c
] = (char *)h
->uri
;
7428 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7430 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7431 if (cmds
[i
].level
< dep
)
7433 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7435 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7443 cmd_getnext(int dir
)
7445 cmd_status
.index
+= dir
;
7447 if (cmd_status
.index
< 0)
7448 cmd_status
.index
= cmd_status
.len
- 1;
7449 else if (cmd_status
.index
>= cmd_status
.len
)
7450 cmd_status
.index
= 0;
7452 return cmd_status
.list
[cmd_status
.index
];
7456 cmd_tokenize(char *s
, char *tokens
[])
7460 size_t len
= strlen(s
);
7463 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7464 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7465 tok
= strtok_r(NULL
, " ", &last
), i
++)
7475 cmd_complete(struct tab
*t
, char *str
, int dir
)
7477 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7478 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7480 char *tok
, *match
, *s
= g_strdup(str
);
7482 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7485 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7488 for (i
= 0; isdigit(s
[i
]); i
++)
7491 for (; isspace(s
[i
]); i
++)
7496 levels
= cmd_tokenize(s
, tokens
);
7498 for (i
= 0; i
< levels
- 1; i
++) {
7501 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7502 if (cmds
[j
].level
< dep
)
7504 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7508 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7515 if (matchcount
== 1) {
7516 strlcat(res
, tok
, sizeof res
);
7517 strlcat(res
, " ", sizeof res
);
7527 if (cmd_status
.index
== -1)
7528 cmd_getlist(parent
, tokens
[i
]);
7530 if (cmd_status
.len
> 0) {
7531 match
= cmd_getnext(dir
);
7532 strlcat(res
, match
, sizeof res
);
7533 gtk_entry_set_text(w
, res
);
7534 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7541 cmd_execute(struct tab
*t
, char *str
)
7543 struct cmd
*cmd
= NULL
;
7544 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7546 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7547 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7548 struct karg arg
= {0, NULL
, -1};
7553 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7559 while (isspace(s
[0]))
7562 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7563 prefix
= atoi(prefixstr
);
7567 for (tok
= strtok_r(s
, " ", &last
); tok
;
7568 tok
= strtok_r(NULL
, " ", &last
)) {
7570 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7571 if (cmds
[j
].level
< dep
)
7573 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7575 if (cmds
[j
].level
== dep
&&
7576 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7580 if (len
== strlen(cmds
[j
].cmd
)) {
7586 if (matchcount
== 1) {
7591 show_oops(t
, "Invalid command: %s", str
);
7599 arg
.precount
= prefix
;
7600 else if (cmd_prefix
> 0)
7601 arg
.precount
= cmd_prefix
;
7603 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
7604 show_oops(t
, "No prefix allowed: %s", str
);
7608 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7609 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7610 arg
.precount
= atoi(arg
.s
);
7611 if (arg
.precount
<= 0) {
7612 if (arg
.s
[0] == '0')
7613 show_oops(t
, "Zero count");
7615 show_oops(t
, "Trailing characters");
7620 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
7621 __func__
, arg
.precount
, arg
.s
);
7637 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7640 show_oops(NULL
, "entry_key_cb invalid parameters");
7641 return (XT_CB_PASSTHROUGH
);
7644 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7645 e
->keyval
, e
->state
, t
);
7649 if (e
->keyval
== GDK_Escape
) {
7650 /* don't use focus_webview(t) because we want to type :cmds */
7651 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7654 return (handle_keypress(t
, e
, 1));
7658 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7660 int rv
= XT_CB_HANDLED
;
7661 const gchar
*c
= gtk_entry_get_text(w
);
7664 show_oops(NULL
, "cmd_keypress_cb parameters");
7665 return (XT_CB_PASSTHROUGH
);
7668 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7669 e
->keyval
, e
->state
, t
);
7673 e
->keyval
= GDK_Escape
;
7674 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7675 e
->keyval
= GDK_Escape
;
7677 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7678 e
->keyval
!= GDK_ISO_Left_Tab
)
7679 cmd_status
.index
= -1;
7681 switch (e
->keyval
) {
7684 cmd_complete(t
, (char *)&c
[1], 1);
7686 case GDK_ISO_Left_Tab
:
7688 cmd_complete(t
, (char *)&c
[1], -1);
7692 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7700 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7701 webkit_web_view_unmark_text_matches(t
->wv
);
7705 rv
= XT_CB_PASSTHROUGH
;
7711 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7714 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7715 return (XT_CB_PASSTHROUGH
);
7717 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7722 if (show_url
== 0 || t
->focus_wv
)
7725 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7727 return (XT_CB_PASSTHROUGH
);
7731 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7734 const gchar
*c
= gtk_entry_get_text(entry
);
7737 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7741 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7748 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7754 if (c
[0] == '/' || c
[0] == '?') {
7755 if (t
->search_text
) {
7756 g_free(t
->search_text
);
7757 t
->search_text
= NULL
;
7760 t
->search_text
= g_strdup(s
);
7762 g_free(global_search
);
7763 global_search
= g_strdup(s
);
7764 t
->search_forward
= c
[0] == '/';
7776 backward_cb(GtkWidget
*w
, struct tab
*t
)
7781 show_oops(NULL
, "backward_cb invalid parameters");
7785 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7792 forward_cb(GtkWidget
*w
, struct tab
*t
)
7797 show_oops(NULL
, "forward_cb invalid parameters");
7801 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7803 a
.i
= XT_NAV_FORWARD
;
7808 home_cb(GtkWidget
*w
, struct tab
*t
)
7811 show_oops(NULL
, "home_cb invalid parameters");
7815 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7821 stop_cb(GtkWidget
*w
, struct tab
*t
)
7823 WebKitWebFrame
*frame
;
7826 show_oops(NULL
, "stop_cb invalid parameters");
7830 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7832 frame
= webkit_web_view_get_main_frame(t
->wv
);
7833 if (frame
== NULL
) {
7834 show_oops(t
, "stop_cb: no frame");
7838 webkit_web_frame_stop_loading(frame
);
7839 abort_favicon_download(t
);
7843 setup_webkit(struct tab
*t
)
7845 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7846 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7847 FALSE
, (char *)NULL
);
7849 warnx("webkit does not have \"enable-dns-prefetching\" property");
7850 g_object_set(G_OBJECT(t
->settings
),
7851 "user-agent", t
->user_agent
, (char *)NULL
);
7852 g_object_set(G_OBJECT(t
->settings
),
7853 "enable-scripts", enable_scripts
, (char *)NULL
);
7854 g_object_set(G_OBJECT(t
->settings
),
7855 "enable-plugins", enable_plugins
, (char *)NULL
);
7856 g_object_set(G_OBJECT(t
->settings
),
7857 "javascript-can-open-windows-automatically", enable_scripts
,
7859 g_object_set(G_OBJECT(t
->settings
),
7860 "enable-html5-database", FALSE
, (char *)NULL
);
7861 g_object_set(G_OBJECT(t
->settings
),
7862 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7863 g_object_set(G_OBJECT(t
->settings
),
7864 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7865 g_object_set(G_OBJECT(t
->settings
),
7866 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7867 g_object_set(G_OBJECT(t
->wv
),
7868 "full-content-zoom", TRUE
, (char *)NULL
);
7870 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7874 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
7876 struct tab
*ti
, *t
= NULL
;
7877 gdouble view_size
, value
, max
;
7880 TAILQ_FOREACH(ti
, &tabs
, entry
)
7881 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
7889 if (adjustment
== NULL
)
7890 adjustment
= gtk_scrolled_window_get_vadjustment(
7891 GTK_SCROLLED_WINDOW(t
->browser_win
));
7893 view_size
= gtk_adjustment_get_page_size(adjustment
);
7894 value
= gtk_adjustment_get_value(adjustment
);
7895 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
7898 position
= g_strdup("All");
7899 else if (value
== max
)
7900 position
= g_strdup("Bot");
7901 else if (value
== 0)
7902 position
= g_strdup("Top");
7904 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
7906 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
7913 create_browser(struct tab
*t
)
7917 GtkAdjustment
*adjustment
;
7920 show_oops(NULL
, "create_browser invalid parameters");
7924 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7925 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7926 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7927 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7929 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7930 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7931 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7933 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7934 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7937 t
->settings
= webkit_web_settings_new();
7939 if (user_agent
== NULL
) {
7940 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7942 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7945 t
->user_agent
= g_strdup(user_agent
);
7947 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7950 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
7951 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
7952 G_CALLBACK(update_statusbar_position
), NULL
);
7964 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7965 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7966 gtk_widget_set_name(w
, "xxxterm");
7967 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7968 g_signal_connect(G_OBJECT(w
), "delete_event",
7969 G_CALLBACK (gtk_main_quit
), NULL
);
7975 create_kiosk_toolbar(struct tab
*t
)
7977 GtkWidget
*toolbar
= NULL
, *b
;
7979 b
= gtk_hbox_new(FALSE
, 0);
7981 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7983 /* backward button */
7984 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7985 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7986 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7987 G_CALLBACK(backward_cb
), t
);
7988 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7990 /* forward button */
7991 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7992 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7993 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7994 G_CALLBACK(forward_cb
), t
);
7995 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7998 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7999 gtk_widget_set_sensitive(t
->gohome
, true);
8000 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8001 G_CALLBACK(home_cb
), t
);
8002 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8004 /* create widgets but don't use them */
8005 t
->uri_entry
= gtk_entry_new();
8006 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8007 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8008 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8014 create_toolbar(struct tab
*t
)
8016 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8018 b
= gtk_hbox_new(FALSE
, 0);
8020 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8022 /* backward button */
8023 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8024 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8025 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8026 G_CALLBACK(backward_cb
), t
);
8027 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8029 /* forward button */
8030 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8031 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8032 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8033 G_CALLBACK(forward_cb
), t
);
8034 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8038 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8039 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8040 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8041 G_CALLBACK(stop_cb
), t
);
8042 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8046 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8047 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8048 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8049 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8050 G_CALLBACK(js_toggle_cb
), t
);
8051 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8053 t
->uri_entry
= gtk_entry_new();
8054 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8055 G_CALLBACK(activate_uri_entry_cb
), t
);
8056 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8057 G_CALLBACK(entry_key_cb
), t
);
8059 eb1
= gtk_hbox_new(FALSE
, 0);
8060 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8061 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8062 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8065 if (search_string
) {
8067 t
->search_entry
= gtk_entry_new();
8068 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8069 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8070 G_CALLBACK(activate_search_entry_cb
), t
);
8071 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8072 G_CALLBACK(entry_key_cb
), t
);
8073 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8074 eb2
= gtk_hbox_new(FALSE
, 0);
8075 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8076 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8078 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8085 create_buffers(struct tab
*t
)
8087 GtkCellRenderer
*renderer
;
8090 view
= gtk_tree_view_new();
8092 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8094 renderer
= gtk_cell_renderer_text_new();
8095 gtk_tree_view_insert_column_with_attributes
8096 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
8098 renderer
= gtk_cell_renderer_text_new();
8099 gtk_tree_view_insert_column_with_attributes
8100 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8103 gtk_tree_view_set_model
8104 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8110 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8111 GtkTreeViewColumn
*col
, struct tab
*t
)
8116 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8118 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8121 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8122 set_current_tab(id
- 1);
8128 /* after tab reordering/creation/removal */
8135 TAILQ_FOREACH(t
, &tabs
, entry
) {
8136 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8137 if (t
->tab_id
> maxid
)
8140 gtk_widget_show(t
->tab_elems
.sep
);
8143 TAILQ_FOREACH(t
, &tabs
, entry
) {
8144 if (t
->tab_id
== maxid
) {
8145 gtk_widget_hide(t
->tab_elems
.sep
);
8151 /* after active tab change */
8153 recolor_compact_tabs(void)
8159 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8160 TAILQ_FOREACH(t
, &tabs
, entry
)
8161 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8164 curid
= gtk_notebook_get_current_page(notebook
);
8165 TAILQ_FOREACH(t
, &tabs
, entry
)
8166 if (t
->tab_id
== curid
) {
8167 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8168 gtk_widget_modify_fg(t
->tab_elems
.label
,
8169 GTK_STATE_NORMAL
, &color
);
8175 set_current_tab(int page_num
)
8177 buffercmd_abort(get_current_tab());
8178 gtk_notebook_set_current_page(notebook
, page_num
);
8179 recolor_compact_tabs();
8183 undo_close_tab_save(struct tab
*t
)
8187 struct undo
*u1
, *u2
;
8189 WebKitWebHistoryItem
*item
;
8191 if ((uri
= get_uri(t
)) == NULL
)
8194 u1
= g_malloc0(sizeof(struct undo
));
8195 u1
->uri
= g_strdup(uri
);
8197 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8199 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8200 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8203 /* forward history */
8204 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8208 u1
->history
= g_list_prepend(u1
->history
,
8209 webkit_web_history_item_copy(item
));
8210 items
= g_list_next(items
);
8215 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8216 u1
->history
= g_list_prepend(u1
->history
,
8217 webkit_web_history_item_copy(item
));
8221 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8225 u1
->history
= g_list_prepend(u1
->history
,
8226 webkit_web_history_item_copy(item
));
8227 items
= g_list_next(items
);
8230 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8232 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8233 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8234 TAILQ_REMOVE(&undos
, u2
, entry
);
8236 g_list_free(u2
->history
);
8245 delete_tab(struct tab
*t
)
8249 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8255 TAILQ_REMOVE(&tabs
, t
, entry
);
8257 /* Halt all webkit activity. */
8258 abort_favicon_download(t
);
8259 webkit_web_view_stop_loading(t
->wv
);
8261 /* Save the tab, so we can undo the close. */
8262 undo_close_tab_save(t
);
8264 if (browser_mode
== XT_BM_KIOSK
) {
8265 gtk_widget_destroy(t
->uri_entry
);
8266 gtk_widget_destroy(t
->stop
);
8267 gtk_widget_destroy(t
->js_toggle
);
8270 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8271 gtk_widget_destroy(t
->vbox
);
8273 g_free(t
->user_agent
);
8274 g_free(t
->stylesheet
);
8278 if (TAILQ_EMPTY(&tabs
)) {
8279 if (browser_mode
== XT_BM_KIOSK
)
8280 create_new_tab(home
, NULL
, 1, -1);
8282 create_new_tab(NULL
, NULL
, 1, -1);
8285 /* recreate session */
8286 if (session_autosave
) {
8292 recolor_compact_tabs();
8296 update_statusbar_zoom(struct tab
*t
)
8299 char s
[16] = { '\0' };
8301 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8302 if ((zoom
<= 0.99 || zoom
>= 1.01))
8303 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8304 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8308 setzoom_webkit(struct tab
*t
, int adjust
)
8310 #define XT_ZOOMPERCENT 0.04
8315 show_oops(NULL
, "setzoom_webkit invalid parameters");
8319 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8320 if (adjust
== XT_ZOOM_IN
)
8321 zoom
+= XT_ZOOMPERCENT
;
8322 else if (adjust
== XT_ZOOM_OUT
)
8323 zoom
-= XT_ZOOMPERCENT
;
8324 else if (adjust
> 0)
8325 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8327 show_oops(t
, "setzoom_webkit invalid zoom value");
8331 if (zoom
< XT_ZOOMPERCENT
)
8332 zoom
= XT_ZOOMPERCENT
;
8333 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8334 update_statusbar_zoom(t
);
8338 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8340 struct tab
*t
= (struct tab
*) data
;
8342 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8344 switch (event
->button
) {
8346 set_current_tab(t
->tab_id
);
8357 append_tab(struct tab
*t
)
8362 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8363 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8367 create_sbe(int width
)
8371 sbe
= gtk_entry_new();
8372 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8373 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8374 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8375 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8376 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8377 gtk_widget_set_size_request(sbe
, width
, -1);
8383 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8388 WebKitWebHistoryItem
*item
;
8392 int sbe_p
= 0, sbe_b
= 0,
8395 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8397 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8398 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8402 t
= g_malloc0(sizeof *t
);
8404 if (title
== NULL
) {
8405 title
= "(untitled)";
8409 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8411 /* label + button for tab */
8412 b
= gtk_hbox_new(FALSE
, 0);
8415 #if GTK_CHECK_VERSION(2, 20, 0)
8416 t
->spinner
= gtk_spinner_new();
8418 t
->label
= gtk_label_new(title
);
8419 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8420 gtk_widget_set_size_request(t
->label
, 100, 0);
8421 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8422 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8423 gtk_widget_set_size_request(b
, 130, 0);
8425 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8426 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8427 #if GTK_CHECK_VERSION(2, 20, 0)
8428 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8432 if (browser_mode
== XT_BM_KIOSK
) {
8433 t
->toolbar
= create_kiosk_toolbar(t
);
8434 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8437 t
->toolbar
= create_toolbar(t
);
8439 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8447 t
->browser_win
= create_browser(t
);
8448 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8450 /* oops message for user feedback */
8451 t
->oops
= gtk_entry_new();
8452 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8453 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8454 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8455 gdk_color_parse(XT_COLOR_RED
, &color
);
8456 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8457 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8458 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8461 t
->cmd
= gtk_entry_new();
8462 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8463 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8464 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8465 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8468 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8470 t
->sbe
.statusbar
= gtk_entry_new();
8471 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8472 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8473 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8474 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8476 /* create these widgets only if specified in statusbar_elems */
8478 t
->sbe
.position
= create_sbe(40);
8479 t
->sbe
.zoom
= create_sbe(40);
8480 t
->sbe
.buffercmd
= create_sbe(60);
8482 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8484 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8487 /* gtk widgets cannot be added to a box twice. sbe_* variables
8488 make sure of this */
8489 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8493 GtkWidget
*sep
= gtk_vseparator_new();
8495 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8496 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8497 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8498 FALSE
, FALSE
, FALSE
);
8503 warnx("flag \"%c\" specified more than "
8504 "once in statusbar_elems\n", *p
);
8508 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8509 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8513 warnx("flag \"%c\" specified more than "
8514 "once in statusbar_elems\n", *p
);
8518 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8519 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8523 warnx("flag \"%c\" specified more than "
8524 "once in statusbar_elems\n", *p
);
8528 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8529 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8532 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8537 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8540 t
->buffers
= create_buffers(t
);
8541 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8543 /* xtp meaning is normal by default */
8544 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8546 /* set empty favicon */
8547 xt_icon_from_name(t
, "text-html");
8549 /* and show it all */
8550 gtk_widget_show_all(b
);
8551 gtk_widget_show_all(t
->vbox
);
8553 /* compact tab bar */
8554 t
->tab_elems
.label
= gtk_label_new(title
);
8555 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8556 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8557 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8558 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8560 t
->tab_elems
.eventbox
= gtk_event_box_new();
8561 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8562 t
->tab_elems
.sep
= gtk_vseparator_new();
8564 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8565 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8566 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8567 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8568 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8569 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8571 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8573 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8575 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8578 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8580 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8582 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8585 id
= position
>= 0 ? position
:
8586 gtk_notebook_get_current_page(notebook
) + 1;
8587 if (id
> gtk_notebook_get_n_pages(notebook
))
8590 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8591 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8592 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8593 t
->tab_elems
.eventbox
, id
);
8598 #if GTK_CHECK_VERSION(2, 20, 0)
8599 /* turn spinner off if we are a new tab without uri */
8601 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8602 gtk_widget_hide(t
->spinner
);
8605 /* make notebook tabs reorderable */
8606 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8608 /* compact tabs clickable */
8609 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8610 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8612 g_object_connect(G_OBJECT(t
->cmd
),
8613 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8614 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8615 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8616 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8619 /* reuse wv_button_cb to hide oops */
8620 g_object_connect(G_OBJECT(t
->oops
),
8621 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8624 g_signal_connect(t
->buffers
,
8625 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8626 g_object_connect(G_OBJECT(t
->buffers
),
8627 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8629 g_object_connect(G_OBJECT(t
->wv
),
8630 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8631 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8632 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8633 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8634 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8635 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8636 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8637 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8638 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8639 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8640 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8641 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8642 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8643 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8644 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8646 g_signal_connect(t
->wv
,
8647 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8649 * XXX this puts invalid url in uri_entry and that is undesirable
8652 g_signal_connect(t
->wv
,
8653 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8655 g_signal_connect(t
->wv
,
8656 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8658 /* hijack the unused keys as if we were the browser */
8659 g_object_connect(G_OBJECT(t
->toolbar
),
8660 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8663 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8664 G_CALLBACK(tab_close_cb
), t
);
8670 url_set_visibility();
8671 statusbar_set_visibility();
8674 set_current_tab(t
->tab_id
);
8675 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8680 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8684 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8689 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8690 /* restore the tab's history */
8691 if (u
&& u
->history
) {
8695 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8696 items
= g_list_next(items
);
8699 item
= g_list_nth_data(u
->history
, u
->back
);
8701 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8704 g_list_free(u
->history
);
8706 webkit_web_back_forward_list_clear(t
->bfl
);
8708 recolor_compact_tabs();
8709 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
8714 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8720 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8722 if (gtk_notebook_get_current_page(notebook
) == -1)
8725 TAILQ_FOREACH(t
, &tabs
, entry
) {
8726 if (t
->tab_id
== pn
) {
8727 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
8730 uri
= get_title(t
, TRUE
);
8731 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8737 /* can't use focus_webview here */
8738 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8745 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8748 struct tab
*t
= NULL
, *tt
;
8752 TAILQ_FOREACH(tt
, &tabs
, entry
)
8753 if (tt
->tab_id
== pn
) {
8758 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
8760 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
8765 menuitem_response(struct tab
*t
)
8767 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
8771 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
8773 GtkWidget
*menu
, *menu_items
;
8774 GdkEventButton
*bevent
;
8778 if (event
->type
== GDK_BUTTON_PRESS
) {
8779 bevent
= (GdkEventButton
*) event
;
8780 menu
= gtk_menu_new();
8782 TAILQ_FOREACH(ti
, &tabs
, entry
) {
8783 if ((uri
= get_uri(ti
)) == NULL
)
8784 /* XXX make sure there is something to print */
8785 /* XXX add gui pages in here to look purdy */
8787 menu_items
= gtk_menu_item_new_with_label(uri
);
8788 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
8789 gtk_widget_show(menu_items
);
8791 g_signal_connect_swapped((menu_items
),
8792 "activate", G_CALLBACK(menuitem_response
),
8796 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
8797 bevent
->button
, bevent
->time
);
8799 /* unref object so it'll free itself when popped down */
8800 #if !GTK_CHECK_VERSION(3, 0, 0)
8801 /* XXX does not need unref with gtk+3? */
8802 g_object_ref_sink(menu
);
8803 g_object_unref(menu
);
8806 return (TRUE
/* eat event */);
8809 return (FALSE
/* propagate */);
8813 icon_size_map(int icon_size
)
8815 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
8816 icon_size
> GTK_ICON_SIZE_DIALOG
)
8817 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
8823 create_button(char *name
, char *stockid
, int size
)
8825 GtkWidget
*button
, *image
;
8829 rcstring
= g_strdup_printf(
8830 "style \"%s-style\"\n"
8832 " GtkWidget::focus-padding = 0\n"
8833 " GtkWidget::focus-line-width = 0\n"
8837 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8838 gtk_rc_parse_string(rcstring
);
8840 button
= gtk_button_new();
8841 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8842 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8844 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8845 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8846 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8847 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8848 gtk_widget_set_name(button
, name
);
8849 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8855 button_set_stockid(GtkWidget
*button
, char *stockid
)
8859 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8860 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8861 gtk_button_set_image(GTK_BUTTON(button
), image
);
8865 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8868 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
8871 if (xterm_workaround
== 0)
8875 * xterm doesn't play nice with clipboards because it clears the
8876 * primary when clicked. We rely on primary being set to properly
8877 * handle middle mouse button clicks (paste). So when someone clears
8878 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
8879 * other application behavior (as in DON'T clear primary).
8882 p
= gtk_clipboard_wait_for_text(primary
);
8884 if (gdk_property_get(gdk_get_default_root_window(),
8886 gdk_atom_intern("STRING", FALSE
),
8888 1024 * 1024 /* picked out of my butt */,
8894 /* yes sir, we need to NUL the string */
8896 gtk_clipboard_set_text(primary
, p
, -1);
8910 char file
[PATH_MAX
];
8913 vbox
= gtk_vbox_new(FALSE
, 0);
8914 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8915 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8916 #if !GTK_CHECK_VERSION(3, 0, 0)
8917 /* XXX seems to be needed with gtk+2 */
8918 gtk_notebook_set_tab_hborder(notebook
, 0);
8919 gtk_notebook_set_tab_vborder(notebook
, 0);
8921 gtk_notebook_set_scrollable(notebook
, TRUE
);
8922 gtk_notebook_set_show_border(notebook
, FALSE
);
8923 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8925 abtn
= gtk_button_new();
8926 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8927 gtk_widget_set_size_request(arrow
, -1, -1);
8928 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8929 gtk_widget_set_size_request(abtn
, -1, 20);
8931 #if GTK_CHECK_VERSION(2, 20, 0)
8932 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8934 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8936 /* compact tab bar */
8937 tab_bar
= gtk_hbox_new(TRUE
, 0);
8939 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8940 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8941 gtk_widget_set_size_request(vbox
, -1, -1);
8943 g_object_connect(G_OBJECT(notebook
),
8944 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8946 g_object_connect(G_OBJECT(notebook
),
8947 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
8948 NULL
, (char *)NULL
);
8949 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8950 G_CALLBACK(arrow_cb
), NULL
);
8952 main_window
= create_window();
8953 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8956 for (i
= 0; i
< LENGTH(icons
); i
++) {
8957 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8958 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8959 l
= g_list_append(l
, pb
);
8961 gtk_window_set_default_icon_list(l
);
8963 /* clipboard work around */
8964 if (xterm_workaround
)
8966 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8967 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8969 gtk_widget_show_all(abtn
);
8970 gtk_widget_show_all(main_window
);
8971 notebook_tab_set_visibility();
8975 set_hook(void **hook
, char *name
)
8978 errx(1, "set_hook");
8980 if (*hook
== NULL
) {
8981 *hook
= dlsym(RTLD_NEXT
, name
);
8983 errx(1, "can't hook %s", name
);
8987 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8989 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8991 g_return_val_if_fail(cookie1
, FALSE
);
8992 g_return_val_if_fail(cookie2
, FALSE
);
8994 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8995 !strcmp (cookie1
->value
, cookie2
->value
) &&
8996 !strcmp (cookie1
->path
, cookie2
->path
) &&
8997 !strcmp (cookie1
->domain
, cookie2
->domain
));
9001 transfer_cookies(void)
9004 SoupCookie
*sc
, *pc
;
9006 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9008 for (;cf
; cf
= cf
->next
) {
9010 sc
= soup_cookie_copy(pc
);
9011 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9014 soup_cookies_free(cf
);
9018 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9023 print_cookie("soup_cookie_jar_delete_cookie", c
);
9025 if (cookies_enabled
== 0)
9028 if (jar
== NULL
|| c
== NULL
)
9031 /* find and remove from persistent jar */
9032 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9034 for (;cf
; cf
= cf
->next
) {
9036 if (soup_cookie_equal(ci
, c
)) {
9037 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9042 soup_cookies_free(cf
);
9044 /* delete from session jar */
9045 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9049 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9051 struct domain
*d
= NULL
;
9055 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9056 jar
, p_cookiejar
, s_cookiejar
);
9058 if (cookies_enabled
== 0)
9061 /* see if we are up and running */
9062 if (p_cookiejar
== NULL
) {
9063 _soup_cookie_jar_add_cookie(jar
, cookie
);
9066 /* disallow p_cookiejar adds, shouldn't happen */
9067 if (jar
== p_cookiejar
)
9071 if (jar
== NULL
|| cookie
== NULL
)
9074 if (enable_cookie_whitelist
&&
9075 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9077 DNPRINTF(XT_D_COOKIE
,
9078 "soup_cookie_jar_add_cookie: reject %s\n",
9080 if (save_rejected_cookies
) {
9081 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9082 show_oops(NULL
, "can't open reject cookie file");
9085 fseek(r_cookie_f
, 0, SEEK_END
);
9086 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9087 cookie
->http_only
? "#HttpOnly_" : "",
9089 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9091 cookie
->secure
? "TRUE" : "FALSE",
9093 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9100 if (!allow_volatile_cookies
)
9104 if (cookie
->expires
== NULL
&& session_timeout
) {
9105 soup_cookie_set_expires(cookie
,
9106 soup_date_new_from_now(session_timeout
));
9107 print_cookie("modified add cookie", cookie
);
9110 /* see if we are white listed for persistence */
9111 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9112 /* add to persistent jar */
9113 c
= soup_cookie_copy(cookie
);
9114 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9115 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9118 /* add to session jar */
9119 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9120 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9126 char file
[PATH_MAX
];
9128 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9129 "soup_cookie_jar_add_cookie");
9130 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9131 "soup_cookie_jar_delete_cookie");
9133 if (cookies_enabled
== 0)
9137 * the following code is intricate due to overriding several libsoup
9139 * do not alter order of these operations.
9142 /* rejected cookies */
9143 if (save_rejected_cookies
)
9144 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9147 /* persistent cookies */
9148 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9149 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9151 /* session cookies */
9152 s_cookiejar
= soup_cookie_jar_new();
9153 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9154 cookie_policy
, (void *)NULL
);
9157 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9161 setup_proxy(char *uri
)
9164 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9165 soup_uri_free(proxy_uri
);
9169 if (http_proxy
!= uri
) {
9176 http_proxy
= g_strdup(uri
);
9177 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9178 proxy_uri
= soup_uri_new(http_proxy
);
9179 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
9184 send_cmd_to_socket(char *cmd
)
9187 struct sockaddr_un sa
;
9189 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9190 warnx("%s: socket", __func__
);
9194 sa
.sun_family
= AF_UNIX
;
9195 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9196 work_dir
, XT_SOCKET_FILE
);
9199 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9200 warnx("%s: connect", __func__
);
9204 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9205 warnx("%s: send", __func__
);
9216 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9219 char str
[XT_MAX_URL_LENGTH
];
9220 socklen_t t
= sizeof(struct sockaddr_un
);
9221 struct sockaddr_un sa
;
9226 gint fd
= g_io_channel_unix_get_fd(source
);
9228 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9233 if (getpeereid(s
, &uid
, &gid
) == -1) {
9237 if (uid
!= getuid() || gid
!= getgid()) {
9238 warnx("unauthorized user");
9244 warnx("not a valid user");
9248 n
= recv(s
, str
, sizeof(str
), 0);
9252 tt
= TAILQ_LAST(&tabs
, tab_list
);
9253 cmd_execute(tt
, str
);
9261 struct sockaddr_un sa
;
9263 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9264 warn("is_running: socket");
9268 sa
.sun_family
= AF_UNIX
;
9269 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9270 work_dir
, XT_SOCKET_FILE
);
9273 /* connect to see if there is a listener */
9274 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9275 rv
= 0; /* not running */
9277 rv
= 1; /* already running */
9288 struct sockaddr_un sa
;
9290 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9291 warn("build_socket: socket");
9295 sa
.sun_family
= AF_UNIX
;
9296 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9297 work_dir
, XT_SOCKET_FILE
);
9300 /* connect to see if there is a listener */
9301 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9302 /* no listener so we will */
9303 unlink(sa
.sun_path
);
9305 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9306 warn("build_socket: bind");
9310 if (listen(s
, 1) == -1) {
9311 warn("build_socket: listen");
9324 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9325 GtkTreeIter
*iter
, struct tab
*t
)
9329 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9337 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9338 GtkTreeIter
*iter
, struct tab
*t
)
9342 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9343 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9344 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9351 completion_add_uri(const gchar
*uri
)
9355 /* add uri to list_store */
9356 gtk_list_store_append(completion_model
, &iter
);
9357 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9361 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9362 GtkTreeIter
*iter
, gpointer user_data
)
9365 gboolean match
= FALSE
;
9367 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9373 match
= match_uri(value
, key
);
9380 completion_add(struct tab
*t
)
9382 /* enable completion for tab */
9383 t
->completion
= gtk_entry_completion_new();
9384 gtk_entry_completion_set_text_column(t
->completion
, 0);
9385 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9386 gtk_entry_completion_set_model(t
->completion
,
9387 GTK_TREE_MODEL(completion_model
));
9388 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9390 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9391 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9392 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9393 G_CALLBACK(completion_select_cb
), t
);
9394 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9395 G_CALLBACK(completion_hover_cb
), t
);
9403 if (stat(dir
, &sb
)) {
9404 if (mkdir(dir
, S_IRWXU
) == -1)
9405 err(1, "mkdir %s", dir
);
9407 err(1, "stat %s", dir
);
9409 if (S_ISDIR(sb
.st_mode
) == 0)
9410 errx(1, "%s not a dir", dir
);
9411 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9412 warnx("fixing invalid permissions on %s", dir
);
9413 if (chmod(dir
, S_IRWXU
) == -1)
9414 err(1, "chmod %s", dir
);
9422 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9428 main(int argc
, char *argv
[])
9431 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9432 char conf
[PATH_MAX
] = { '\0' };
9433 char file
[PATH_MAX
];
9434 char *env_proxy
= NULL
;
9438 struct sigaction sact
;
9439 GIOChannel
*channel
;
9444 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9446 /* fiddle with ulimits */
9447 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9450 /* just use them all */
9451 rlp
.rlim_cur
= rlp
.rlim_max
;
9452 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9454 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9456 else if (rlp
.rlim_cur
<= 256)
9457 warnx("%s requires at least 256 file descriptors",
9461 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9470 errx(0 , "Version: %s", version
);
9473 strlcpy(conf
, optarg
, sizeof(conf
));
9476 strlcpy(named_session
, optarg
, sizeof(named_session
));
9497 RB_INIT(&downloads
);
9501 TAILQ_INIT(&aliases
);
9507 gnutls_global_init();
9509 /* generate session keys for xtp pages */
9510 generate_xtp_session_key(&dl_session_key
);
9511 generate_xtp_session_key(&hl_session_key
);
9512 generate_xtp_session_key(&cl_session_key
);
9513 generate_xtp_session_key(&fl_session_key
);
9516 if (!g_thread_supported()) {
9517 g_thread_init(NULL
);
9519 gdk_threads_enter();
9521 gtk_init(&argc
, &argv
);
9524 bzero(&sact
, sizeof(sact
));
9525 sigemptyset(&sact
.sa_mask
);
9526 sact
.sa_handler
= sigchild
;
9527 sact
.sa_flags
= SA_NOCLDSTOP
;
9528 sigaction(SIGCHLD
, &sact
, NULL
);
9530 /* set download dir */
9531 pwd
= getpwuid(getuid());
9533 errx(1, "invalid user %d", getuid());
9534 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9536 /* compile buffer command regexes */
9539 /* set default string settings */
9540 home
= g_strdup("https://www.cyphertite.com");
9541 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9542 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9543 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9544 cmd_font_name
= g_strdup("monospace normal 9");
9545 oops_font_name
= g_strdup("monospace normal 9");
9546 statusbar_font_name
= g_strdup("monospace normal 9");
9547 tabbar_font_name
= g_strdup("monospace normal 9");
9548 statusbar_elems
= g_strdup("BP");
9550 /* read config file */
9551 if (strlen(conf
) == 0)
9552 snprintf(conf
, sizeof conf
, "%s/.%s",
9553 pwd
->pw_dir
, XT_CONF_FILE
);
9554 config_parse(conf
, 0);
9557 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9558 oops_font
= pango_font_description_from_string(oops_font_name
);
9559 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9560 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9562 /* working directory */
9563 if (strlen(work_dir
) == 0)
9564 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9565 pwd
->pw_dir
, XT_DIR
);
9568 /* icon cache dir */
9569 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9573 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9577 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9578 work_dir
, XT_SESSIONS_DIR
);
9579 xxx_dir(sessions_dir
);
9581 /* runtime settings that can override config file */
9582 if (runtime_settings
[0] != '\0')
9583 config_parse(runtime_settings
, 1);
9586 if (!strcmp(download_dir
, pwd
->pw_dir
))
9587 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9588 xxx_dir(download_dir
);
9590 /* favorites file */
9591 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9592 if (stat(file
, &sb
)) {
9593 warnx("favorites file doesn't exist, creating it");
9594 if ((f
= fopen(file
, "w")) == NULL
)
9595 err(1, "favorites");
9599 /* quickmarks file */
9600 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9601 if (stat(file
, &sb
)) {
9602 warnx("quickmarks file doesn't exist, creating it");
9603 if ((f
= fopen(file
, "w")) == NULL
)
9604 err(1, "quickmarks");
9609 session
= webkit_get_default_session();
9614 if (stat(ssl_ca_file
, &sb
)) {
9615 warnx("no CA file: %s", ssl_ca_file
);
9616 g_free(ssl_ca_file
);
9619 g_object_set(session
,
9620 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9621 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9626 env_proxy
= getenv("http_proxy");
9628 setup_proxy(env_proxy
);
9630 setup_proxy(http_proxy
);
9633 send_cmd_to_socket(argv
[0]);
9637 /* set some connection parameters */
9638 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9639 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9642 /* see if there is already an xxxterm running */
9643 if (single_instance
&& is_running()) {
9645 warnx("already running");
9650 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9651 send_cmd_to_socket(cmd
);
9661 /* uri completion */
9662 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9665 buffers_store
= gtk_list_store_new
9666 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9672 notebook_tab_set_visibility();
9674 if (save_global_history
)
9675 restore_global_history();
9677 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9678 restore_saved_tabs();
9680 a
.s
= named_session
;
9681 a
.i
= XT_SES_DONOTHING
;
9682 open_tabs(NULL
, &a
);
9686 create_new_tab(argv
[0], NULL
, focus
, -1);
9693 if (TAILQ_EMPTY(&tabs
))
9694 create_new_tab(home
, NULL
, 1, -1);
9697 if ((s
= build_socket()) != -1) {
9698 channel
= g_io_channel_unix_new(s
);
9699 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
9704 if (!g_thread_supported()) {
9705 gdk_threads_leave();
9708 gnutls_global_deinit();