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 #include <bsd/stdlib.h>
49 # if !defined(sane_libbsd_headers)
50 void arc4random_buf(void *, size_t);
52 #elif defined(__FreeBSD__)
54 #include "freebsd/util.h"
60 #include <sys/queue.h>
61 #include <sys/resource.h>
62 #include <sys/socket.h>
68 #include <gdk/gdkkeysyms.h>
70 #if GTK_CHECK_VERSION(3,0,0)
71 /* we still use GDK_* instead of GDK_KEY_* */
72 #include <gdk/gdkkeysyms-compat.h>
75 #include <webkit/webkit.h>
76 #include <libsoup/soup.h>
77 #include <gnutls/gnutls.h>
78 #include <JavaScriptCore/JavaScript.h>
79 #include <gnutls/x509.h>
82 #include "javascript.h"
85 javascript.h borrowed from vimprobable2 under the following license:
87 Copyright (c) 2009 Leon Winter
88 Copyright (c) 2009 Hannes Schueller
89 Copyright (c) 2009 Matto Fransen
91 Permission is hereby granted, free of charge, to any person obtaining a copy
92 of this software and associated documentation files (the "Software"), to deal
93 in the Software without restriction, including without limitation the rights
94 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
95 copies of the Software, and to permit persons to whom the Software is
96 furnished to do so, subject to the following conditions:
98 The above copyright notice and this permission notice shall be included in
99 all copies or substantial portions of the Software.
101 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
102 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
103 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
104 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
105 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
106 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
110 static char *version
= XXXTERM_VERSION
;
112 /* hooked functions */
113 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
114 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
119 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
120 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
121 #define XT_D_MOVE 0x0001
122 #define XT_D_KEY 0x0002
123 #define XT_D_TAB 0x0004
124 #define XT_D_URL 0x0008
125 #define XT_D_CMD 0x0010
126 #define XT_D_NAV 0x0020
127 #define XT_D_DOWNLOAD 0x0040
128 #define XT_D_CONFIG 0x0080
129 #define XT_D_JS 0x0100
130 #define XT_D_FAVORITE 0x0200
131 #define XT_D_PRINTING 0x0400
132 #define XT_D_COOKIE 0x0800
133 #define XT_D_KEYBINDING 0x1000
134 #define XT_D_CLIP 0x2000
135 #define XT_D_BUFFERCMD 0x4000
136 u_int32_t swm_debug
= 0
154 #define DPRINTF(x...)
155 #define DNPRINTF(n,x...)
158 #define LENGTH(x) (sizeof x / sizeof x[0])
159 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
160 ~(GDK_BUTTON1_MASK) & \
161 ~(GDK_BUTTON2_MASK) & \
162 ~(GDK_BUTTON3_MASK) & \
163 ~(GDK_BUTTON4_MASK) & \
166 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
177 TAILQ_ENTRY(tab
) entry
;
179 GtkWidget
*tab_content
;
188 GtkWidget
*uri_entry
;
189 GtkWidget
*search_entry
;
191 GtkWidget
*browser_win
;
192 GtkWidget
*statusbar_box
;
194 GtkWidget
*statusbar
;
195 GtkWidget
*buffercmd
;
206 GtkWidget
*js_toggle
;
207 GtkEntryCompletion
*completion
;
211 WebKitWebHistoryItem
*item
;
212 WebKitWebBackForwardList
*bfl
;
215 WebKitNetworkRequest
*icon_request
;
216 WebKitDownload
*icon_download
;
217 gchar
*icon_dest_uri
;
219 /* adjustments for browser */
222 GtkAdjustment
*adjust_h
;
223 GtkAdjustment
*adjust_v
;
229 int xtp_meaning
; /* identifies dls/favorites */
231 int popup
; /* 1 if cmd_entry has popup visible */
236 #define XT_HINT_NONE (0)
237 #define XT_HINT_NUMERICAL (1)
238 #define XT_HINT_ALPHANUM (2)
242 /* custom stylesheet */
252 WebKitWebSettings
*settings
;
256 double mark
[XT_NOMARKS
];
258 TAILQ_HEAD(tab_list
, tab
);
261 RB_ENTRY(history
) entry
;
265 RB_HEAD(history_list
, history
);
268 TAILQ_ENTRY(session
) entry
;
271 TAILQ_HEAD(session_list
, session
);
274 RB_ENTRY(download
) entry
;
276 WebKitDownload
*download
;
279 RB_HEAD(download_list
, download
);
282 RB_ENTRY(domain
) entry
;
284 int handy
; /* app use */
286 RB_HEAD(domain_list
, domain
);
289 TAILQ_ENTRY(undo
) entry
;
292 int back
; /* Keeps track of how many back
293 * history items there are. */
295 TAILQ_HEAD(undo_tailq
, undo
);
299 TAILQ_ENTRY(sp
) entry
;
301 TAILQ_HEAD(sp_list
, sp
);
303 struct command_entry
{
305 TAILQ_ENTRY(command_entry
) entry
;
307 TAILQ_HEAD(command_list
, command_entry
);
309 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
310 int next_download_id
= 1;
319 #define XT_NAME ("XXXTerm")
320 #define XT_DIR (".xxxterm")
321 #define XT_CACHE_DIR ("cache")
322 #define XT_CERT_DIR ("certs/")
323 #define XT_SESSIONS_DIR ("sessions/")
324 #define XT_CONF_FILE ("xxxterm.conf")
325 #define XT_FAVS_FILE ("favorites")
326 #define XT_QMARKS_FILE ("quickmarks")
327 #define XT_SAVED_TABS_FILE ("main_session")
328 #define XT_RESTART_TABS_FILE ("restart_tabs")
329 #define XT_SOCKET_FILE ("socket")
330 #define XT_HISTORY_FILE ("history")
331 #define XT_REJECT_FILE ("rejected.txt")
332 #define XT_COOKIE_FILE ("cookies.txt")
333 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
334 #define XT_SEARCH_FILE ("search_history")
335 #define XT_COMMAND_FILE ("command_history")
336 #define XT_CB_HANDLED (TRUE)
337 #define XT_CB_PASSTHROUGH (FALSE)
338 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
339 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
340 #define XT_DLMAN_REFRESH "10"
341 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
342 "td{overflow: hidden;" \
343 " padding: 2px 2px 2px 2px;" \
344 " border: 1px solid black;" \
345 " vertical-align:top;" \
346 " word-wrap: break-word}\n" \
347 "tr:hover{background: #ffff99}\n" \
348 "th{background-color: #cccccc;" \
349 " border: 1px solid black}\n" \
350 "table{width: 100%%;" \
351 " border: 1px black solid;" \
352 " border-collapse:collapse}\n" \
354 "border: 1px solid black;" \
357 ".progress-inner{float: left;" \
359 " background: green}\n" \
360 ".dlstatus{font-size: small;" \
361 " text-align: center}\n" \
363 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
364 #define XT_MAX_UNDO_CLOSE_TAB (32)
365 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
366 #define XT_PRINT_EXTRA_MARGIN 10
367 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
368 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
371 #define XT_COLOR_RED "#cc0000"
372 #define XT_COLOR_YELLOW "#ffff66"
373 #define XT_COLOR_BLUE "lightblue"
374 #define XT_COLOR_GREEN "#99ff66"
375 #define XT_COLOR_WHITE "white"
376 #define XT_COLOR_BLACK "black"
378 #define XT_COLOR_CT_BACKGROUND "#000000"
379 #define XT_COLOR_CT_INACTIVE "#dddddd"
380 #define XT_COLOR_CT_ACTIVE "#bbbb00"
381 #define XT_COLOR_CT_SEPARATOR "#555555"
383 #define XT_COLOR_SB_SEPARATOR "#555555"
385 #define XT_PROTO_DELIM "://"
388 * xxxterm "protocol" (xtp)
389 * We use this for managing stuff like downloads and favorites. They
390 * make magical HTML pages in memory which have xxxt:// links in order
391 * to communicate with xxxterm's internals. These links take the format:
392 * xxxt://class/session_key/action/arg
394 * Don't begin xtp class/actions as 0. atoi returns that on error.
396 * Typically we have not put addition of items in this framework, as
397 * adding items is either done via an ex-command or via a keybinding instead.
400 #define XT_XTP_STR "xxxt://"
402 /* XTP classes (xxxt://<class>) */
403 #define XT_XTP_INVALID 0 /* invalid */
404 #define XT_XTP_DL 1 /* downloads */
405 #define XT_XTP_HL 2 /* history */
406 #define XT_XTP_CL 3 /* cookies */
407 #define XT_XTP_FL 4 /* favorites */
409 /* XTP download actions */
410 #define XT_XTP_DL_LIST 1
411 #define XT_XTP_DL_CANCEL 2
412 #define XT_XTP_DL_REMOVE 3
414 /* XTP history actions */
415 #define XT_XTP_HL_LIST 1
416 #define XT_XTP_HL_REMOVE 2
418 /* XTP cookie actions */
419 #define XT_XTP_CL_LIST 1
420 #define XT_XTP_CL_REMOVE 2
422 /* XTP cookie actions */
423 #define XT_XTP_FL_LIST 1
424 #define XT_XTP_FL_REMOVE 2
427 #define XT_MOVE_INVALID (0)
428 #define XT_MOVE_DOWN (1)
429 #define XT_MOVE_UP (2)
430 #define XT_MOVE_BOTTOM (3)
431 #define XT_MOVE_TOP (4)
432 #define XT_MOVE_PAGEDOWN (5)
433 #define XT_MOVE_PAGEUP (6)
434 #define XT_MOVE_HALFDOWN (7)
435 #define XT_MOVE_HALFUP (8)
436 #define XT_MOVE_LEFT (9)
437 #define XT_MOVE_FARLEFT (10)
438 #define XT_MOVE_RIGHT (11)
439 #define XT_MOVE_FARRIGHT (12)
440 #define XT_MOVE_PERCENT (13)
442 #define XT_QMARK_SET (0)
443 #define XT_QMARK_OPEN (1)
444 #define XT_QMARK_TAB (2)
446 #define XT_MARK_SET (0)
447 #define XT_MARK_GOTO (1)
449 #define XT_TAB_LAST (-4)
450 #define XT_TAB_FIRST (-3)
451 #define XT_TAB_PREV (-2)
452 #define XT_TAB_NEXT (-1)
453 #define XT_TAB_INVALID (0)
454 #define XT_TAB_NEW (1)
455 #define XT_TAB_DELETE (2)
456 #define XT_TAB_DELQUIT (3)
457 #define XT_TAB_OPEN (4)
458 #define XT_TAB_UNDO_CLOSE (5)
459 #define XT_TAB_SHOW (6)
460 #define XT_TAB_HIDE (7)
461 #define XT_TAB_NEXTSTYLE (8)
463 #define XT_NAV_INVALID (0)
464 #define XT_NAV_BACK (1)
465 #define XT_NAV_FORWARD (2)
466 #define XT_NAV_RELOAD (3)
468 #define XT_FOCUS_INVALID (0)
469 #define XT_FOCUS_URI (1)
470 #define XT_FOCUS_SEARCH (2)
472 #define XT_SEARCH_INVALID (0)
473 #define XT_SEARCH_NEXT (1)
474 #define XT_SEARCH_PREV (2)
476 #define XT_PASTE_CURRENT_TAB (0)
477 #define XT_PASTE_NEW_TAB (1)
479 #define XT_ZOOM_IN (-1)
480 #define XT_ZOOM_OUT (-2)
481 #define XT_ZOOM_NORMAL (100)
483 #define XT_URL_SHOW (1)
484 #define XT_URL_HIDE (2)
486 #define XT_WL_TOGGLE (1<<0)
487 #define XT_WL_ENABLE (1<<1)
488 #define XT_WL_DISABLE (1<<2)
489 #define XT_WL_FQDN (1<<3) /* default */
490 #define XT_WL_TOPLEVEL (1<<4)
491 #define XT_WL_PERSISTENT (1<<5)
492 #define XT_WL_SESSION (1<<6)
493 #define XT_WL_RELOAD (1<<7)
495 #define XT_SHOW (1<<7)
496 #define XT_DELETE (1<<8)
497 #define XT_SAVE (1<<9)
498 #define XT_OPEN (1<<10)
500 #define XT_CMD_OPEN (0)
501 #define XT_CMD_OPEN_CURRENT (1)
502 #define XT_CMD_TABNEW (2)
503 #define XT_CMD_TABNEW_CURRENT (3)
505 #define XT_STATUS_NOTHING (0)
506 #define XT_STATUS_LINK (1)
507 #define XT_STATUS_URI (2)
508 #define XT_STATUS_LOADING (3)
510 #define XT_SES_DONOTHING (0)
511 #define XT_SES_CLOSETABS (1)
513 #define XT_BM_NORMAL (0)
514 #define XT_BM_WHITELIST (1)
515 #define XT_BM_KIOSK (2)
517 #define XT_PREFIX (1<<0)
518 #define XT_USERARG (1<<1)
519 #define XT_URLARG (1<<2)
520 #define XT_INTARG (1<<3)
521 #define XT_SESSARG (1<<4)
522 #define XT_SETARG (1<<5)
524 #define XT_TABS_NORMAL 0
525 #define XT_TABS_COMPACT 1
527 #define XT_BUFCMD_SZ (8)
535 TAILQ_ENTRY(mime_type
) entry
;
537 TAILQ_HEAD(mime_type_list
, mime_type
);
543 TAILQ_ENTRY(alias
) entry
;
545 TAILQ_HEAD(alias_list
, alias
);
547 /* settings that require restart */
548 int tabless
= 0; /* allow only 1 tab */
549 int enable_socket
= 0;
550 int single_instance
= 0; /* only allow one xxxterm to run */
551 int fancy_bar
= 1; /* fancy toolbar */
552 int browser_mode
= XT_BM_NORMAL
;
553 int enable_localstorage
= 1;
554 char *statusbar_elems
= NULL
;
556 /* runtime settings */
557 int show_tabs
= 1; /* show tabs on notebook */
558 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
559 int show_url
= 1; /* show url toolbar on notebook */
560 int show_statusbar
= 0; /* vimperator style status bar */
561 int ctrl_click_focus
= 0; /* ctrl click gets focus */
562 int cookies_enabled
= 1; /* enable cookies */
563 int read_only_cookies
= 0; /* enable to not write cookies */
564 int enable_scripts
= 1;
565 int enable_plugins
= 0;
566 gfloat default_zoom_level
= 1.0;
567 char default_script
[PATH_MAX
];
568 int window_height
= 768;
569 int window_width
= 1024;
570 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
571 int refresh_interval
= 10; /* download refresh interval */
572 int enable_cookie_whitelist
= 0;
573 int enable_js_whitelist
= 0;
574 int session_timeout
= 3600; /* cookie session timeout */
575 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
576 char *ssl_ca_file
= NULL
;
577 char *resource_dir
= NULL
;
578 gboolean ssl_strict_certs
= FALSE
;
579 int append_next
= 1; /* append tab after current tab */
581 char *search_string
= NULL
;
582 char *http_proxy
= NULL
;
583 char download_dir
[PATH_MAX
];
584 char runtime_settings
[PATH_MAX
]; /* override of settings */
585 int allow_volatile_cookies
= 0;
586 int save_global_history
= 0; /* save global history to disk */
587 char *user_agent
= NULL
;
588 int save_rejected_cookies
= 0;
589 int session_autosave
= 0;
590 int guess_search
= 0;
591 int dns_prefetch
= FALSE
;
592 gint max_connections
= 25;
593 gint max_host_connections
= 5;
594 gint enable_spell_checking
= 0;
595 char *spell_check_languages
= NULL
;
596 int xterm_workaround
= 0;
597 char *url_regex
= NULL
;
598 int history_autosave
= 0;
599 char search_file
[PATH_MAX
];
600 char command_file
[PATH_MAX
];
602 char *cmd_font_name
= NULL
;
603 char *oops_font_name
= NULL
;
604 char *statusbar_font_name
= NULL
;
605 char *tabbar_font_name
= NULL
;
606 PangoFontDescription
*cmd_font
;
607 PangoFontDescription
*oops_font
;
608 PangoFontDescription
*statusbar_font
;
609 PangoFontDescription
*tabbar_font
;
610 char *qmarks
[XT_NOMARKS
];
612 int btn_down
; /* M1 down in any wv */
613 regex_t url_re
; /* guess_search regex */
617 int set_browser_mode(struct settings
*, char *);
618 int set_cookie_policy(struct settings
*, char *);
619 int set_download_dir(struct settings
*, char *);
620 int set_default_script(struct settings
*, char *);
621 int set_runtime_dir(struct settings
*, char *);
622 int set_tab_style(struct settings
*, char *);
623 int set_work_dir(struct settings
*, char *);
624 int add_alias(struct settings
*, char *);
625 int add_mime_type(struct settings
*, char *);
626 int add_cookie_wl(struct settings
*, char *);
627 int add_js_wl(struct settings
*, char *);
628 int add_kb(struct settings
*, char *);
629 void button_set_stockid(GtkWidget
*, char *);
630 GtkWidget
* create_button(char *, char *, int);
632 char *get_browser_mode(struct settings
*);
633 char *get_cookie_policy(struct settings
*);
634 char *get_download_dir(struct settings
*);
635 char *get_default_script(struct settings
*);
636 char *get_runtime_dir(struct settings
*);
637 char *get_tab_style(struct settings
*);
638 char *get_work_dir(struct settings
*);
639 void startpage_add(const char *, ...);
641 void walk_alias(struct settings
*, void (*)(struct settings
*,
642 char *, void *), void *);
643 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
644 char *, void *), void *);
645 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
646 char *, void *), void *);
647 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
649 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
650 char *, void *), void *);
652 void recalc_tabs(void);
653 void recolor_compact_tabs(void);
654 void set_current_tab(int page_num
);
655 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
656 void marks_clear(struct tab
*t
);
658 int set_http_proxy(char *);
661 int (*set
)(struct settings
*, char *);
662 char *(*get
)(struct settings
*);
663 void (*walk
)(struct settings
*,
664 void (*cb
)(struct settings
*, char *, void *),
668 struct special s_browser_mode
= {
674 struct special s_cookie
= {
680 struct special s_alias
= {
686 struct special s_mime
= {
692 struct special s_js
= {
698 struct special s_kb
= {
704 struct special s_cookie_wl
= {
710 struct special s_default_script
= {
716 struct special s_download_dir
= {
722 struct special s_work_dir
= {
728 struct special s_tab_style
= {
737 #define XT_S_INVALID (0)
740 #define XT_S_FLOAT (3)
742 #define XT_SF_RESTART (1<<0)
743 #define XT_SF_RUNTIME (1<<1)
748 int (*activate
)(char *);
750 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
751 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
752 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
753 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
754 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
755 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
756 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
757 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
758 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
759 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
760 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
761 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
762 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
763 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
764 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
765 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
766 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
767 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
768 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
769 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
770 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
771 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
772 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
773 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
774 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
775 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
776 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
777 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
778 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
779 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
780 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
781 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
782 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
783 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
784 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
785 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
786 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
787 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
788 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
789 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
790 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
791 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
792 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
793 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
794 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
795 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
796 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
799 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
800 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
801 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
802 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
804 /* runtime settings */
805 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
806 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
807 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
808 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
809 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
812 int about(struct tab
*, struct karg
*);
813 int blank(struct tab
*, struct karg
*);
814 int ca_cmd(struct tab
*, struct karg
*);
815 int cookie_show_wl(struct tab
*, struct karg
*);
816 int js_show_wl(struct tab
*, struct karg
*);
817 int help(struct tab
*, struct karg
*);
818 int set(struct tab
*, struct karg
*);
819 int stats(struct tab
*, struct karg
*);
820 int marco(struct tab
*, struct karg
*);
821 int startpage(struct tab
*, struct karg
*);
822 const char * marco_message(int *);
823 int xtp_page_cl(struct tab
*, struct karg
*);
824 int xtp_page_dl(struct tab
*, struct karg
*);
825 int xtp_page_fl(struct tab
*, struct karg
*);
826 int xtp_page_hl(struct tab
*, struct karg
*);
827 void xt_icon_from_file(struct tab
*, char *);
828 const gchar
*get_uri(struct tab
*);
829 const gchar
*get_title(struct tab
*, bool);
831 #define XT_URI_ABOUT ("about:")
832 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
833 #define XT_URI_ABOUT_ABOUT ("about")
834 #define XT_URI_ABOUT_BLANK ("blank")
835 #define XT_URI_ABOUT_CERTS ("certs")
836 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
837 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
838 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
839 #define XT_URI_ABOUT_FAVORITES ("favorites")
840 #define XT_URI_ABOUT_HELP ("help")
841 #define XT_URI_ABOUT_HISTORY ("history")
842 #define XT_URI_ABOUT_JSWL ("jswl")
843 #define XT_URI_ABOUT_SET ("set")
844 #define XT_URI_ABOUT_STATS ("stats")
845 #define XT_URI_ABOUT_MARCO ("marco")
846 #define XT_URI_ABOUT_STARTPAGE ("startpage")
850 int (*func
)(struct tab
*, struct karg
*);
852 { XT_URI_ABOUT_ABOUT
, about
},
853 { XT_URI_ABOUT_BLANK
, blank
},
854 { XT_URI_ABOUT_CERTS
, ca_cmd
},
855 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
856 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
857 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
858 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
859 { XT_URI_ABOUT_HELP
, help
},
860 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
861 { XT_URI_ABOUT_JSWL
, js_show_wl
},
862 { XT_URI_ABOUT_SET
, set
},
863 { XT_URI_ABOUT_STATS
, stats
},
864 { XT_URI_ABOUT_MARCO
, marco
},
865 { XT_URI_ABOUT_STARTPAGE
, startpage
},
868 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
869 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
870 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
871 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
872 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
873 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
874 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
877 extern char *__progname
;
880 GtkWidget
*main_window
;
881 GtkNotebook
*notebook
;
883 GtkWidget
*arrow
, *abtn
;
884 struct tab_list tabs
;
885 struct history_list hl
;
886 struct session_list sessions
;
887 struct download_list downloads
;
888 struct domain_list c_wl
;
889 struct domain_list js_wl
;
890 struct undo_tailq undos
;
891 struct keybinding_list kbl
;
893 struct command_list chl
;
894 struct command_list shl
;
895 struct command_entry
*history_at
;
896 struct command_entry
*search_at
;
898 int updating_dl_tabs
= 0;
899 int updating_hl_tabs
= 0;
900 int updating_cl_tabs
= 0;
901 int updating_fl_tabs
= 0;
902 int cmd_history_count
= 0;
903 int search_history_count
= 0;
905 uint64_t blocked_cookies
= 0;
906 char named_session
[PATH_MAX
];
907 GtkListStore
*completion_model
;
908 GtkListStore
*buffers_store
;
910 void xxx_dir(char *);
911 int icon_size_map(int);
912 void completion_add(struct tab
*);
913 void completion_add_uri(const gchar
*);
914 void show_oops(struct tab
*, const char *, ...);
917 history_delete(struct command_list
*l
, int *counter
)
919 struct command_entry
*c
;
921 if (l
== NULL
|| counter
== NULL
)
924 c
= TAILQ_LAST(l
, command_list
);
928 TAILQ_REMOVE(l
, c
, entry
);
935 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
937 struct command_entry
*c
;
940 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
943 /* don't add the same line */
944 c
= TAILQ_FIRST(list
);
946 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
949 c
= g_malloc0(sizeof *c
);
950 c
->line
= g_strdup_printf(" %s", l
);
953 TAILQ_INSERT_HEAD(list
, c
, entry
);
956 history_delete(list
, counter
);
958 if (history_autosave
&& file
) {
959 f
= fopen(file
, "w");
961 show_oops(NULL
, "couldn't write history %s", file
);
965 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
967 fprintf(f
, "%s\n", c
->line
);
975 history_read(struct command_list
*list
, char *file
, int *counter
)
978 char *s
, line
[65536];
980 if (list
== NULL
|| file
== NULL
)
983 f
= fopen(file
, "r");
985 startpage_add("couldn't open history file %s", file
);
990 s
= fgets(line
, sizeof line
, f
);
991 if (s
== NULL
|| feof(f
) || ferror(f
))
993 if ((s
= strchr(line
, '\n')) == NULL
) {
994 startpage_add("invalid history file %s", file
);
1000 history_add(list
, NULL
, line
+ 1, counter
);
1008 /* marks and quickmarks array storage.
1009 * first a-z, then A-Z, then 0-9 */
1016 if (i
>= 0 && i
<= 'z' - 'a')
1020 if (i
>= 0 && i
<= 'Z' - 'A')
1035 if (m
>= 'a' && m
<= 'z')
1036 return ret
+ m
- 'a';
1038 ret
+= 'z' - 'a' + 1;
1039 if (m
>= 'A' && m
<= 'Z')
1040 return ret
+ m
- 'A';
1042 ret
+= 'Z' - 'A' + 1;
1043 if (m
>= '0' && m
<= '9')
1044 return ret
+ m
- '0';
1053 int saved_errno
, status
;
1056 saved_errno
= errno
;
1058 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1062 if (errno
!= ECHILD
) {
1064 clog_warn("sigchild: waitpid:");
1070 if (WIFEXITED(status
)) {
1071 if (WEXITSTATUS(status
) != 0) {
1073 clog_warnx("sigchild: child exit status: %d",
1074 WEXITSTATUS(status));
1079 clog_warnx("sigchild: child is terminated abnormally");
1084 errno
= saved_errno
;
1088 is_g_object_setting(GObject
*o
, char *str
)
1090 guint n_props
= 0, i
;
1091 GParamSpec
**proplist
;
1093 if (! G_IS_OBJECT(o
))
1096 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1099 for (i
=0; i
< n_props
; i
++) {
1100 if (! strcmp(proplist
[i
]->name
, str
))
1107 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1111 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1113 "<title>%s</title>\n"
1122 addstyles
? XT_PAGE_STYLE
: "",
1131 * Display a web page from a HTML string in memory, rather than from a URL
1134 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1136 char file
[PATH_MAX
];
1139 /* we set this to indicate we want to manually do navaction */
1141 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1143 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1145 /* set t->xtp_meaning */
1146 for (i
= 0; i
< LENGTH(about_list
); i
++)
1147 if (!strcmp(title
, about_list
[i
].name
)) {
1152 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1153 #if GTK_CHECK_VERSION(2, 20, 0)
1154 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1155 gtk_widget_hide(t
->spinner
);
1157 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1158 xt_icon_from_file(t
, file
);
1163 get_current_tab(void)
1167 TAILQ_FOREACH(t
, &tabs
, entry
) {
1168 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1172 warnx("%s: no current tab", __func__
);
1178 set_status(struct tab
*t
, gchar
*s
, int status
)
1186 case XT_STATUS_LOADING
:
1187 type
= g_strdup_printf("Loading: %s", s
);
1190 case XT_STATUS_LINK
:
1191 type
= g_strdup_printf("Link: %s", s
);
1193 t
->status
= g_strdup(gtk_entry_get_text(
1194 GTK_ENTRY(t
->sbe
.statusbar
)));
1198 type
= g_strdup_printf("%s", s
);
1200 t
->status
= g_strdup(type
);
1204 t
->status
= g_strdup(s
);
1206 case XT_STATUS_NOTHING
:
1211 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1217 hide_cmd(struct tab
*t
)
1219 history_at
= NULL
; /* just in case */
1220 search_at
= NULL
; /* just in case */
1221 gtk_widget_hide(t
->cmd
);
1225 show_cmd(struct tab
*t
)
1229 gtk_widget_hide(t
->oops
);
1230 gtk_widget_show(t
->cmd
);
1234 hide_buffers(struct tab
*t
)
1236 gtk_widget_hide(t
->buffers
);
1237 gtk_list_store_clear(buffers_store
);
1247 sort_tabs_by_page_num(struct tab
***stabs
)
1252 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1254 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1256 TAILQ_FOREACH(t
, &tabs
, entry
)
1257 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1263 buffers_make_list(void)
1266 const gchar
*title
= NULL
;
1268 struct tab
**stabs
= NULL
;
1270 num_tabs
= sort_tabs_by_page_num(&stabs
);
1272 for (i
= 0; i
< num_tabs
; i
++)
1274 gtk_list_store_append(buffers_store
, &iter
);
1275 title
= get_title(stabs
[i
], FALSE
);
1276 gtk_list_store_set(buffers_store
, &iter
,
1277 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1287 show_buffers(struct tab
*t
)
1289 buffers_make_list();
1290 gtk_widget_show(t
->buffers
);
1291 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1295 toggle_buffers(struct tab
*t
)
1297 if (gtk_widget_get_visible(t
->buffers
))
1304 buffers(struct tab
*t
, struct karg
*args
)
1312 hide_oops(struct tab
*t
)
1314 gtk_widget_hide(t
->oops
);
1318 show_oops(struct tab
*at
, const char *fmt
, ...)
1322 struct tab
*t
= NULL
;
1328 if ((t
= get_current_tab()) == NULL
)
1334 if (vasprintf(&msg
, fmt
, ap
) == -1)
1335 errx(1, "show_oops failed");
1338 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1339 gtk_widget_hide(t
->cmd
);
1340 gtk_widget_show(t
->oops
);
1347 get_as_string(struct settings
*s
)
1358 warnx("get_as_string skip %s\n", s
->name
);
1359 } else if (s
->type
== XT_S_INT
)
1360 r
= g_strdup_printf("%d", *s
->ival
);
1361 else if (s
->type
== XT_S_STR
)
1362 r
= g_strdup(*s
->sval
);
1363 else if (s
->type
== XT_S_FLOAT
)
1364 r
= g_strdup_printf("%f", *s
->fval
);
1366 r
= g_strdup_printf("INVALID TYPE");
1372 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1377 for (i
= 0; i
< LENGTH(rs
); i
++) {
1378 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1379 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1381 s
= get_as_string(&rs
[i
]);
1382 cb(&rs
[i
], s
, cb_args
);
1389 set_browser_mode(struct settings
*s
, char *val
)
1391 if (!strcmp(val
, "whitelist")) {
1392 browser_mode
= XT_BM_WHITELIST
;
1393 allow_volatile_cookies
= 0;
1394 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1395 cookies_enabled
= 1;
1396 enable_cookie_whitelist
= 1;
1397 read_only_cookies
= 0;
1398 save_rejected_cookies
= 0;
1399 session_timeout
= 3600;
1401 enable_js_whitelist
= 1;
1402 enable_localstorage
= 0;
1403 } else if (!strcmp(val
, "normal")) {
1404 browser_mode
= XT_BM_NORMAL
;
1405 allow_volatile_cookies
= 0;
1406 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1407 cookies_enabled
= 1;
1408 enable_cookie_whitelist
= 0;
1409 read_only_cookies
= 0;
1410 save_rejected_cookies
= 0;
1411 session_timeout
= 3600;
1413 enable_js_whitelist
= 0;
1414 enable_localstorage
= 1;
1415 } else if (!strcmp(val
, "kiosk")) {
1416 browser_mode
= XT_BM_KIOSK
;
1417 allow_volatile_cookies
= 0;
1418 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1419 cookies_enabled
= 1;
1420 enable_cookie_whitelist
= 0;
1421 read_only_cookies
= 0;
1422 save_rejected_cookies
= 0;
1423 session_timeout
= 3600;
1425 enable_js_whitelist
= 0;
1426 enable_localstorage
= 1;
1436 get_browser_mode(struct settings
*s
)
1440 if (browser_mode
== XT_BM_WHITELIST
)
1441 r
= g_strdup("whitelist");
1442 else if (browser_mode
== XT_BM_NORMAL
)
1443 r
= g_strdup("normal");
1444 else if (browser_mode
== XT_BM_KIOSK
)
1445 r
= g_strdup("kiosk");
1453 set_cookie_policy(struct settings
*s
, char *val
)
1455 if (!strcmp(val
, "no3rdparty"))
1456 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1457 else if (!strcmp(val
, "accept"))
1458 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1459 else if (!strcmp(val
, "reject"))
1460 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1468 get_cookie_policy(struct settings
*s
)
1472 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1473 r
= g_strdup("no3rdparty");
1474 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1475 r
= g_strdup("accept");
1476 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1477 r
= g_strdup("reject");
1485 get_default_script(struct settings
*s
)
1487 if (default_script
[0] == '\0')
1489 return (g_strdup(default_script
));
1493 set_default_script(struct settings
*s
, char *val
)
1496 snprintf(default_script
, sizeof default_script
, "%s/%s",
1497 pwd
->pw_dir
, &val
[1]);
1499 strlcpy(default_script
, val
, sizeof default_script
);
1505 get_download_dir(struct settings
*s
)
1507 if (download_dir
[0] == '\0')
1509 return (g_strdup(download_dir
));
1513 set_download_dir(struct settings
*s
, char *val
)
1516 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1517 pwd
->pw_dir
, &val
[1]);
1519 strlcpy(download_dir
, val
, sizeof download_dir
);
1526 * We use these to prevent people putting xxxt:// URLs on
1527 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1529 #define XT_XTP_SES_KEY_SZ 8
1530 #define XT_XTP_SES_KEY_HEX_FMT \
1531 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1532 char *dl_session_key
; /* downloads */
1533 char *hl_session_key
; /* history list */
1534 char *cl_session_key
; /* cookie list */
1535 char *fl_session_key
; /* favorites list */
1537 char work_dir
[PATH_MAX
];
1538 char certs_dir
[PATH_MAX
];
1539 char cache_dir
[PATH_MAX
];
1540 char sessions_dir
[PATH_MAX
];
1541 char cookie_file
[PATH_MAX
];
1542 SoupURI
*proxy_uri
= NULL
;
1543 SoupSession
*session
;
1544 SoupCookieJar
*s_cookiejar
;
1545 SoupCookieJar
*p_cookiejar
;
1546 char rc_fname
[PATH_MAX
];
1548 struct mime_type_list mtl
;
1549 struct alias_list aliases
;
1552 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1553 void delete_tab(struct tab
*);
1554 void setzoom_webkit(struct tab
*, int);
1555 int run_script(struct tab
*, char *);
1556 int download_rb_cmp(struct download
*, struct download
*);
1557 gboolean
cmd_execute(struct tab
*t
, char *str
);
1560 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1562 return (strcmp(h1
->uri
, h2
->uri
));
1564 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1567 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1569 return (strcmp(d1
->d
, d2
->d
));
1571 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1574 get_work_dir(struct settings
*s
)
1576 if (work_dir
[0] == '\0')
1578 return (g_strdup(work_dir
));
1582 set_work_dir(struct settings
*s
, char *val
)
1585 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1586 pwd
->pw_dir
, &val
[1]);
1588 strlcpy(work_dir
, val
, sizeof work_dir
);
1594 get_tab_style(struct settings
*s
)
1596 if (tab_style
== XT_TABS_NORMAL
)
1597 return (g_strdup("normal"));
1599 return (g_strdup("compact"));
1603 set_tab_style(struct settings
*s
, char *val
)
1605 if (!strcmp(val
, "normal"))
1606 tab_style
= XT_TABS_NORMAL
;
1607 else if (!strcmp(val
, "compact"))
1608 tab_style
= XT_TABS_COMPACT
;
1616 * generate a session key to secure xtp commands.
1617 * pass in a ptr to the key in question and it will
1618 * be modified in place.
1621 generate_xtp_session_key(char **key
)
1623 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1629 /* make a new one */
1630 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1631 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1632 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1633 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1635 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1639 * validate a xtp session key.
1643 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1645 if (strcmp(trusted
, untrusted
) != 0) {
1646 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1655 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1657 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1659 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1661 struct valid_url_types
{
1672 valid_url_type(char *url
)
1676 for (i
= 0; i
< LENGTH(vut
); i
++)
1677 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1684 print_cookie(char *msg
, SoupCookie
*c
)
1690 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1691 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1692 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1693 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1694 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1695 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1696 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1697 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1698 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1699 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1703 walk_alias(struct settings
*s
,
1704 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1709 if (s
== NULL
|| cb
== NULL
) {
1710 show_oops(NULL
, "walk_alias invalid parameters");
1714 TAILQ_FOREACH(a
, &aliases
, entry
) {
1715 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1716 cb(s
, str
, cb_args
);
1722 match_alias(char *url_in
)
1726 char *url_out
= NULL
, *search
, *enc_arg
;
1728 search
= g_strdup(url_in
);
1730 if (strsep(&arg
, " \t") == NULL
) {
1731 show_oops(NULL
, "match_alias: NULL URL");
1735 TAILQ_FOREACH(a
, &aliases
, entry
) {
1736 if (!strcmp(search
, a
->a_name
))
1741 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1744 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1745 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1748 url_out
= g_strdup_printf(a
->a_uri
, "");
1756 guess_url_type(char *url_in
)
1759 char *url_out
= NULL
, *enc_search
= NULL
;
1762 /* substitute aliases */
1763 url_out
= match_alias(url_in
);
1764 if (url_out
!= NULL
)
1767 /* see if we are an about page */
1768 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1769 for (i
= 0; i
< LENGTH(about_list
); i
++)
1770 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1771 about_list
[i
].name
)) {
1772 url_out
= g_strdup(url_in
);
1776 if (guess_search
&& url_regex
&&
1777 !(g_str_has_prefix(url_in
, "http://") ||
1778 g_str_has_prefix(url_in
, "https://"))) {
1779 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1780 /* invalid URI so search instead */
1781 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1782 url_out
= g_strdup_printf(search_string
, enc_search
);
1788 /* XXX not sure about this heuristic */
1789 if (stat(url_in
, &sb
) == 0)
1790 url_out
= g_strdup_printf("file://%s", url_in
);
1792 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1794 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1800 load_uri(struct tab
*t
, gchar
*uri
)
1803 gchar
*newuri
= NULL
;
1809 /* Strip leading spaces. */
1810 while (*uri
&& isspace(*uri
))
1813 if (strlen(uri
) == 0) {
1818 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1820 if (valid_url_type(uri
)) {
1821 newuri
= guess_url_type(uri
);
1825 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1826 for (i
= 0; i
< LENGTH(about_list
); i
++)
1827 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1828 bzero(&args
, sizeof args
);
1829 about_list
[i
].func(t
, &args
);
1830 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1834 show_oops(t
, "invalid about page");
1838 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1840 webkit_web_view_load_uri(t
->wv
, uri
);
1847 get_uri(struct tab
*t
)
1849 const gchar
*uri
= NULL
;
1851 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1853 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1854 uri
= webkit_web_view_get_uri(t
->wv
);
1856 /* use tmp_uri to make sure it is g_freed */
1859 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1860 about_list
[t
->xtp_meaning
].name
);
1867 get_title(struct tab
*t
, bool window
)
1869 const gchar
*set
= NULL
, *title
= NULL
;
1870 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1872 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1873 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1876 title
= webkit_web_view_get_title(t
->wv
);
1877 if ((set
= title
? title
: get_uri(t
)))
1881 set
= window
? XT_NAME
: "(untitled)";
1887 add_alias(struct settings
*s
, char *line
)
1890 struct alias
*a
= NULL
;
1892 if (s
== NULL
|| line
== NULL
) {
1893 show_oops(NULL
, "add_alias invalid parameters");
1898 a
= g_malloc(sizeof(*a
));
1900 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1901 show_oops(NULL
, "add_alias: incomplete alias definition");
1904 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1905 show_oops(NULL
, "add_alias: invalid alias definition");
1909 a
->a_name
= g_strdup(alias
);
1910 a
->a_uri
= g_strdup(l
);
1912 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1914 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1924 add_mime_type(struct settings
*s
, char *line
)
1928 struct mime_type
*m
= NULL
;
1929 int downloadfirst
= 0;
1931 /* XXX this could be smarter */
1933 if (line
== NULL
|| strlen(line
) == 0) {
1934 show_oops(NULL
, "add_mime_type invalid parameters");
1943 m
= g_malloc(sizeof(*m
));
1945 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1946 show_oops(NULL
, "add_mime_type: invalid mime_type");
1949 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1950 mime_type
[strlen(mime_type
) - 1] = '\0';
1955 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1956 show_oops(NULL
, "add_mime_type: invalid mime_type");
1960 m
->mt_type
= g_strdup(mime_type
);
1961 m
->mt_action
= g_strdup(l
);
1962 m
->mt_download
= downloadfirst
;
1964 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1965 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1967 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1977 find_mime_type(char *mime_type
)
1979 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1981 TAILQ_FOREACH(m
, &mtl
, entry
) {
1982 if (m
->mt_default
&&
1983 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1986 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1999 walk_mime_type(struct settings
*s
,
2000 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2002 struct mime_type
*m
;
2005 if (s
== NULL
|| cb
== NULL
) {
2006 show_oops(NULL
, "walk_mime_type invalid parameters");
2010 TAILQ_FOREACH(m
, &mtl
, entry
) {
2011 str
= g_strdup_printf("%s%s --> %s",
2013 m
->mt_default
? "*" : "",
2015 cb(s
, str
, cb_args
);
2021 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2027 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2030 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2032 /* treat *.moo.com the same as .moo.com */
2033 if (str
[0] == '*' && str
[1] == '.')
2035 else if (str
[0] == '.')
2040 /* slice off port number */
2041 p
= g_strrstr(str
, ":");
2045 d
= g_malloc(sizeof *d
);
2047 d
->d
= g_strdup_printf(".%s", str
);
2049 d
->d
= g_strdup(str
);
2052 if (RB_INSERT(domain_list
, wl
, d
))
2055 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2066 add_cookie_wl(struct settings
*s
, char *entry
)
2068 wl_add(entry
, &c_wl
, 1);
2073 walk_cookie_wl(struct settings
*s
,
2074 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2078 if (s
== NULL
|| cb
== NULL
) {
2079 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2083 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2084 cb(s
, d
->d
, cb_args
);
2088 walk_js_wl(struct settings
*s
,
2089 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2093 if (s
== NULL
|| cb
== NULL
) {
2094 show_oops(NULL
, "walk_js_wl invalid parameters");
2098 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2099 cb(s
, d
->d
, cb_args
);
2103 add_js_wl(struct settings
*s
, char *entry
)
2105 wl_add(entry
, &js_wl
, 1 /* persistent */);
2110 wl_find(const gchar
*search
, struct domain_list
*wl
)
2113 struct domain
*d
= NULL
, dfind
;
2116 if (search
== NULL
|| wl
== NULL
)
2118 if (strlen(search
) < 2)
2121 if (search
[0] != '.')
2122 s
= g_strdup_printf(".%s", search
);
2124 s
= g_strdup(search
);
2126 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2129 d
= RB_FIND(domain_list
, wl
, &dfind
);
2143 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2149 if (s
== NULL
|| wl
== NULL
)
2152 if (!strncmp(s
, "http://", strlen("http://")))
2153 s
= &s
[strlen("http://")];
2154 else if (!strncmp(s
, "https://", strlen("https://")))
2155 s
= &s
[strlen("https://")];
2160 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2161 /* chop string at first slash */
2162 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2165 r
= wl_find(ss
, wl
);
2174 settings_add(char *var
, char *val
)
2181 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2182 if (strcmp(var
, rs
[i
].name
))
2186 if (rs
[i
].s
->set(&rs
[i
], val
))
2187 errx(1, "invalid value for %s: %s", var
, val
);
2191 switch (rs
[i
].type
) {
2200 errx(1, "invalid sval for %s",
2214 errx(1, "invalid type for %s", var
);
2223 config_parse(char *filename
, int runtime
)
2226 char *line
, *cp
, *var
, *val
;
2227 size_t len
, lineno
= 0;
2229 char file
[PATH_MAX
];
2232 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2234 if (filename
== NULL
)
2237 if (runtime
&& runtime_settings
[0] != '\0') {
2238 snprintf(file
, sizeof file
, "%s/%s",
2239 work_dir
, runtime_settings
);
2240 if (stat(file
, &sb
)) {
2241 warnx("runtime file doesn't exist, creating it");
2242 if ((f
= fopen(file
, "w")) == NULL
)
2244 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2248 strlcpy(file
, filename
, sizeof file
);
2250 if ((config
= fopen(file
, "r")) == NULL
) {
2251 warn("config_parse: cannot open %s", filename
);
2256 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2257 if (feof(config
) || ferror(config
))
2261 cp
+= (long)strspn(cp
, WS
);
2262 if (cp
[0] == '\0') {
2268 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2269 startpage_add("invalid configuration file entry: %s",
2272 cp
+= (long)strspn(cp
, WS
);
2274 if ((val
= strsep(&cp
, "\0")) == NULL
)
2277 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2278 handled
= settings_add(var
, val
);
2280 startpage_add("invalid configuration file entry: %s=%s",
2290 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2296 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2300 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2303 JSStringGetUTF8CString(jsref
, s
, l
);
2304 JSStringRelease(jsref
);
2310 disable_hints(struct tab
*t
)
2312 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2313 bzero(t
->hint_num
, sizeof t
->hint_num
);
2314 run_script(t
, "vimprobable_clear()");
2316 t
->hint_mode
= XT_HINT_NONE
;
2320 enable_hints(struct tab
*t
)
2322 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2323 run_script(t
, "vimprobable_show_hints()");
2325 t
->hint_mode
= XT_HINT_NONE
;
2328 #define XT_JS_OPEN ("open;")
2329 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2330 #define XT_JS_FIRE ("fire;")
2331 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2332 #define XT_JS_FOUND ("found;")
2333 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2336 run_script(struct tab
*t
, char *s
)
2338 JSGlobalContextRef ctx
;
2339 WebKitWebFrame
*frame
;
2341 JSValueRef val
, exception
;
2344 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2345 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2347 frame
= webkit_web_view_get_main_frame(t
->wv
);
2348 ctx
= webkit_web_frame_get_global_context(frame
);
2350 str
= JSStringCreateWithUTF8CString(s
);
2351 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2352 NULL
, 0, &exception
);
2353 JSStringRelease(str
);
2355 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2357 es
= js_ref_to_string(ctx
, exception
);
2358 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2362 es
= js_ref_to_string(ctx
, val
);
2363 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2365 /* handle return value right here */
2366 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2369 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2372 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2373 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2374 &es
[XT_JS_FIRE_LEN
]);
2379 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2380 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2391 hint(struct tab
*t
, struct karg
*args
)
2394 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2396 if (t
->hints_on
== 0)
2405 apply_style(struct tab
*t
)
2407 g_object_set(G_OBJECT(t
->settings
),
2408 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2412 userstyle(struct tab
*t
, struct karg
*args
)
2414 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2418 g_object_set(G_OBJECT(t
->settings
),
2419 "user-stylesheet-uri", NULL
, (char *)NULL
);
2428 * Doesn't work fully, due to the following bug:
2429 * https://bugs.webkit.org/show_bug.cgi?id=51747
2432 restore_global_history(void)
2434 char file
[PATH_MAX
];
2439 const char delim
[3] = {'\\', '\\', '\0'};
2441 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2443 if ((f
= fopen(file
, "r")) == NULL
) {
2444 warnx("%s: fopen", __func__
);
2449 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2450 if (feof(f
) || ferror(f
))
2453 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2454 if (feof(f
) || ferror(f
)) {
2456 warnx("%s: broken history file\n", __func__
);
2460 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2461 webkit_web_history_item_new_with_data(uri
, title
);
2462 h
= g_malloc(sizeof(struct history
));
2463 h
->uri
= g_strdup(uri
);
2464 h
->title
= g_strdup(title
);
2465 RB_INSERT(history_list
, &hl
, h
);
2466 completion_add_uri(h
->uri
);
2468 warnx("%s: failed to restore history\n", __func__
);
2484 save_global_history_to_disk(struct tab
*t
)
2486 char file
[PATH_MAX
];
2490 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2492 if ((f
= fopen(file
, "w")) == NULL
) {
2493 show_oops(t
, "%s: global history file: %s",
2494 __func__
, strerror(errno
));
2498 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2499 if (h
->uri
&& h
->title
)
2500 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2509 quit(struct tab
*t
, struct karg
*args
)
2511 if (save_global_history
)
2512 save_global_history_to_disk(t
);
2520 restore_sessions_list(void)
2523 struct dirent
*dp
= NULL
;
2526 sdir
= opendir(sessions_dir
);
2528 while ((dp
= readdir(sdir
)) != NULL
)
2529 if (dp
->d_type
== DT_REG
) {
2530 s
= g_malloc(sizeof(struct session
));
2531 s
->name
= g_strdup(dp
->d_name
);
2532 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2539 open_tabs(struct tab
*t
, struct karg
*a
)
2541 char file
[PATH_MAX
];
2545 struct tab
*ti
, *tt
;
2550 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2551 if ((f
= fopen(file
, "r")) == NULL
)
2554 ti
= TAILQ_LAST(&tabs
, tab_list
);
2557 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2558 if (feof(f
) || ferror(f
))
2561 /* retrieve session name */
2562 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2563 strlcpy(named_session
,
2564 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2565 sizeof named_session
);
2569 if (uri
&& strlen(uri
))
2570 create_new_tab(uri
, NULL
, 1, -1);
2576 /* close open tabs */
2577 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2579 tt
= TAILQ_FIRST(&tabs
);
2599 restore_saved_tabs(void)
2601 char file
[PATH_MAX
];
2602 int unlink_file
= 0;
2607 snprintf(file
, sizeof file
, "%s/%s",
2608 sessions_dir
, XT_RESTART_TABS_FILE
);
2609 if (stat(file
, &sb
) == -1)
2610 a
.s
= XT_SAVED_TABS_FILE
;
2613 a
.s
= XT_RESTART_TABS_FILE
;
2616 a
.i
= XT_SES_DONOTHING
;
2617 rv
= open_tabs(NULL
, &a
);
2626 save_tabs(struct tab
*t
, struct karg
*a
)
2628 char file
[PATH_MAX
];
2630 int num_tabs
= 0, i
;
2631 struct tab
**stabs
= NULL
;
2636 snprintf(file
, sizeof file
, "%s/%s",
2637 sessions_dir
, named_session
);
2639 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2641 if ((f
= fopen(file
, "w")) == NULL
) {
2642 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2646 /* save session name */
2647 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2649 /* Save tabs, in the order they are arranged in the notebook. */
2650 num_tabs
= sort_tabs_by_page_num(&stabs
);
2652 for (i
= 0; i
< num_tabs
; i
++)
2654 if (get_uri(stabs
[i
]) != NULL
)
2655 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2656 else if (gtk_entry_get_text(GTK_ENTRY(
2657 stabs
[i
]->uri_entry
)))
2658 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2659 stabs
[i
]->uri_entry
)));
2664 /* try and make sure this gets to disk NOW. XXX Backup first? */
2665 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2666 show_oops(t
, "May not have managed to save session: %s",
2676 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2688 run_page_script(struct tab
*t
, struct karg
*args
)
2691 char *tmp
, script
[PATH_MAX
];
2693 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2694 if (tmp
[0] == '\0') {
2695 show_oops(t
, "no script specified");
2699 if ((uri
= get_uri(t
)) == NULL
) {
2700 show_oops(t
, "tab is empty, not running script");
2705 snprintf(script
, sizeof script
, "%s/%s",
2706 pwd
->pw_dir
, &tmp
[1]);
2708 strlcpy(script
, tmp
, sizeof script
);
2712 show_oops(t
, "can't fork to run script");
2722 execlp(script
, script
, uri
, (void *)NULL
);
2732 yank_uri(struct tab
*t
, struct karg
*args
)
2735 GtkClipboard
*clipboard
;
2737 if ((uri
= get_uri(t
)) == NULL
)
2740 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2741 gtk_clipboard_set_text(clipboard
, uri
, -1);
2747 paste_uri(struct tab
*t
, struct karg
*args
)
2749 GtkClipboard
*clipboard
;
2750 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2752 gchar
*p
= NULL
, *uri
;
2754 /* try primary clipboard first */
2755 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2756 p
= gtk_clipboard_wait_for_text(clipboard
);
2758 /* if it failed get whatever text is in cut_buffer0 */
2759 if (p
== NULL
&& xterm_workaround
)
2760 if (gdk_property_get(gdk_get_default_root_window(),
2762 gdk_atom_intern("STRING", FALSE
),
2764 1024 * 1024 /* picked out of my butt */,
2770 /* yes sir, we need to NUL the string */
2776 while (*uri
&& isspace(*uri
))
2778 if (strlen(uri
) == 0) {
2779 show_oops(t
, "empty paste buffer");
2782 if (guess_search
== 0 && valid_url_type(uri
)) {
2783 /* we can be clever and paste this in search box */
2784 show_oops(t
, "not a valid URL");
2788 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2790 else if (args
->i
== XT_PASTE_NEW_TAB
)
2791 create_new_tab(uri
, NULL
, 1, -1);
2802 find_domain(const gchar
*s
, int toplevel
)
2810 uri
= soup_uri_new(s
);
2812 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2816 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2817 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2818 while(--p
>= uri
->host
&& *p
!= '.');
2825 ret
= g_strdup_printf(".%s", p
);
2833 toggle_cwl(struct tab
*t
, struct karg
*args
)
2844 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2846 if (uri
== NULL
|| dom
== NULL
||
2847 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2848 show_oops(t
, "Can't toggle domain in cookie white list");
2851 d
= wl_find(dom
, &c_wl
);
2858 if (args
->i
& XT_WL_TOGGLE
)
2860 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2862 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2866 /* enable cookies for domain */
2867 wl_add(dom
, &c_wl
, 0);
2869 /* disable cookies for domain */
2870 RB_REMOVE(domain_list
, &c_wl
, d
);
2872 if (args
->i
& XT_WL_RELOAD
)
2873 webkit_web_view_reload(t
->wv
);
2881 toggle_js(struct tab
*t
, struct karg
*args
)
2891 g_object_get(G_OBJECT(t
->settings
),
2892 "enable-scripts", &es
, (char *)NULL
);
2893 if (args
->i
& XT_WL_TOGGLE
)
2895 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2897 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2903 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2905 if (uri
== NULL
|| dom
== NULL
||
2906 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2907 show_oops(t
, "Can't toggle domain in JavaScript white list");
2912 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2913 wl_add(dom
, &js_wl
, 0 /* session */);
2915 d
= wl_find(dom
, &js_wl
);
2917 RB_REMOVE(domain_list
, &js_wl
, d
);
2918 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2920 g_object_set(G_OBJECT(t
->settings
),
2921 "enable-scripts", es
, (char *)NULL
);
2922 g_object_set(G_OBJECT(t
->settings
),
2923 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2924 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2926 if (args
->i
& XT_WL_RELOAD
)
2927 webkit_web_view_reload(t
->wv
);
2935 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2939 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2942 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2947 toggle_src(struct tab
*t
, struct karg
*args
)
2954 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2955 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2956 webkit_web_view_reload(t
->wv
);
2962 focus_webview(struct tab
*t
)
2967 /* only grab focus if we are visible */
2968 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2969 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2973 focus(struct tab
*t
, struct karg
*args
)
2975 if (t
== NULL
|| args
== NULL
)
2981 if (args
->i
== XT_FOCUS_URI
)
2982 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2983 else if (args
->i
== XT_FOCUS_SEARCH
)
2984 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2990 stats(struct tab
*t
, struct karg
*args
)
2992 char *page
, *body
, *s
, line
[64 * 1024];
2993 uint64_t line_count
= 0;
2997 show_oops(NULL
, "stats invalid parameters");
3000 if (save_rejected_cookies
) {
3001 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
3003 s
= fgets(line
, sizeof line
, r_cookie_f
);
3004 if (s
== NULL
|| feof(r_cookie_f
) ||
3010 snprintf(line
, sizeof line
,
3011 "<br/>Cookies blocked(*) total: %llu", line_count
);
3013 show_oops(t
, "Can't open blocked cookies file: %s",
3017 body
= g_strdup_printf(
3018 "Cookies blocked(*) this session: %llu"
3020 "<p><small><b>*</b> results vary based on settings</small></p>",
3024 page
= get_html_page("Statistics", body
, "", 0);
3027 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3034 marco(struct tab
*t
, struct karg
*args
)
3036 char *page
, line
[64 * 1024];
3040 show_oops(NULL
, "marco invalid parameters");
3043 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3045 page
= get_html_page("Marco Sez...", line
, "", 0);
3047 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3054 blank(struct tab
*t
, struct karg
*args
)
3057 show_oops(NULL
, "blank invalid parameters");
3059 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3065 about(struct tab
*t
, struct karg
*args
)
3070 show_oops(NULL
, "about invalid parameters");
3072 body
= g_strdup_printf("<b>Version: %s</b>"
3073 #ifdef XXXTERM_BUILDSTR
3074 "<br><b>Build: %s</b>"
3079 "<li>Marco Peereboom <marco@peereboom.us></li>"
3080 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3081 "<li>Edd Barrett <vext01@gmail.com> </li>"
3082 "<li>Todd T. Fries <todd@fries.net> </li>"
3083 "<li>Raphael Graf <r@undefined.ch> </li>"
3085 "Copyrights and licenses can be found on the XXXTerm "
3086 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3088 #ifdef XXXTERM_BUILDSTR
3089 version
, XXXTERM_BUILDSTR
3095 page
= get_html_page("About", body
, "", 0);
3098 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3105 help(struct tab
*t
, struct karg
*args
)
3107 char *page
, *head
, *body
;
3110 show_oops(NULL
, "help invalid parameters");
3112 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3113 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3115 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3116 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3117 "cgi-bin/man-cgi?xxxterm</a>";
3119 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3121 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3128 startpage(struct tab
*t
, struct karg
*args
)
3130 char *page
, *body
, *b
;
3134 show_oops(NULL
, "startpage invalid parameters");
3136 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3138 TAILQ_FOREACH(s
, &spl
, entry
) {
3140 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3144 page
= get_html_page("Startup Exception", body
, "", 0);
3147 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3154 startpage_add(const char *fmt
, ...)
3164 if (vasprintf(&msg
, fmt
, ap
) == -1)
3165 errx(1, "startpage_add failed");
3168 s
= g_malloc0(sizeof *s
);
3171 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3175 * update all favorite tabs apart from one. Pass NULL if
3176 * you want to update all.
3179 update_favorite_tabs(struct tab
*apart_from
)
3182 if (!updating_fl_tabs
) {
3183 updating_fl_tabs
= 1; /* stop infinite recursion */
3184 TAILQ_FOREACH(t
, &tabs
, entry
)
3185 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3186 && (t
!= apart_from
))
3187 xtp_page_fl(t
, NULL
);
3188 updating_fl_tabs
= 0;
3192 /* show a list of favorites (bookmarks) */
3194 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3196 char file
[PATH_MAX
];
3198 char *uri
= NULL
, *title
= NULL
;
3199 size_t len
, lineno
= 0;
3201 char *body
, *tmp
, *page
= NULL
;
3202 const char delim
[3] = {'\\', '\\', '\0'};
3204 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3207 warn("%s: bad param", __func__
);
3209 /* new session key */
3210 if (!updating_fl_tabs
)
3211 generate_xtp_session_key(&fl_session_key
);
3213 /* open favorites */
3214 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3215 if ((f
= fopen(file
, "r")) == NULL
) {
3216 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3221 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3222 "<th style='width: 40px'>#</th><th>Link</th>"
3223 "<th style='width: 40px'>Rm</th></tr>\n");
3226 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3227 if (feof(f
) || ferror(f
))
3229 if (strlen(title
) == 0 || title
[0] == '#') {
3235 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3236 if (feof(f
) || ferror(f
)) {
3237 show_oops(t
, "favorites file corrupt");
3243 body
= g_strdup_printf("%s<tr>"
3245 "<td><a href='%s'>%s</a></td>"
3246 "<td style='text-align: center'>"
3247 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3249 body
, i
, uri
, title
,
3250 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3262 /* if none, say so */
3265 body
= g_strdup_printf("%s<tr>"
3266 "<td colspan='3' style='text-align: center'>"
3267 "No favorites - To add one use the 'favadd' command."
3268 "</td></tr>", body
);
3273 body
= g_strdup_printf("%s</table>", body
);
3283 page
= get_html_page("Favorites", body
, "", 1);
3284 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3288 update_favorite_tabs(t
);
3297 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3298 size_t cert_count
, char *title
)
3300 gnutls_datum_t cinfo
;
3304 body
= g_strdup("");
3306 for (i
= 0; i
< cert_count
; i
++) {
3307 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3312 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3313 body
, i
, cinfo
.data
);
3314 gnutls_free(cinfo
.data
);
3318 tmp
= get_html_page(title
, body
, "", 0);
3321 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3326 ca_cmd(struct tab
*t
, struct karg
*args
)
3329 int rv
= 1, certs
= 0, certs_read
;
3332 gnutls_x509_crt_t
*c
= NULL
;
3333 char *certs_buf
= NULL
, *s
;
3335 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3336 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3340 if (fstat(fileno(f
), &sb
) == -1) {
3341 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3345 certs_buf
= g_malloc(sb
.st_size
+ 1);
3346 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3347 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3350 certs_buf
[sb
.st_size
] = '\0';
3353 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3355 s
+= strlen("BEGIN CERTIFICATE");
3358 bzero(&dt
, sizeof dt
);
3359 dt
.data
= (unsigned char *)certs_buf
;
3360 dt
.size
= sb
.st_size
;
3361 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3362 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3363 GNUTLS_X509_FMT_PEM
, 0);
3364 if (certs_read
<= 0) {
3365 show_oops(t
, "No cert(s) available");
3368 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3381 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3385 struct addrinfo hints
, *res
= NULL
, *ai
;
3386 int rv
= -1, s
= -1, on
, error
;
3389 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3390 show_oops(t
, "invalid URI");
3394 su
= soup_uri_new(uri
);
3396 show_oops(t
, "invalid soup URI");
3399 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3400 show_oops(t
, "invalid HTTPS URI");
3404 snprintf(port
, sizeof port
, "%d", su
->port
);
3405 bzero(&hints
, sizeof(struct addrinfo
));
3406 hints
.ai_flags
= AI_CANONNAME
;
3407 hints
.ai_family
= AF_UNSPEC
;
3408 hints
.ai_socktype
= SOCK_STREAM
;
3410 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3411 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3415 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3421 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3423 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3426 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3429 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3433 show_oops(t
, "could not obtain certificates from: %s",
3439 strlcpy(domain
, su
->host
, domain_sz
);
3446 if (rv
== -1 && s
!= -1)
3453 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3456 gnutls_deinit(gsession
);
3458 gnutls_certificate_free_credentials(xcred
);
3464 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3465 gnutls_certificate_credentials_t
*xc
)
3467 gnutls_certificate_credentials_t xcred
;
3468 gnutls_session_t gsession
;
3471 if (gs
== NULL
|| xc
== NULL
)
3477 gnutls_certificate_allocate_credentials(&xcred
);
3478 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3479 GNUTLS_X509_FMT_PEM
);
3481 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3482 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3483 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3484 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3485 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3486 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3488 gnutls_error_is_fatal(rv
),
3489 gnutls_strerror_name(rv
));
3490 stop_tls(gsession
, xcred
);
3494 gnutls_credentials_type_t cred
;
3495 cred
= gnutls_auth_get_type(gsession
);
3496 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3497 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3498 stop_tls(gsession
, xcred
);
3510 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3514 const gnutls_datum_t
*cl
;
3515 gnutls_x509_crt_t
*all_certs
;
3518 if (certs
== NULL
|| cert_count
== NULL
)
3520 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3522 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3526 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3527 for (i
= 0; i
< len
; i
++) {
3528 gnutls_x509_crt_init(&all_certs
[i
]);
3529 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3530 GNUTLS_X509_FMT_PEM
< 0)) {
3544 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3548 for (i
= 0; i
< cert_count
; i
++)
3549 gnutls_x509_crt_deinit(certs
[i
]);
3554 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3556 GdkColor c_text
, c_base
;
3558 gdk_color_parse(text
, &c_text
);
3559 gdk_color_parse(base
, &c_base
);
3561 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3562 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3563 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3564 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3566 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3567 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3568 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3569 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3573 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3574 size_t cert_count
, char *domain
)
3577 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3582 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3585 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3586 if ((f
= fopen(file
, "w")) == NULL
) {
3587 show_oops(t
, "Can't create cert file %s %s",
3588 file
, strerror(errno
));
3592 for (i
= 0; i
< cert_count
; i
++) {
3593 cert_buf_sz
= sizeof cert_buf
;
3594 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3595 cert_buf
, &cert_buf_sz
)) {
3596 show_oops(t
, "gnutls_x509_crt_export failed");
3599 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3600 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3605 /* not the best spot but oh well */
3606 gdk_color_parse("lightblue", &color
);
3607 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3608 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3621 load_compare_cert(struct tab
*t
, struct karg
*args
)
3624 char domain
[8182], file
[PATH_MAX
];
3625 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3629 size_t cert_buf_sz
, cert_count
;
3630 enum cert_trust rv
= CERT_UNTRUSTED
;
3632 gnutls_session_t gsession
;
3633 gnutls_x509_crt_t
*certs
;
3634 gnutls_certificate_credentials_t xcred
;
3636 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3641 if ((uri
= get_uri(t
)) == NULL
)
3643 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3645 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3647 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3650 if (start_tls(t
, s
, &gsession
, &xcred
))
3652 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3654 /* verify certs in case cert file doesn't exist */
3655 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3657 show_oops(t
, "Invalid certificates");
3662 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3663 show_oops(t
, "Can't get connection certificates");
3667 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3668 if ((f
= fopen(file
, "r")) == NULL
) {
3674 for (i
= 0; i
< cert_count
; i
++) {
3675 cert_buf_sz
= sizeof cert_buf
;
3676 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3677 cert_buf
, &cert_buf_sz
)) {
3680 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3681 rv
= CERT_BAD
; /* critical */
3684 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3685 rv
= CERT_BAD
; /* critical */
3694 free_connection_certs(certs
, cert_count
);
3696 /* we close the socket first for speed */
3700 /* only complain if we didn't save it locally */
3701 if (error
&& rv
!= CERT_LOCAL
) {
3702 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3703 if (error
& GNUTLS_CERT_INVALID
)
3704 strlcat(serr
, "invalid, ", sizeof serr
);
3705 if (error
& GNUTLS_CERT_REVOKED
)
3706 strlcat(serr
, "revoked, ", sizeof serr
);
3707 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3708 strlcat(serr
, "signer not found, ", sizeof serr
);
3709 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3710 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3711 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3712 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3713 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3714 strlcat(serr
, "not activated, ", sizeof serr
);
3715 if (error
& GNUTLS_CERT_EXPIRED
)
3716 strlcat(serr
, "expired, ", sizeof serr
);
3717 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3718 if (serr
[i
] == ',') {
3725 stop_tls(gsession
, xcred
);
3731 cert_cmd(struct tab
*t
, struct karg
*args
)
3737 gnutls_session_t gsession
;
3738 gnutls_x509_crt_t
*certs
;
3739 gnutls_certificate_credentials_t xcred
;
3744 if (ssl_ca_file
== NULL
) {
3745 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3749 if ((uri
= get_uri(t
)) == NULL
) {
3750 show_oops(t
, "Invalid URI");
3754 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3755 show_oops(t
, "Invalid certificate URI: %s", uri
);
3760 if (start_tls(t
, s
, &gsession
, &xcred
))
3764 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3765 show_oops(t
, "get_connection_certs failed");
3769 if (args
->i
& XT_SHOW
)
3770 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3771 else if (args
->i
& XT_SAVE
)
3772 save_certs(t
, certs
, cert_count
, domain
);
3774 free_connection_certs(certs
, cert_count
);
3776 /* we close the socket first for speed */
3779 stop_tls(gsession
, xcred
);
3785 remove_cookie(int index
)
3791 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3793 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3795 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3799 print_cookie("remove cookie", c
);
3800 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3805 soup_cookies_free(cf
);
3811 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3816 body
= g_strdup("");
3819 if (args
->i
& XT_WL_PERSISTENT
) {
3821 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3823 RB_FOREACH(d
, domain_list
, wl
) {
3827 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3833 if (args
->i
& XT_WL_SESSION
) {
3835 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3837 RB_FOREACH(d
, domain_list
, wl
) {
3841 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3846 tmp
= get_html_page(title
, body
, "", 0);
3849 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3851 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3857 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3859 char file
[PATH_MAX
];
3861 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3869 if (t
== NULL
|| args
== NULL
)
3872 if (runtime_settings
[0] == '\0')
3875 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3876 if ((f
= fopen(file
, "r+")) == NULL
)
3880 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3881 if (uri
== NULL
|| dom
== NULL
||
3882 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3883 show_oops(t
, "Can't add domain to %s white list",
3884 js
? "JavaScript" : "cookie");
3888 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3891 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3894 if (!strcmp(line
, lt
))
3900 fprintf(f
, "%s\n", lt
);
3905 d
= wl_find(dom
, &js_wl
);
3907 settings_add("js_wl", dom
);
3908 d
= wl_find(dom
, &js_wl
);
3912 d
= wl_find(dom
, &c_wl
);
3914 settings_add("cookie_wl", dom
);
3915 d
= wl_find(dom
, &c_wl
);
3919 /* find and add to persistent jar */
3920 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3921 for (;cf
; cf
= cf
->next
) {
3923 if (!strcmp(dom
, ci
->domain
) ||
3924 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3925 c
= soup_cookie_copy(ci
);
3926 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3929 soup_cookies_free(cf
);
3947 js_show_wl(struct tab
*t
, struct karg
*args
)
3949 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3950 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3956 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3958 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3959 wl_show(t
, args
, "Cookie White List", &c_wl
);
3965 cookie_cmd(struct tab
*t
, struct karg
*args
)
3967 if (args
->i
& XT_SHOW
)
3968 wl_show(t
, args
, "Cookie White List", &c_wl
);
3969 else if (args
->i
& XT_WL_TOGGLE
) {
3970 args
->i
|= XT_WL_RELOAD
;
3971 toggle_cwl(t
, args
);
3972 } else if (args
->i
& XT_SAVE
) {
3973 args
->i
|= XT_WL_RELOAD
;
3974 wl_save(t
, args
, 0);
3975 } else if (args
->i
& XT_DELETE
)
3976 show_oops(t
, "'cookie delete' currently unimplemented");
3982 js_cmd(struct tab
*t
, struct karg
*args
)
3984 if (args
->i
& XT_SHOW
)
3985 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3986 else if (args
->i
& XT_SAVE
) {
3987 args
->i
|= XT_WL_RELOAD
;
3988 wl_save(t
, args
, 1);
3989 } else if (args
->i
& XT_WL_TOGGLE
) {
3990 args
->i
|= XT_WL_RELOAD
;
3992 } else if (args
->i
& XT_DELETE
)
3993 show_oops(t
, "'js delete' currently unimplemented");
3999 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4001 js_toggle_cb(t
->js_toggle
, t
);
4007 add_favorite(struct tab
*t
, struct karg
*args
)
4009 char file
[PATH_MAX
];
4012 size_t urilen
, linelen
;
4013 const gchar
*uri
, *title
;
4018 /* don't allow adding of xtp pages to favorites */
4019 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4020 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4024 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4025 if ((f
= fopen(file
, "r+")) == NULL
) {
4026 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4030 title
= get_title(t
, FALSE
);
4033 if (title
== NULL
|| uri
== NULL
) {
4034 show_oops(t
, "can't add page to favorites");
4038 urilen
= strlen(uri
);
4041 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4042 if (feof(f
) || ferror(f
))
4045 if (linelen
== urilen
&& !strcmp(line
, uri
))
4052 fprintf(f
, "\n%s\n%s", title
, uri
);
4058 update_favorite_tabs(NULL
);
4064 can_go_back_for_real(struct tab
*t
)
4067 WebKitWebHistoryItem
*item
;
4069 /* rely on webkit to make sure we can go backward when on an about page */
4070 if (get_uri(t
) == NULL
|| g_str_has_prefix(get_uri(t
), "about:"))
4071 return (webkit_web_view_can_go_forward(t
->wv
));
4074 /* the back/forwars list is stupid so help determine if we can go back */
4075 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4077 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4078 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
)))
4086 can_go_forward_for_real(struct tab
*t
)
4089 WebKitWebHistoryItem
*item
;
4091 /* rely on webkit to make sure we can go forward when on an about page */
4092 if (get_uri(t
) == NULL
|| g_str_has_prefix(get_uri(t
), "about:"))
4093 return (webkit_web_view_can_go_forward(t
->wv
));
4095 /* the back/forwars list is stupid so help selecting a different item */
4096 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4098 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4099 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
)))
4107 go_back_for_real(struct tab
*t
)
4110 WebKitWebHistoryItem
*item
;
4112 /* the back/forwars list is stupid so help selecting a different item */
4113 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4115 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4116 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
))) {
4117 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4124 go_forward_for_real(struct tab
*t
)
4127 WebKitWebHistoryItem
*item
;
4129 /* the back/forwars list is stupid so help selecting a different item */
4130 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4132 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4133 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
))) {
4134 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4141 navaction(struct tab
*t
, struct karg
*args
)
4143 WebKitWebHistoryItem
*item
;
4144 WebKitWebFrame
*frame
;
4146 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4147 t
->tab_id
, args
->i
);
4149 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4151 if (args
->i
== XT_NAV_BACK
)
4152 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4154 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4156 return (XT_CB_PASSTHROUGH
);
4157 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4159 return (XT_CB_PASSTHROUGH
);
4165 go_back_for_real(t
);
4167 case XT_NAV_FORWARD
:
4169 go_forward_for_real(t
);
4172 frame
= webkit_web_view_get_main_frame(t
->wv
);
4173 webkit_web_frame_reload(frame
);
4176 return (XT_CB_PASSTHROUGH
);
4180 move(struct tab
*t
, struct karg
*args
)
4182 GtkAdjustment
*adjust
;
4183 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4189 case XT_MOVE_BOTTOM
:
4191 case XT_MOVE_PAGEDOWN
:
4192 case XT_MOVE_PAGEUP
:
4193 case XT_MOVE_HALFDOWN
:
4194 case XT_MOVE_HALFUP
:
4195 case XT_MOVE_PERCENT
:
4196 adjust
= t
->adjust_v
;
4199 adjust
= t
->adjust_h
;
4203 pos
= gtk_adjustment_get_value(adjust
);
4204 ps
= gtk_adjustment_get_page_size(adjust
);
4205 upper
= gtk_adjustment_get_upper(adjust
);
4206 lower
= gtk_adjustment_get_lower(adjust
);
4207 si
= gtk_adjustment_get_step_increment(adjust
);
4208 pi
= gtk_adjustment_get_page_increment(adjust
);
4211 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4212 "max %f si %f pi %f\n",
4213 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4214 pos
, ps
, upper
, lower
, max
, si
, pi
);
4220 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4225 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4227 case XT_MOVE_BOTTOM
:
4228 case XT_MOVE_FARRIGHT
:
4229 gtk_adjustment_set_value(adjust
, max
);
4232 case XT_MOVE_FARLEFT
:
4233 gtk_adjustment_set_value(adjust
, lower
);
4235 case XT_MOVE_PAGEDOWN
:
4237 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4239 case XT_MOVE_PAGEUP
:
4241 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4243 case XT_MOVE_HALFDOWN
:
4245 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4247 case XT_MOVE_HALFUP
:
4249 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4251 case XT_MOVE_PERCENT
:
4252 percent
= atoi(args
->s
) / 100.0;
4253 pos
= max
* percent
;
4254 if (pos
< 0.0 || pos
> max
)
4256 gtk_adjustment_set_value(adjust
, pos
);
4259 return (XT_CB_PASSTHROUGH
);
4262 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4264 return (XT_CB_HANDLED
);
4268 url_set_visibility(void)
4272 TAILQ_FOREACH(t
, &tabs
, entry
)
4273 if (show_url
== 0) {
4274 gtk_widget_hide(t
->toolbar
);
4277 gtk_widget_show(t
->toolbar
);
4281 notebook_tab_set_visibility(void)
4283 if (show_tabs
== 0) {
4284 gtk_widget_hide(tab_bar
);
4285 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4287 if (tab_style
== XT_TABS_NORMAL
) {
4288 gtk_widget_hide(tab_bar
);
4289 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4290 } else if (tab_style
== XT_TABS_COMPACT
) {
4291 gtk_widget_show(tab_bar
);
4292 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4298 statusbar_set_visibility(void)
4302 TAILQ_FOREACH(t
, &tabs
, entry
)
4303 if (show_statusbar
== 0) {
4304 gtk_widget_hide(t
->statusbar_box
);
4307 gtk_widget_show(t
->statusbar_box
);
4311 url_set(struct tab
*t
, int enable_url_entry
)
4316 show_url
= enable_url_entry
;
4318 if (enable_url_entry
) {
4319 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4320 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4321 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4323 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4324 GTK_ENTRY_ICON_PRIMARY
);
4326 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4327 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4328 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4329 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4335 fullscreen(struct tab
*t
, struct karg
*args
)
4337 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4340 return (XT_CB_PASSTHROUGH
);
4342 if (show_url
== 0) {
4350 url_set_visibility();
4351 notebook_tab_set_visibility();
4353 return (XT_CB_HANDLED
);
4357 statustoggle(struct tab
*t
, struct karg
*args
)
4359 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4361 if (show_statusbar
== 1) {
4363 statusbar_set_visibility();
4364 } else if (show_statusbar
== 0) {
4366 statusbar_set_visibility();
4368 return (XT_CB_HANDLED
);
4372 urlaction(struct tab
*t
, struct karg
*args
)
4374 int rv
= XT_CB_HANDLED
;
4376 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4379 return (XT_CB_PASSTHROUGH
);
4383 if (show_url
== 0) {
4385 url_set_visibility();
4389 if (show_url
== 1) {
4391 url_set_visibility();
4399 tabaction(struct tab
*t
, struct karg
*args
)
4401 int rv
= XT_CB_HANDLED
;
4402 char *url
= args
->s
;
4406 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4409 return (XT_CB_PASSTHROUGH
);
4413 if (strlen(url
) > 0)
4414 create_new_tab(url
, NULL
, 1, args
->precount
);
4416 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4419 if (args
->precount
< 0)
4422 TAILQ_FOREACH(tt
, &tabs
, entry
)
4423 if (tt
->tab_id
== args
->precount
- 1) {
4428 case XT_TAB_DELQUIT
:
4429 if (gtk_notebook_get_n_pages(notebook
) > 1)
4435 if (strlen(url
) > 0)
4438 rv
= XT_CB_PASSTHROUGH
;
4444 if (show_tabs
== 0) {
4446 notebook_tab_set_visibility();
4450 if (show_tabs
== 1) {
4452 notebook_tab_set_visibility();
4455 case XT_TAB_NEXTSTYLE
:
4456 if (tab_style
== XT_TABS_NORMAL
) {
4457 tab_style
= XT_TABS_COMPACT
;
4458 recolor_compact_tabs();
4461 tab_style
= XT_TABS_NORMAL
;
4462 notebook_tab_set_visibility();
4464 case XT_TAB_UNDO_CLOSE
:
4465 if (undo_count
== 0) {
4466 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4471 u
= TAILQ_FIRST(&undos
);
4472 create_new_tab(u
->uri
, u
, 1, -1);
4474 TAILQ_REMOVE(&undos
, u
, entry
);
4476 /* u->history is freed in create_new_tab() */
4481 rv
= XT_CB_PASSTHROUGH
;
4495 resizetab(struct tab
*t
, struct karg
*args
)
4497 if (t
== NULL
|| args
== NULL
) {
4498 show_oops(NULL
, "resizetab invalid parameters");
4499 return (XT_CB_PASSTHROUGH
);
4502 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4503 t
->tab_id
, args
->i
);
4505 setzoom_webkit(t
, args
->i
);
4507 return (XT_CB_HANDLED
);
4511 movetab(struct tab
*t
, struct karg
*args
)
4515 if (t
== NULL
|| args
== NULL
) {
4516 show_oops(NULL
, "movetab invalid parameters");
4517 return (XT_CB_PASSTHROUGH
);
4520 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4521 t
->tab_id
, args
->i
);
4523 if (args
->i
>= XT_TAB_INVALID
)
4524 return (XT_CB_PASSTHROUGH
);
4526 if (TAILQ_EMPTY(&tabs
))
4527 return (XT_CB_PASSTHROUGH
);
4529 n
= gtk_notebook_get_n_pages(notebook
);
4530 dest
= gtk_notebook_get_current_page(notebook
);
4534 if (args
->precount
< 0)
4535 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4537 dest
= args
->precount
- 1;
4541 if (args
->precount
< 0)
4544 dest
-= args
->precount
% n
;
4557 return (XT_CB_PASSTHROUGH
);
4560 if (dest
< 0 || dest
>= n
)
4561 return (XT_CB_PASSTHROUGH
);
4562 if (t
->tab_id
== dest
) {
4563 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4564 return (XT_CB_HANDLED
);
4567 set_current_tab(dest
);
4569 return (XT_CB_HANDLED
);
4575 command(struct tab
*t
, struct karg
*args
)
4577 char *s
= NULL
, *ss
= NULL
;
4581 if (t
== NULL
|| args
== NULL
) {
4582 show_oops(NULL
, "command invalid parameters");
4583 return (XT_CB_PASSTHROUGH
);
4594 if (cmd_prefix
== 0)
4597 ss
= g_strdup_printf(":%d", cmd_prefix
);
4608 case XT_CMD_OPEN_CURRENT
:
4611 case XT_CMD_TABNEW_CURRENT
:
4612 if (!s
) /* FALL THROUGH? */
4614 if ((uri
= get_uri(t
)) != NULL
) {
4615 ss
= g_strdup_printf("%s%s", s
, uri
);
4620 show_oops(t
, "command: invalid opcode %d", args
->i
);
4621 return (XT_CB_PASSTHROUGH
);
4624 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4626 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4627 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4628 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4630 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4631 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4636 return (XT_CB_HANDLED
);
4640 * Return a new string with a download row (in html)
4641 * appended. Old string is freed.
4644 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4647 WebKitDownloadStatus stat
;
4648 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4650 char cur_sz
[FMT_SCALED_STRSIZE
];
4651 char tot_sz
[FMT_SCALED_STRSIZE
];
4654 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4656 /* All actions wil take this form:
4657 * xxxt://class/seskey
4659 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4660 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4662 stat
= webkit_download_get_status(dl
->download
);
4665 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4666 status_html
= g_strdup_printf("Finished");
4667 cmd_html
= g_strdup_printf(
4668 "<a href='%s%d/%d'>Remove</a>",
4669 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4671 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4672 /* gather size info */
4673 progress
= 100 * webkit_download_get_progress(dl
->download
);
4676 webkit_download_get_current_size(dl
->download
), cur_sz
);
4678 webkit_download_get_total_size(dl
->download
), tot_sz
);
4680 status_html
= g_strdup_printf(
4681 "<div style='width: 100%%' align='center'>"
4682 "<div class='progress-outer'>"
4683 "<div class='progress-inner' style='width: %.2f%%'>"
4684 "</div></div></div>"
4685 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4686 progress
, cur_sz
, tot_sz
, progress
);
4688 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4689 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4693 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4694 status_html
= g_strdup_printf("Cancelled");
4695 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4696 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4698 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4699 status_html
= g_strdup_printf("Error!");
4700 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4701 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4703 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4704 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4705 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4706 status_html
= g_strdup_printf("Starting");
4709 show_oops(t
, "%s: unknown download status", __func__
);
4712 new_html
= g_strdup_printf(
4713 "%s\n<tr><td>%s</td><td>%s</td>"
4714 "<td style='text-align:center'>%s</td></tr>\n",
4715 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4716 status_html
, cmd_html
);
4720 g_free(status_html
);
4731 * update all download tabs apart from one. Pass NULL if
4732 * you want to update all.
4735 update_download_tabs(struct tab
*apart_from
)
4738 if (!updating_dl_tabs
) {
4739 updating_dl_tabs
= 1; /* stop infinite recursion */
4740 TAILQ_FOREACH(t
, &tabs
, entry
)
4741 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4742 && (t
!= apart_from
))
4743 xtp_page_dl(t
, NULL
);
4744 updating_dl_tabs
= 0;
4749 * update all cookie tabs apart from one. Pass NULL if
4750 * you want to update all.
4753 update_cookie_tabs(struct tab
*apart_from
)
4756 if (!updating_cl_tabs
) {
4757 updating_cl_tabs
= 1; /* stop infinite recursion */
4758 TAILQ_FOREACH(t
, &tabs
, entry
)
4759 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4760 && (t
!= apart_from
))
4761 xtp_page_cl(t
, NULL
);
4762 updating_cl_tabs
= 0;
4767 * update all history tabs apart from one. Pass NULL if
4768 * you want to update all.
4771 update_history_tabs(struct tab
*apart_from
)
4775 if (!updating_hl_tabs
) {
4776 updating_hl_tabs
= 1; /* stop infinite recursion */
4777 TAILQ_FOREACH(t
, &tabs
, entry
)
4778 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4779 && (t
!= apart_from
))
4780 xtp_page_hl(t
, NULL
);
4781 updating_hl_tabs
= 0;
4785 /* cookie management XTP page */
4787 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4789 char *body
, *page
, *tmp
;
4790 int i
= 1; /* all ids start 1 */
4791 GSList
*sc
, *pc
, *pc_start
;
4793 char *type
, *table_headers
, *last_domain
;
4795 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4798 show_oops(NULL
, "%s invalid parameters", __func__
);
4802 /* Generate a new session key */
4803 if (!updating_cl_tabs
)
4804 generate_xtp_session_key(&cl_session_key
);
4807 table_headers
= g_strdup_printf("<table><tr>"
4810 "<th style='width:200px'>Value</th>"
4814 "<th>HTTP<br />only</th>"
4815 "<th style='width:40px'>Rm</th></tr>\n");
4817 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4818 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4822 last_domain
= strdup("");
4823 for (; sc
; sc
= sc
->next
) {
4826 if (strcmp(last_domain
, c
->domain
) != 0) {
4829 last_domain
= strdup(c
->domain
);
4833 body
= g_strdup_printf("%s</table>"
4835 body
, c
->domain
, table_headers
);
4839 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4840 c
->domain
, table_headers
);
4845 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4846 if (soup_cookie_equal(pc
->data
, c
)) {
4847 type
= "Session + Persistent";
4852 body
= g_strdup_printf(
4855 "<td style='word-wrap:normal'>%s</td>"
4857 " <textarea rows='4'>%s</textarea>"
4863 "<td style='text-align:center'>"
4864 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4871 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4886 soup_cookies_free(sc
);
4887 soup_cookies_free(pc
);
4889 /* small message if there are none */
4891 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4892 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4895 body
= g_strdup_printf("%s</table>", body
);
4898 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4900 g_free(table_headers
);
4901 g_free(last_domain
);
4903 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4904 update_cookie_tabs(t
);
4912 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4914 char *body
, *page
, *tmp
;
4916 int i
= 1; /* all ids start 1 */
4918 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4921 show_oops(NULL
, "%s invalid parameters", __func__
);
4925 /* Generate a new session key */
4926 if (!updating_hl_tabs
)
4927 generate_xtp_session_key(&hl_session_key
);
4930 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4931 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4933 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4935 body
= g_strdup_printf(
4937 "<td><a href='%s'>%s</a></td>"
4939 "<td style='text-align: center'>"
4940 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4941 body
, h
->uri
, h
->uri
, h
->title
,
4942 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4943 XT_XTP_HL_REMOVE
, i
);
4949 /* small message if there are none */
4952 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4953 "colspan='3'>No History</td></tr>\n", body
);
4958 body
= g_strdup_printf("%s</table>", body
);
4961 page
= get_html_page("History", body
, "", TRUE
);
4965 * update all history manager tabs as the xtp session
4966 * key has now changed. No need to update the current tab.
4967 * Already did that above.
4969 update_history_tabs(t
);
4971 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4978 * Generate a web page detailing the status of any downloads
4981 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4983 struct download
*dl
;
4984 char *body
, *page
, *tmp
;
4988 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4991 show_oops(NULL
, "%s invalid parameters", __func__
);
4996 * Generate a new session key for next page instance.
4997 * This only happens for the top level call to xtp_page_dl()
4998 * in which case updating_dl_tabs is 0.
5000 if (!updating_dl_tabs
)
5001 generate_xtp_session_key(&dl_session_key
);
5003 /* header - with refresh so as to update */
5004 if (refresh_interval
>= 1)
5005 ref
= g_strdup_printf(
5006 "<meta http-equiv='refresh' content='%u"
5007 ";url=%s%d/%s/%d' />\n",
5016 body
= g_strdup_printf("<div align='center'>"
5017 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5018 "</p><table><tr><th style='width: 60%%'>"
5019 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5020 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5022 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5023 body
= xtp_page_dl_row(t
, body
, dl
);
5027 /* message if no downloads in list */
5030 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5031 " style='text-align: center'>"
5032 "No downloads</td></tr>\n", body
);
5037 body
= g_strdup_printf("%s</table></div>", body
);
5040 page
= get_html_page("Downloads", body
, ref
, 1);
5045 * update all download manager tabs as the xtp session
5046 * key has now changed. No need to update the current tab.
5047 * Already did that above.
5049 update_download_tabs(t
);
5051 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5058 search(struct tab
*t
, struct karg
*args
)
5062 if (t
== NULL
|| args
== NULL
) {
5063 show_oops(NULL
, "search invalid parameters");
5066 if (t
->search_text
== NULL
) {
5067 if (global_search
== NULL
)
5068 return (XT_CB_PASSTHROUGH
);
5070 t
->search_text
= g_strdup(global_search
);
5071 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5072 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5076 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5077 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5080 case XT_SEARCH_NEXT
:
5081 d
= t
->search_forward
;
5083 case XT_SEARCH_PREV
:
5084 d
= !t
->search_forward
;
5087 return (XT_CB_PASSTHROUGH
);
5090 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5092 return (XT_CB_HANDLED
);
5095 struct settings_args
{
5101 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5104 struct settings_args
*sa
= cb_args
;
5109 if (s
->flags
& XT_SF_RUNTIME
)
5115 *sa
->body
= g_strdup_printf(
5117 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5118 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5130 set_show(struct tab
*t
, struct karg
*args
)
5132 char *body
, *page
, *tmp
;
5134 struct settings_args sa
;
5136 bzero(&sa
, sizeof sa
);
5140 body
= g_strdup_printf("<div align='center'><table><tr>"
5141 "<th align='left'>Setting</th>"
5142 "<th align='left'>Value</th></tr>\n");
5144 settings_walk(print_setting
, &sa
);
5147 /* small message if there are none */
5150 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5151 "colspan='2'>No settings</td></tr>\n", body
);
5156 body
= g_strdup_printf("%s</table></div>", body
);
5159 page
= get_html_page("Settings", body
, "", 0);
5163 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5167 return (XT_CB_PASSTHROUGH
);
5171 set(struct tab
*t
, struct karg
*args
)
5176 if (args
== NULL
|| args
->s
== NULL
)
5177 return (set_show(t
, args
));
5180 p
= g_strstrip(args
->s
);
5183 return (set_show(t
, args
));
5185 /* we got some sort of string */
5186 val
= g_strrstr(p
, "=");
5189 val
= g_strchomp(val
);
5192 for (i
= 0; i
< LENGTH(rs
); i
++) {
5193 if (strcmp(rs
[i
].name
, p
))
5196 if (rs
[i
].activate
) {
5197 if (rs
[i
].activate(val
))
5198 show_oops(t
, "%s invalid value %s",
5201 show_oops(t
, ":set %s = %s", p
, val
);
5204 show_oops(t
, "not a runtime option: %s", p
);
5208 show_oops(t
, "unknown option: %s", p
);
5212 for (i
= 0; i
< LENGTH(rs
); i
++) {
5213 if (strcmp(rs
[i
].name
, p
))
5216 /* XXX this could use some cleanup */
5217 switch (rs
[i
].type
) {
5220 show_oops(t
, "%s = %d",
5221 rs
[i
].name
, *rs
[i
].ival
);
5222 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5223 show_oops(t
, "%s = %s",
5225 rs
[i
].s
->get(&rs
[i
]));
5226 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5227 show_oops(t
, "%s = ...", rs
[i
].name
);
5229 show_oops(t
, "%s = ", rs
[i
].name
);
5233 show_oops(t
, "%s = %f",
5234 rs
[i
].name
, *rs
[i
].fval
);
5235 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5236 show_oops(t
, "%s = %s",
5238 rs
[i
].s
->get(&rs
[i
]));
5239 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5240 show_oops(t
, "%s = ...", rs
[i
].name
);
5242 show_oops(t
, "%s = ", rs
[i
].name
);
5245 if (rs
[i
].sval
&& *rs
[i
].sval
)
5246 show_oops(t
, "%s = %s",
5247 rs
[i
].name
, *rs
[i
].sval
);
5248 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5249 show_oops(t
, "%s = %s",
5251 rs
[i
].s
->get(&rs
[i
]));
5252 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5253 show_oops(t
, "%s = ...", rs
[i
].name
);
5255 show_oops(t
, "%s = ", rs
[i
].name
);
5258 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5264 show_oops(t
, "unknown option: %s", p
);
5267 return (XT_CB_PASSTHROUGH
);
5271 session_save(struct tab
*t
, char *filename
)
5277 if (strlen(filename
) == 0)
5280 if (filename
[0] == '.' || filename
[0] == '/')
5284 if (save_tabs(t
, &a
))
5286 strlcpy(named_session
, filename
, sizeof named_session
);
5288 /* add the new session to the list of sessions */
5289 s
= g_malloc(sizeof(struct session
));
5290 s
->name
= g_strdup(filename
);
5291 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5299 session_open(struct tab
*t
, char *filename
)
5304 if (strlen(filename
) == 0)
5307 if (filename
[0] == '.' || filename
[0] == '/')
5311 a
.i
= XT_SES_CLOSETABS
;
5312 if (open_tabs(t
, &a
))
5315 strlcpy(named_session
, filename
, sizeof named_session
);
5323 session_delete(struct tab
*t
, char *filename
)
5325 char file
[PATH_MAX
];
5329 if (strlen(filename
) == 0)
5332 if (filename
[0] == '.' || filename
[0] == '/')
5335 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5339 if (!strcmp(filename
, named_session
))
5340 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5341 sizeof named_session
);
5343 /* remove session from sessions list */
5344 TAILQ_FOREACH(s
, &sessions
, entry
) {
5345 if (!strcmp(s
->name
, filename
))
5348 TAILQ_REMOVE(&sessions
, s
, entry
);
5349 g_free((gpointer
) s
->name
);
5358 session_cmd(struct tab
*t
, struct karg
*args
)
5360 char *filename
= args
->s
;
5365 if (args
->i
& XT_SHOW
)
5366 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5367 XT_SAVED_TABS_FILE
: named_session
);
5368 else if (args
->i
& XT_SAVE
) {
5369 if (session_save(t
, filename
)) {
5370 show_oops(t
, "Can't save session: %s",
5371 filename
? filename
: "INVALID");
5374 } else if (args
->i
& XT_OPEN
) {
5375 if (session_open(t
, filename
)) {
5376 show_oops(t
, "Can't open session: %s",
5377 filename
? filename
: "INVALID");
5380 } else if (args
->i
& XT_DELETE
) {
5381 if (session_delete(t
, filename
)) {
5382 show_oops(t
, "Can't delete session: %s",
5383 filename
? filename
: "INVALID");
5388 return (XT_CB_PASSTHROUGH
);
5392 * Make a hardcopy of the page
5395 print_page(struct tab
*t
, struct karg
*args
)
5397 WebKitWebFrame
*frame
;
5399 GtkPrintOperation
*op
;
5400 GtkPrintOperationAction action
;
5401 GtkPrintOperationResult print_res
;
5402 GError
*g_err
= NULL
;
5403 int marg_l
, marg_r
, marg_t
, marg_b
;
5405 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5407 ps
= gtk_page_setup_new();
5408 op
= gtk_print_operation_new();
5409 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5410 frame
= webkit_web_view_get_main_frame(t
->wv
);
5412 /* the default margins are too small, so we will bump them */
5413 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5414 XT_PRINT_EXTRA_MARGIN
;
5415 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5416 XT_PRINT_EXTRA_MARGIN
;
5417 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5418 XT_PRINT_EXTRA_MARGIN
;
5419 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5420 XT_PRINT_EXTRA_MARGIN
;
5423 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5424 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5425 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5426 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5428 gtk_print_operation_set_default_page_setup(op
, ps
);
5430 /* this appears to free 'op' and 'ps' */
5431 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5433 /* check it worked */
5434 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5435 show_oops(NULL
, "can't print: %s", g_err
->message
);
5436 g_error_free (g_err
);
5444 go_home(struct tab
*t
, struct karg
*args
)
5451 restart(struct tab
*t
, struct karg
*args
)
5455 a
.s
= XT_RESTART_TABS_FILE
;
5457 execvp(start_argv
[0], start_argv
);
5463 #define CTRL GDK_CONTROL_MASK
5464 #define MOD1 GDK_MOD1_MASK
5465 #define SHFT GDK_SHIFT_MASK
5467 /* inherent to GTK not all keys will be caught at all times */
5468 /* XXX sort key bindings */
5469 struct key_binding
{
5474 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5476 { "cookiejar", MOD1
, 0, GDK_j
},
5477 { "downloadmgr", MOD1
, 0, GDK_d
},
5478 { "history", MOD1
, 0, GDK_h
},
5479 { "print", CTRL
, 0, GDK_p
},
5480 { "search", 0, 0, GDK_slash
},
5481 { "searchb", 0, 0, GDK_question
},
5482 { "statustoggle", CTRL
, 0, GDK_n
},
5483 { "command", 0, 0, GDK_colon
},
5484 { "qa", CTRL
, 0, GDK_q
},
5485 { "restart", MOD1
, 0, GDK_q
},
5486 { "js toggle", CTRL
, 0, GDK_j
},
5487 { "cookie toggle", MOD1
, 0, GDK_c
},
5488 { "togglesrc", CTRL
, 0, GDK_s
},
5489 { "yankuri", 0, 0, GDK_y
},
5490 { "pasteuricur", 0, 0, GDK_p
},
5491 { "pasteurinew", 0, 0, GDK_P
},
5492 { "toplevel toggle", 0, 0, GDK_F4
},
5493 { "help", 0, 0, GDK_F1
},
5494 { "run_script", MOD1
, 0, GDK_r
},
5497 { "searchnext", 0, 0, GDK_n
},
5498 { "searchprevious", 0, 0, GDK_N
},
5501 { "focusaddress", 0, 0, GDK_F6
},
5502 { "focussearch", 0, 0, GDK_F7
},
5505 { "hinting", 0, 0, GDK_f
},
5507 /* custom stylesheet */
5508 { "userstyle", 0, 0, GDK_i
},
5511 { "goback", 0, 0, GDK_BackSpace
},
5512 { "goback", MOD1
, 0, GDK_Left
},
5513 { "goforward", SHFT
, 0, GDK_BackSpace
},
5514 { "goforward", MOD1
, 0, GDK_Right
},
5515 { "reload", 0, 0, GDK_F5
},
5516 { "reload", CTRL
, 0, GDK_r
},
5517 { "reload", CTRL
, 0, GDK_l
},
5518 { "favorites", MOD1
, 1, GDK_f
},
5520 /* vertical movement */
5521 { "scrolldown", 0, 0, GDK_j
},
5522 { "scrolldown", 0, 0, GDK_Down
},
5523 { "scrollup", 0, 0, GDK_Up
},
5524 { "scrollup", 0, 0, GDK_k
},
5525 { "scrollbottom", 0, 0, GDK_G
},
5526 { "scrollbottom", 0, 0, GDK_End
},
5527 { "scrolltop", 0, 0, GDK_Home
},
5528 { "scrollpagedown", 0, 0, GDK_space
},
5529 { "scrollpagedown", CTRL
, 0, GDK_f
},
5530 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5531 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5532 { "scrollpageup", 0, 0, GDK_Page_Up
},
5533 { "scrollpageup", CTRL
, 0, GDK_b
},
5534 { "scrollhalfup", CTRL
, 0, GDK_u
},
5535 /* horizontal movement */
5536 { "scrollright", 0, 0, GDK_l
},
5537 { "scrollright", 0, 0, GDK_Right
},
5538 { "scrollleft", 0, 0, GDK_Left
},
5539 { "scrollleft", 0, 0, GDK_h
},
5540 { "scrollfarright", 0, 0, GDK_dollar
},
5541 { "scrollfarleft", 0, 0, GDK_0
},
5544 { "tabnew", CTRL
, 0, GDK_t
},
5545 { "999tabnew", CTRL
, 0, GDK_T
},
5546 { "tabclose", CTRL
, 1, GDK_w
},
5547 { "tabundoclose", 0, 0, GDK_U
},
5548 { "tabnext 1", CTRL
, 0, GDK_1
},
5549 { "tabnext 2", CTRL
, 0, GDK_2
},
5550 { "tabnext 3", CTRL
, 0, GDK_3
},
5551 { "tabnext 4", CTRL
, 0, GDK_4
},
5552 { "tabnext 5", CTRL
, 0, GDK_5
},
5553 { "tabnext 6", CTRL
, 0, GDK_6
},
5554 { "tabnext 7", CTRL
, 0, GDK_7
},
5555 { "tabnext 8", CTRL
, 0, GDK_8
},
5556 { "tabnext 9", CTRL
, 0, GDK_9
},
5557 { "tabfirst", CTRL
, 0, GDK_less
},
5558 { "tablast", CTRL
, 0, GDK_greater
},
5559 { "tabprevious", CTRL
, 0, GDK_Left
},
5560 { "tabnext", CTRL
, 0, GDK_Right
},
5561 { "focusout", CTRL
, 0, GDK_minus
},
5562 { "focusin", CTRL
, 0, GDK_plus
},
5563 { "focusin", CTRL
, 0, GDK_equal
},
5564 { "focusreset", CTRL
, 0, GDK_0
},
5566 /* command aliases (handy when -S flag is used) */
5567 { "promptopen", 0, 0, GDK_F9
},
5568 { "promptopencurrent", 0, 0, GDK_F10
},
5569 { "prompttabnew", 0, 0, GDK_F11
},
5570 { "prompttabnewcurrent",0, 0, GDK_F12
},
5572 TAILQ_HEAD(keybinding_list
, key_binding
);
5575 walk_kb(struct settings
*s
,
5576 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5578 struct key_binding
*k
;
5581 if (s
== NULL
|| cb
== NULL
) {
5582 show_oops(NULL
, "walk_kb invalid parameters");
5586 TAILQ_FOREACH(k
, &kbl
, entry
) {
5592 if (gdk_keyval_name(k
->key
) == NULL
)
5595 strlcat(str
, k
->cmd
, sizeof str
);
5596 strlcat(str
, ",", sizeof str
);
5598 if (k
->mask
& GDK_SHIFT_MASK
)
5599 strlcat(str
, "S-", sizeof str
);
5600 if (k
->mask
& GDK_CONTROL_MASK
)
5601 strlcat(str
, "C-", sizeof str
);
5602 if (k
->mask
& GDK_MOD1_MASK
)
5603 strlcat(str
, "M1-", sizeof str
);
5604 if (k
->mask
& GDK_MOD2_MASK
)
5605 strlcat(str
, "M2-", sizeof str
);
5606 if (k
->mask
& GDK_MOD3_MASK
)
5607 strlcat(str
, "M3-", sizeof str
);
5608 if (k
->mask
& GDK_MOD4_MASK
)
5609 strlcat(str
, "M4-", sizeof str
);
5610 if (k
->mask
& GDK_MOD5_MASK
)
5611 strlcat(str
, "M5-", sizeof str
);
5613 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5614 cb(s
, str
, cb_args
);
5619 init_keybindings(void)
5622 struct key_binding
*k
;
5624 for (i
= 0; i
< LENGTH(keys
); i
++) {
5625 k
= g_malloc0(sizeof *k
);
5626 k
->cmd
= keys
[i
].cmd
;
5627 k
->mask
= keys
[i
].mask
;
5628 k
->use_in_entry
= keys
[i
].use_in_entry
;
5629 k
->key
= keys
[i
].key
;
5630 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5632 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5633 k
->cmd
? k
->cmd
: "unnamed key");
5638 keybinding_clearall(void)
5640 struct key_binding
*k
, *next
;
5642 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5643 next
= TAILQ_NEXT(k
, entry
);
5647 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5648 k
->cmd
? k
->cmd
: "unnamed key");
5649 TAILQ_REMOVE(&kbl
, k
, entry
);
5655 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5657 struct key_binding
*k
;
5658 guint keyval
, mask
= 0;
5661 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5663 /* Keys which are to be used in entry have been prefixed with an
5664 * exclamation mark. */
5668 /* find modifier keys */
5669 if (strstr(key
, "S-"))
5670 mask
|= GDK_SHIFT_MASK
;
5671 if (strstr(key
, "C-"))
5672 mask
|= GDK_CONTROL_MASK
;
5673 if (strstr(key
, "M1-"))
5674 mask
|= GDK_MOD1_MASK
;
5675 if (strstr(key
, "M2-"))
5676 mask
|= GDK_MOD2_MASK
;
5677 if (strstr(key
, "M3-"))
5678 mask
|= GDK_MOD3_MASK
;
5679 if (strstr(key
, "M4-"))
5680 mask
|= GDK_MOD4_MASK
;
5681 if (strstr(key
, "M5-"))
5682 mask
|= GDK_MOD5_MASK
;
5685 for (i
= strlen(key
) - 1; i
> 0; i
--)
5689 /* validate keyname */
5690 keyval
= gdk_keyval_from_name(key
);
5691 if (keyval
== GDK_VoidSymbol
) {
5692 warnx("invalid keybinding name %s", key
);
5695 /* must run this test too, gtk+ doesn't handle 10 for example */
5696 if (gdk_keyval_name(keyval
) == NULL
) {
5697 warnx("invalid keybinding name %s", key
);
5701 /* Remove eventual dupes. */
5702 TAILQ_FOREACH(k
, &kbl
, entry
)
5703 if (k
->key
== keyval
&& k
->mask
== mask
) {
5704 TAILQ_REMOVE(&kbl
, k
, entry
);
5710 k
= g_malloc0(sizeof *k
);
5711 k
->cmd
= g_strdup(cmd
);
5713 k
->use_in_entry
= use_in_entry
;
5716 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5721 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5722 k
->cmd
, gdk_keyval_name(keyval
));
5724 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5730 add_kb(struct settings
*s
, char *entry
)
5734 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5736 /* clearall is special */
5737 if (!strcmp(entry
, "clearall")) {
5738 keybinding_clearall();
5742 kb
= strstr(entry
, ",");
5748 return (keybinding_add(entry
, key
, key
[0] == '!'));
5754 int (*func
)(struct tab
*, struct karg
*);
5758 { "command", 0, command
, ':', 0 },
5759 { "search", 0, command
, '/', 0 },
5760 { "searchb", 0, command
, '?', 0 },
5761 { "togglesrc", 0, toggle_src
, 0, 0 },
5763 /* yanking and pasting */
5764 { "yankuri", 0, yank_uri
, 0, 0 },
5765 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5766 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5767 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5770 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5771 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5774 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5775 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5778 { "hinting", 0, hint
, 0, 0 },
5780 /* custom stylesheet */
5781 { "userstyle", 0, userstyle
, 0, 0 },
5784 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5785 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5786 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5788 /* vertical movement */
5789 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5790 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5791 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5792 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5793 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5794 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5795 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5796 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5797 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5798 /* horizontal movement */
5799 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5800 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5801 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5802 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5804 { "favorites", 0, xtp_page_fl
, 0, 0 },
5805 { "fav", 0, xtp_page_fl
, 0, 0 },
5806 { "favadd", 0, add_favorite
, 0, 0 },
5808 { "qall", 0, quit
, 0, 0 },
5809 { "quitall", 0, quit
, 0, 0 },
5810 { "w", 0, save_tabs
, 0, 0 },
5811 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5812 { "help", 0, help
, 0, 0 },
5813 { "about", 0, about
, 0, 0 },
5814 { "stats", 0, stats
, 0, 0 },
5815 { "version", 0, about
, 0, 0 },
5818 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5819 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5820 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5821 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5822 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5823 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5824 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5825 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5826 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5827 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5828 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5830 /* cookie command */
5831 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5832 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5833 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5834 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5835 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5836 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5837 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5838 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5839 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5840 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5841 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5843 /* toplevel (domain) command */
5844 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5845 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5848 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5851 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5852 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5853 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5855 { "ca", 0, ca_cmd
, 0, 0 },
5856 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5857 { "dl", 0, xtp_page_dl
, 0, 0 },
5858 { "h", 0, xtp_page_hl
, 0, 0 },
5859 { "history", 0, xtp_page_hl
, 0, 0 },
5860 { "home", 0, go_home
, 0, 0 },
5861 { "restart", 0, restart
, 0, 0 },
5862 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5863 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5864 { "statustoggle", 0, statustoggle
, 0, 0 },
5865 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5867 { "print", 0, print_page
, 0, 0 },
5870 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5871 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5872 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5873 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5874 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5875 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5876 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5877 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5878 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5879 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5880 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5881 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5882 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5883 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5884 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5885 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5886 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5887 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5888 { "buffers", 0, buffers
, 0, 0 },
5889 { "ls", 0, buffers
, 0, 0 },
5890 { "tabs", 0, buffers
, 0, 0 },
5892 /* command aliases (handy when -S flag is used) */
5893 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5894 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5895 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5896 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5899 { "set", 0, set
, 0, XT_SETARG
},
5901 { "fullscreen", 0, fullscreen
, 0, 0 },
5902 { "f", 0, fullscreen
, 0, 0 },
5905 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5906 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
5907 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
5908 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5909 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5916 } cmd_status
= {-1, 0};
5919 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5922 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5929 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5936 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5938 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5944 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5946 a
.i
= XT_NAV_FORWARD
;
5956 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5958 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5960 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5967 * cancel, remove, etc. downloads
5970 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5972 struct download find
, *d
= NULL
;
5974 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5976 /* some commands require a valid download id */
5977 if (cmd
!= XT_XTP_DL_LIST
) {
5978 /* lookup download in question */
5980 d
= RB_FIND(download_list
, &downloads
, &find
);
5983 show_oops(t
, "%s: no such download", __func__
);
5988 /* decide what to do */
5990 case XT_XTP_DL_CANCEL
:
5991 webkit_download_cancel(d
->download
);
5993 case XT_XTP_DL_REMOVE
:
5994 webkit_download_cancel(d
->download
); /* just incase */
5995 g_object_unref(d
->download
);
5996 RB_REMOVE(download_list
, &downloads
, d
);
5998 case XT_XTP_DL_LIST
:
6002 show_oops(t
, "%s: unknown command", __func__
);
6005 xtp_page_dl(t
, NULL
);
6009 * Actions on history, only does one thing for now, but
6010 * we provide the function for future actions
6013 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6015 struct history
*h
, *next
;
6019 case XT_XTP_HL_REMOVE
:
6020 /* walk backwards, as listed in reverse */
6021 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6022 next
= RB_PREV(history_list
, &hl
, h
);
6024 RB_REMOVE(history_list
, &hl
, h
);
6025 g_free((gpointer
) h
->title
);
6026 g_free((gpointer
) h
->uri
);
6033 case XT_XTP_HL_LIST
:
6034 /* Nothing - just xtp_page_hl() below */
6037 show_oops(t
, "%s: unknown command", __func__
);
6041 xtp_page_hl(t
, NULL
);
6044 /* remove a favorite */
6046 remove_favorite(struct tab
*t
, int index
)
6048 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6049 char *new_favs
, *tmp
;
6054 /* open favorites */
6055 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6057 if ((f
= fopen(file
, "r")) == NULL
) {
6058 show_oops(t
, "%s: can't open favorites: %s",
6059 __func__
, strerror(errno
));
6063 /* build a string which will become the new favroites file */
6064 new_favs
= g_strdup("");
6067 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6068 if (feof(f
) || ferror(f
))
6070 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6077 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6078 if (feof(f
) || ferror(f
)) {
6079 show_oops(t
, "%s: can't parse favorites %s",
6080 __func__
, strerror(errno
));
6085 /* as long as this isn't the one we are deleting add to file */
6088 new_favs
= g_strdup_printf("%s%s\n%s\n",
6089 new_favs
, title
, uri
);
6101 /* write back new favorites file */
6102 if ((f
= fopen(file
, "w")) == NULL
) {
6103 show_oops(t
, "%s: can't open favorites: %s",
6104 __func__
, strerror(errno
));
6108 fwrite(new_favs
, strlen(new_favs
), 1, f
);
6121 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6124 case XT_XTP_FL_LIST
:
6125 /* nothing, just the below call to xtp_page_fl() */
6127 case XT_XTP_FL_REMOVE
:
6128 remove_favorite(t
, arg
);
6131 show_oops(t
, "%s: invalid favorites command", __func__
);
6135 xtp_page_fl(t
, NULL
);
6139 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6142 case XT_XTP_CL_LIST
:
6143 /* nothing, just xtp_page_cl() */
6145 case XT_XTP_CL_REMOVE
:
6149 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6153 xtp_page_cl(t
, NULL
);
6156 /* link an XTP class to it's session key and handler function */
6157 struct xtp_despatch
{
6160 void (*handle_func
)(struct tab
*, uint8_t, int);
6163 struct xtp_despatch xtp_despatches
[] = {
6164 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6165 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6166 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6167 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6168 { XT_XTP_INVALID
, NULL
, NULL
}
6172 * is the url xtp protocol? (xxxt://)
6173 * if so, parse and despatch correct bahvior
6176 parse_xtp_url(struct tab
*t
, const char *url
)
6178 char *dup
= NULL
, *p
, *last
;
6179 uint8_t n_tokens
= 0;
6180 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6181 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6186 * tokens array meaning:
6188 * tokens[1] = session key
6189 * tokens[2] = action
6190 * tokens[3] = optional argument
6193 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6195 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6198 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6200 /* split out the url */
6201 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6202 (p
= strtok_r(NULL
, "/", &last
))) {
6204 tokens
[n_tokens
++] = p
;
6207 /* should be atleast three fields 'class/seskey/command/arg' */
6211 dsp
= xtp_despatches
;
6212 req_class
= atoi(tokens
[0]);
6213 while (dsp
->xtp_class
) {
6214 if (dsp
->xtp_class
== req_class
) {
6221 /* did we find one atall? */
6222 if (dsp_match
== NULL
) {
6223 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6227 /* check session key and call despatch function */
6228 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6229 ret
= TRUE
; /* all is well, this was a valid xtp request */
6230 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6243 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6245 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6247 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6250 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6255 show_oops(t
, "activate_uri_entry_cb no uri");
6259 uri
+= strspn(uri
, "\t ");
6261 /* if xxxt:// treat specially */
6262 if (parse_xtp_url(t
, uri
))
6265 /* otherwise continue to load page normally */
6266 load_uri(t
, (gchar
*)uri
);
6271 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6273 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6274 char *newuri
= NULL
;
6277 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6280 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6284 if (search_string
== NULL
) {
6285 show_oops(t
, "no search_string");
6289 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6291 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6292 newuri
= g_strdup_printf(search_string
, enc_search
);
6296 webkit_web_view_load_uri(t
->wv
, newuri
);
6304 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6306 struct domain
*d
= NULL
;
6309 if (uri
== NULL
|| t
== NULL
)
6312 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6317 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6318 es
? "enable" : "disable", uri
);
6320 g_object_set(G_OBJECT(t
->settings
),
6321 "enable-html5-local-storage", es
, (char *)NULL
);
6322 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6326 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6328 struct domain
*d
= NULL
;
6331 if (uri
== NULL
|| t
== NULL
)
6334 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6339 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6340 es
? "enable" : "disable", uri
);
6342 g_object_set(G_OBJECT(t
->settings
),
6343 "enable-scripts", es
, (char *)NULL
);
6344 g_object_set(G_OBJECT(t
->settings
),
6345 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6346 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6348 button_set_stockid(t
->js_toggle
,
6349 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6353 color_address_bar(gpointer p
)
6356 struct tab
*tt
, *t
= p
;
6357 gchar
*col_str
= XT_COLOR_YELLOW
;
6359 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6361 /* make sure t still exists */
6364 TAILQ_FOREACH(tt
, &tabs
, entry
)
6370 switch (load_compare_cert(t
, NULL
)) {
6372 col_str
= XT_COLOR_BLUE
;
6375 col_str
= XT_COLOR_GREEN
;
6377 case CERT_UNTRUSTED
:
6378 col_str
= XT_COLOR_YELLOW
;
6381 col_str
= XT_COLOR_RED
;
6385 gdk_color_parse(col_str
, &color
);
6386 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6388 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6389 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6391 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6399 show_ca_status(struct tab
*t
, const char *uri
)
6402 gchar
*col_str
= XT_COLOR_WHITE
;
6404 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6405 ssl_strict_certs
, ssl_ca_file
, uri
);
6412 if (ssl_ca_file
== NULL
) {
6413 if (g_str_has_prefix(uri
, "http://"))
6415 if (g_str_has_prefix(uri
, "https://")) {
6416 col_str
= XT_COLOR_RED
;
6421 if (g_str_has_prefix(uri
, "http://") ||
6422 !g_str_has_prefix(uri
, "https://"))
6425 color_address_bar(t
);
6431 gdk_color_parse(col_str
, &color
);
6432 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6434 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6435 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6437 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6442 free_favicon(struct tab
*t
)
6444 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6445 __func__
, t
->icon_download
, t
->icon_request
);
6447 if (t
->icon_request
)
6448 g_object_unref(t
->icon_request
);
6449 if (t
->icon_dest_uri
)
6450 g_free(t
->icon_dest_uri
);
6452 t
->icon_request
= NULL
;
6453 t
->icon_dest_uri
= NULL
;
6457 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6459 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6460 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6462 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6463 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6465 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6466 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6470 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6472 GdkPixbuf
*pb_scaled
;
6474 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6475 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6476 GDK_INTERP_BILINEAR
);
6480 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6481 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6483 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6484 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6486 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6487 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6489 if (pb_scaled
!= pb
)
6490 g_object_unref(pb_scaled
);
6494 xt_icon_from_file(struct tab
*t
, char *file
)
6498 if (g_str_has_prefix(file
, "file://"))
6499 file
+= strlen("file://");
6501 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6503 xt_icon_from_pixbuf(t
, pb
);
6506 xt_icon_from_name(t
, "text-html");
6510 is_valid_icon(char *file
)
6513 const char *mime_type
;
6517 gf
= g_file_new_for_path(file
);
6518 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6520 mime_type
= g_file_info_get_content_type(fi
);
6521 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6522 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6523 g_strcmp0(mime_type
, "image/png") == 0 ||
6524 g_strcmp0(mime_type
, "image/gif") == 0 ||
6525 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6533 set_favicon_from_file(struct tab
*t
, char *file
)
6537 if (t
== NULL
|| file
== NULL
)
6540 if (g_str_has_prefix(file
, "file://"))
6541 file
+= strlen("file://");
6542 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6544 if (!stat(file
, &sb
)) {
6545 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6546 /* corrupt icon so trash it */
6547 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6550 /* no need to set icon to default here */
6554 xt_icon_from_file(t
, file
);
6558 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6561 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6562 struct tab
*tt
= NULL
, *t
= NULL
;
6565 * find the webview instead of passing in the tab as it could have been
6566 * deleted from underneath us.
6568 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6577 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6578 __func__
, t
->tab_id
, status
);
6581 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6583 t
->icon_download
= NULL
;
6586 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6589 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6592 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6594 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6595 __func__
, t
->tab_id
);
6596 t
->icon_download
= NULL
;
6599 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6602 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6603 __func__
, t
->icon_dest_uri
);
6604 set_favicon_from_file(t
, t
->icon_dest_uri
);
6605 /* these will be freed post callback */
6606 t
->icon_request
= NULL
;
6607 t
->icon_download
= NULL
;
6615 abort_favicon_download(struct tab
*t
)
6617 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6619 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6620 if (t
->icon_download
) {
6621 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6622 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6623 webkit_download_cancel(t
->icon_download
);
6624 t
->icon_download
= NULL
;
6629 xt_icon_from_name(t
, "text-html");
6633 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6635 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6637 if (uri
== NULL
|| t
== NULL
)
6640 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6641 /* take icon from WebKitIconDatabase */
6644 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6646 xt_icon_from_pixbuf(t
, pb
);
6649 xt_icon_from_name(t
, "text-html");
6650 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6651 /* download icon to cache dir */
6652 gchar
*name_hash
, file
[PATH_MAX
];
6655 if (t
->icon_request
) {
6656 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6660 /* check to see if we got the icon in cache */
6661 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6662 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6665 if (!stat(file
, &sb
)) {
6666 if (sb
.st_size
> 0) {
6667 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6669 set_favicon_from_file(t
, file
);
6673 /* corrupt icon so trash it */
6674 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6679 /* create download for icon */
6680 t
->icon_request
= webkit_network_request_new(uri
);
6681 if (t
->icon_request
== NULL
) {
6682 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6687 t
->icon_download
= webkit_download_new(t
->icon_request
);
6688 if (t
->icon_download
== NULL
)
6691 /* we have to free icon_dest_uri later */
6692 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6693 webkit_download_set_destination_uri(t
->icon_download
,
6696 if (webkit_download_get_status(t
->icon_download
) ==
6697 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6698 g_object_unref(t
->icon_request
);
6699 g_free(t
->icon_dest_uri
);
6700 t
->icon_request
= NULL
;
6701 t
->icon_dest_uri
= NULL
;
6705 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6706 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6708 webkit_download_start(t
->icon_download
);
6713 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6715 const gchar
*uri
= NULL
, *title
= NULL
;
6716 struct history
*h
, find
;
6720 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6721 webkit_web_view_get_load_status(wview
),
6722 get_uri(t
) ? get_uri(t
) : "NOTHING");
6725 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6729 switch (webkit_web_view_get_load_status(wview
)) {
6730 case WEBKIT_LOAD_PROVISIONAL
:
6732 abort_favicon_download(t
);
6733 #if GTK_CHECK_VERSION(2, 20, 0)
6734 gtk_widget_show(t
->spinner
);
6735 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6737 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6739 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6741 /* assume we are a new address */
6742 gdk_color_parse("white", &color
);
6743 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6744 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6746 /* take focus if we are visible */
6752 case WEBKIT_LOAD_COMMITTED
:
6757 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6763 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6765 /* check if js white listing is enabled */
6766 if (enable_cookie_whitelist
)
6767 check_and_set_cookie(uri
, t
);
6768 if (enable_js_whitelist
)
6769 check_and_set_js(uri
, t
);
6775 /* we know enough to autosave the session */
6776 if (session_autosave
) {
6781 show_ca_status(t
, uri
);
6784 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6788 case WEBKIT_LOAD_FINISHED
:
6794 if (!strncmp(uri
, "http://", strlen("http://")) ||
6795 !strncmp(uri
, "https://", strlen("https://")) ||
6796 !strncmp(uri
, "file://", strlen("file://"))) {
6798 h
= RB_FIND(history_list
, &hl
, &find
);
6800 title
= get_title(t
, FALSE
);
6801 h
= g_malloc(sizeof *h
);
6802 h
->uri
= g_strdup(uri
);
6803 h
->title
= g_strdup(title
);
6804 RB_INSERT(history_list
, &hl
, h
);
6805 completion_add_uri(h
->uri
);
6806 update_history_tabs(NULL
);
6810 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6811 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6812 case WEBKIT_LOAD_FAILED
:
6815 #if GTK_CHECK_VERSION(2, 20, 0)
6816 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6817 gtk_widget_hide(t
->spinner
);
6820 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6825 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6827 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6828 can_go_back_for_real(t
));
6830 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6831 can_go_forward_for_real(t
));
6836 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6837 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6840 * XXX this function is wrong
6841 * it overwrites perfectly good urls with garbage on load errors
6842 * those happen often when popups fail to resolve dns
6846 t
->tmp_uri
= g_strdup(uri
);
6847 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6848 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6849 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6850 set_status(t
, uri
, XT_STATUS_NOTHING
);
6857 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6859 const gchar
*title
= NULL
, *win_title
= NULL
;
6861 title
= get_title(t
, FALSE
);
6862 win_title
= get_title(t
, TRUE
);
6863 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6864 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6865 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6866 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6870 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6872 run_script(t
, JS_HINTING
);
6876 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6878 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6879 progress
== 100 ? 0 : (double)progress
/ 100);
6880 if (show_url
== 0) {
6881 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6882 progress
== 100 ? 0 : (double)progress
/ 100);
6885 update_statusbar_position(NULL
, NULL
);
6889 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6890 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6891 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6894 WebKitWebNavigationReason reason
;
6895 struct domain
*d
= NULL
;
6898 show_oops(NULL
, "webview_npd_cb invalid parameters");
6902 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6904 webkit_network_request_get_uri(request
));
6906 uri
= (char *)webkit_network_request_get_uri(request
);
6908 /* if this is an xtp url, we don't load anything else */
6909 if (parse_xtp_url(t
, uri
))
6912 if (t
->ctrl_click
) {
6914 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6915 webkit_web_policy_decision_ignore(pd
);
6916 return (TRUE
); /* we made the decission */
6920 * This is a little hairy but it comes down to this:
6921 * when we run in whitelist mode we have to assist the browser in
6922 * opening the URL that it would have opened in a new tab.
6924 reason
= webkit_web_navigation_action_get_reason(na
);
6925 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6926 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6927 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6928 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6930 webkit_web_policy_decision_use(pd
);
6931 return (TRUE
); /* we made the decision */
6938 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6941 struct domain
*d
= NULL
;
6943 WebKitWebView
*webview
= NULL
;
6945 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6946 webkit_web_view_get_uri(wv
));
6949 /* open in current tab */
6951 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6952 uri
= webkit_web_view_get_uri(wv
);
6953 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6956 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6958 } else if (enable_scripts
== 1) {
6959 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6967 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6970 struct domain
*d
= NULL
;
6972 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6974 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6975 uri
= webkit_web_view_get_uri(wv
);
6976 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6980 } else if (enable_scripts
== 1)
6987 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6989 /* we can not eat the event without throwing gtk off so defer it */
6991 /* catch middle click */
6992 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6997 /* catch ctrl click */
6998 if (e
->type
== GDK_BUTTON_RELEASE
&&
6999 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7004 return (XT_CB_PASSTHROUGH
);
7008 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7010 struct mime_type
*m
;
7012 m
= find_mime_type(mime_type
);
7020 show_oops(t
, "can't fork mime handler");
7030 execlp(m
->mt_action
, m
->mt_action
,
7031 webkit_network_request_get_uri(request
), (void *)NULL
);
7040 get_mime_type(char *file
)
7042 const char *mime_type
;
7046 if (g_str_has_prefix(file
, "file://"))
7047 file
+= strlen("file://");
7049 gf
= g_file_new_for_path(file
);
7050 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7052 mime_type
= g_file_info_get_content_type(fi
);
7060 run_download_mimehandler(char *mime_type
, char *file
)
7062 struct mime_type
*m
;
7064 m
= find_mime_type(mime_type
);
7070 show_oops(NULL
, "can't fork download mime handler");
7080 if (g_str_has_prefix(file
, "file://"))
7081 file
+= strlen("file://");
7082 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7091 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7094 WebKitDownloadStatus status
;
7095 const gchar
*file
= NULL
, *mime
= NULL
;
7097 if (download
== NULL
)
7099 status
= webkit_download_get_status(download
);
7100 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7103 file
= webkit_download_get_destination_uri(download
);
7106 mime
= get_mime_type((char *)file
);
7110 run_download_mimehandler((char *)mime
, (char *)file
);
7114 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7115 WebKitNetworkRequest
*request
, char *mime_type
,
7116 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7119 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7123 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7124 t
->tab_id
, mime_type
);
7126 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7127 webkit_web_policy_decision_ignore(decision
);
7132 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7133 webkit_web_policy_decision_download(decision
);
7141 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7145 const gchar
*suggested_name
;
7146 gchar
*filename
= NULL
;
7148 struct download
*download_entry
;
7151 if (wk_download
== NULL
|| t
== NULL
) {
7152 show_oops(NULL
, "%s invalid parameters", __func__
);
7156 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7157 if (suggested_name
== NULL
)
7158 return (FALSE
); /* abort download */
7169 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7171 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7172 filename
: suggested_name
);
7174 } while (!stat(uri
+ strlen("file://"), &sb
));
7176 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7177 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7179 webkit_download_set_destination_uri(wk_download
, uri
);
7181 if (webkit_download_get_status(wk_download
) ==
7182 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7183 show_oops(t
, "%s: download failed to start", __func__
);
7185 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7187 /* connect "download first" mime handler */
7188 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7189 G_CALLBACK(download_status_changed_cb
), NULL
);
7191 download_entry
= g_malloc(sizeof(struct download
));
7192 download_entry
->download
= wk_download
;
7193 download_entry
->tab
= t
;
7194 download_entry
->id
= next_download_id
++;
7195 RB_INSERT(download_list
, &downloads
, download_entry
);
7196 /* get from history */
7197 g_object_ref(wk_download
);
7198 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7199 show_oops(t
, "Download of '%s' started...",
7200 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7209 /* sync other download manager tabs */
7210 update_download_tabs(NULL
);
7213 * NOTE: never redirect/render the current tab before this
7214 * function returns. This will cause the download to never start.
7216 return (ret
); /* start download */
7220 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7222 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7225 show_oops(NULL
, "webview_hover_cb");
7230 set_status(t
, uri
, XT_STATUS_LINK
);
7233 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7238 mark(struct tab
*t
, struct karg
*arg
)
7244 if ((index
= marktoindex(mark
)) == -1)
7247 if (arg
->i
== XT_MARK_SET
)
7248 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7249 else if (arg
->i
== XT_MARK_GOTO
) {
7250 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7251 show_oops(t
, "mark '%c' does not exist", mark
);
7254 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7255 something changes the document size */
7256 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7263 marks_clear(struct tab
*t
)
7267 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7268 t
->mark
[i
] = XT_INVALID_MARK
;
7274 char file
[PATH_MAX
];
7275 char *line
= NULL
, *p
;
7280 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7281 if ((f
= fopen(file
, "r+")) == NULL
) {
7282 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7286 for (i
= 1; ; i
++) {
7287 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7288 if (feof(f
) || ferror(f
))
7290 if (strlen(line
) == 0 || line
[0] == '#') {
7296 p
= strtok(line
, " \t");
7298 if (p
== NULL
|| strlen(p
) != 1 ||
7299 (index
= marktoindex(*p
)) == -1) {
7300 warnx("corrupt quickmarks file, line %d", i
);
7304 p
= strtok(NULL
, " \t");
7305 if (qmarks
[index
] != NULL
)
7306 g_free(qmarks
[index
]);
7307 qmarks
[index
] = g_strdup(p
);
7318 char file
[PATH_MAX
];
7322 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7323 if ((f
= fopen(file
, "r+")) == NULL
) {
7324 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7328 for (i
= 0; i
< XT_NOMARKS
; i
++)
7329 if (qmarks
[i
] != NULL
)
7330 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7338 qmark(struct tab
*t
, struct karg
*arg
)
7343 mark
= arg
->s
[strlen(arg
->s
)-1];
7344 index
= marktoindex(mark
);
7350 if (qmarks
[index
] != NULL
)
7351 g_free(qmarks
[index
]);
7353 qmarks_load(); /* sync if multiple instances */
7354 qmarks
[index
] = g_strdup(get_uri(t
));
7358 if (qmarks
[index
] != NULL
)
7359 load_uri(t
, qmarks
[index
]);
7361 show_oops(t
, "quickmark \"%c\" does not exist",
7367 if (qmarks
[index
] != NULL
)
7368 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7370 show_oops(t
, "quickmark \"%c\" does not exist",
7381 go_up(struct tab
*t
, struct karg
*args
)
7387 levels
= atoi(args
->s
);
7391 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7392 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7394 tmp
+= strlen(XT_PROTO_DELIM
);
7396 /* if an uri starts with a slash, leave it alone (for file:///) */
7403 p
= strrchr(tmp
, '/');
7417 gototab(struct tab
*t
, struct karg
*args
)
7420 struct karg arg
= {0, NULL
, -1};
7422 tab
= atoi(args
->s
);
7424 arg
.i
= XT_TAB_NEXT
;
7433 zoom_amount(struct tab
*t
, struct karg
*arg
)
7435 struct karg narg
= {0, NULL
, -1};
7437 narg
.i
= atoi(arg
->s
);
7438 resizetab(t
, &narg
);
7444 flip_colon(struct tab
*t
, struct karg
*arg
)
7446 struct karg narg
= {0, NULL
, -1};
7449 if (t
== NULL
|| arg
== NULL
)
7452 p
= strstr(arg
->s
, ":");
7464 /* buffer commands receive the regex that triggered them in arg.s */
7465 char bcmd
[XT_BUFCMD_SZ
];
7469 #define XT_PRE_NO (0)
7470 #define XT_PRE_YES (1)
7471 #define XT_PRE_MAYBE (2)
7473 int (*func
)(struct tab
*, struct karg
*);
7477 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7478 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7479 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7480 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7481 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7482 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7483 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7484 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7485 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7486 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7487 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7488 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7489 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7490 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7491 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7492 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7493 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7494 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7498 buffercmd_init(void)
7502 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7503 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7504 REG_EXTENDED
| REG_NOSUB
))
7505 startpage_add("invalid buffercmd regex %s",
7506 buffercmds
[i
].regex
);
7510 buffercmd_abort(struct tab
*t
)
7514 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7515 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7518 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7519 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7523 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7525 struct karg arg
= {0, NULL
, -1};
7528 arg
.s
= g_strdup(bcmd
);
7530 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7531 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7541 buffercmd_addkey(struct tab
*t
, guint keyval
)
7544 char s
[XT_BUFCMD_SZ
];
7546 if (keyval
== GDK_Escape
) {
7548 return (XT_CB_HANDLED
);
7551 /* key with modifier or non-ascii character */
7552 if (!isascii(keyval
))
7553 return (XT_CB_PASSTHROUGH
);
7555 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7556 "to buffer \"%s\"\n", keyval
, bcmd
);
7558 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7559 if (bcmd
[i
] == '\0') {
7564 /* buffer full, ignore input */
7565 if (i
>= LENGTH(bcmd
) -1) {
7566 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7568 return (XT_CB_HANDLED
);
7571 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7573 /* find exact match */
7574 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7575 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7576 (size_t) 0, NULL
, 0) == 0) {
7577 buffercmd_execute(t
, &buffercmds
[i
]);
7581 /* find non exact matches to see if we need to abort ot not */
7582 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7583 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7586 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7587 if (isdigit(bcmd
[0])) {
7588 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7592 if (sscanf(bcmd
, "%s", s
) == 0)
7595 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7596 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7599 if (sscanf(bcmd
, "%s", s
) == 0)
7602 if (c
== -1 && buffercmds
[i
].precount
)
7604 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7607 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7608 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7611 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7616 return (XT_CB_HANDLED
);
7620 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7622 struct key_binding
*k
;
7624 /* handle keybindings if buffercmd is empty.
7625 if not empty, allow commands like C-n */
7626 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7627 TAILQ_FOREACH(k
, &kbl
, entry
)
7628 if (e
->keyval
== k
->key
7629 && (entry
? k
->use_in_entry
: 1)) {
7631 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7632 return (cmd_execute(t
, k
->cmd
));
7633 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7634 return (cmd_execute(t
, k
->cmd
));
7638 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7639 return buffercmd_addkey(t
, e
->keyval
);
7641 return (XT_CB_PASSTHROUGH
);
7645 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7647 char s
[2], buf
[128];
7648 const char *errstr
= NULL
;
7650 /* don't use w directly; use t->whatever instead */
7653 show_oops(NULL
, "wv_keypress_after_cb");
7654 return (XT_CB_PASSTHROUGH
);
7657 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7658 e
->keyval
, e
->state
, t
);
7662 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7664 return (XT_CB_HANDLED
);
7668 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7670 /* we have a string */
7672 /* we have a number */
7673 snprintf(buf
, sizeof buf
,
7674 "vimprobable_fire(%s)", t
->hint_num
);
7681 /* XXX unfuck this */
7682 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7683 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7684 /* last input was numerical */
7686 l
= strlen(t
->hint_num
);
7693 t
->hint_num
[l
] = '\0';
7697 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7698 /* last input was alphanumerical */
7700 l
= strlen(t
->hint_buf
);
7707 t
->hint_buf
[l
] = '\0';
7717 /* numerical input */
7718 if (CLEAN(e
->state
) == 0 &&
7719 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7720 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7721 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7722 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7723 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7727 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7728 "invalid link number\n");
7731 snprintf(buf
, sizeof buf
,
7732 "vimprobable_update_hints(%s)",
7734 t
->hint_mode
= XT_HINT_NUMERICAL
;
7738 /* empty the counter buffer */
7739 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7740 return (XT_CB_HANDLED
);
7743 /* alphanumerical input */
7744 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7745 e
->keyval
<= GDK_z
) ||
7746 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7747 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7748 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7749 e
->keyval
<= GDK_9
) ||
7750 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7751 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7752 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7753 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7754 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7755 " %s\n", t
->hint_buf
);
7757 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7760 snprintf(buf
, sizeof buf
,
7761 "vimprobable_show_hints('%s')", t
->hint_buf
);
7762 t
->hint_mode
= XT_HINT_ALPHANUM
;
7765 /* empty the counter buffer */
7766 bzero(t
->hint_num
, sizeof t
->hint_num
);
7767 return (XT_CB_HANDLED
);
7770 return (XT_CB_HANDLED
);
7773 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7774 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7775 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7778 return (handle_keypress(t
, e
, 0));
7782 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7786 /* Hide buffers, if they are visible, with escape. */
7787 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7788 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7789 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7791 return (XT_CB_HANDLED
);
7794 return (XT_CB_PASSTHROUGH
);
7798 search_continue(struct tab
*t
)
7800 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7801 gboolean rv
= FALSE
;
7805 if (strlen(c
) == 1) {
7806 webkit_web_view_unmark_text_matches(t
->wv
);
7811 t
->search_forward
= TRUE
;
7812 else if (c
[0] == '?')
7813 t
->search_forward
= FALSE
;
7823 search_cb(struct tab
*t
)
7825 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7828 if (search_continue(t
) == FALSE
)
7832 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7834 /* not found, mark red */
7835 gdk_color_parse(XT_COLOR_RED
, &color
);
7836 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7837 /* unmark and remove selection */
7838 webkit_web_view_unmark_text_matches(t
->wv
);
7839 /* my kingdom for a way to unselect text in webview */
7841 /* found, highlight all */
7842 webkit_web_view_unmark_text_matches(t
->wv
);
7843 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7844 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7845 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7846 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7854 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7856 const gchar
*c
= gtk_entry_get_text(w
);
7859 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7860 return (XT_CB_PASSTHROUGH
);
7863 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7864 e
->keyval
, e
->state
, t
);
7866 if (search_continue(t
) == FALSE
)
7869 /* if search length is > 4 then no longer play timeout games */
7870 if (strlen(c
) > 4) {
7872 g_source_remove(t
->search_id
);
7879 /* reestablish a new timer if the user types fast */
7881 g_source_remove(t
->search_id
);
7882 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7885 return (XT_CB_PASSTHROUGH
);
7889 match_uri(const gchar
*uri
, const gchar
*key
) {
7892 gboolean match
= FALSE
;
7896 if (!strncmp(key
, uri
, len
))
7899 voffset
= strstr(uri
, "/") + 2;
7900 if (!strncmp(key
, voffset
, len
))
7902 else if (g_str_has_prefix(voffset
, "www.")) {
7903 voffset
= voffset
+ strlen("www.");
7904 if (!strncmp(key
, voffset
, len
))
7913 match_session(const gchar
*name
, const gchar
*key
) {
7916 sub
= strcasestr(name
, key
);
7922 cmd_getlist(int id
, char *key
)
7929 if (cmds
[id
].type
& XT_URLARG
) {
7930 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7931 if (match_uri(h
->uri
, key
)) {
7932 cmd_status
.list
[c
] = (char *)h
->uri
;
7938 } else if (cmds
[id
].type
& XT_SESSARG
) {
7939 TAILQ_FOREACH(s
, &sessions
, entry
)
7940 if (match_session(s
->name
, key
)) {
7941 cmd_status
.list
[c
] = (char *)s
->name
;
7947 } else if (cmds
[id
].type
& XT_SETARG
) {
7948 for (i
= 0; i
< LENGTH(rs
); i
++)
7949 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
7950 cmd_status
.list
[c
++] = rs
[i
].name
;
7956 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7958 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7959 if (cmds
[i
].level
< dep
)
7961 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7962 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
7963 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7971 cmd_getnext(int dir
)
7973 cmd_status
.index
+= dir
;
7975 if (cmd_status
.index
< 0)
7976 cmd_status
.index
= cmd_status
.len
- 1;
7977 else if (cmd_status
.index
>= cmd_status
.len
)
7978 cmd_status
.index
= 0;
7980 return cmd_status
.list
[cmd_status
.index
];
7984 cmd_tokenize(char *s
, char *tokens
[])
7988 size_t len
= strlen(s
);
7991 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7992 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7993 tok
= strtok_r(NULL
, " ", &last
), i
++)
8003 cmd_complete(struct tab
*t
, char *str
, int dir
)
8005 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8006 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8008 char *tok
, *match
, *s
= g_strdup(str
);
8010 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8013 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8016 for (i
= 0; isdigit(s
[i
]); i
++)
8019 for (; isspace(s
[i
]); i
++)
8024 levels
= cmd_tokenize(s
, tokens
);
8026 for (i
= 0; i
< levels
- 1; i
++) {
8029 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8030 if (cmds
[j
].level
< dep
)
8032 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8036 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8043 if (matchcount
== 1) {
8044 strlcat(res
, tok
, sizeof res
);
8045 strlcat(res
, " ", sizeof res
);
8055 if (cmd_status
.index
== -1)
8056 cmd_getlist(parent
, tokens
[i
]);
8058 if (cmd_status
.len
> 0) {
8059 match
= cmd_getnext(dir
);
8060 strlcat(res
, match
, sizeof res
);
8061 gtk_entry_set_text(w
, res
);
8062 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8069 cmd_execute(struct tab
*t
, char *str
)
8071 struct cmd
*cmd
= NULL
;
8072 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
8074 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8075 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8076 struct karg arg
= {0, NULL
, -1};
8081 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8087 while (isspace(s
[0]))
8090 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8091 prefix
= atoi(prefixstr
);
8095 for (tok
= strtok_r(s
, " ", &last
); tok
;
8096 tok
= strtok_r(NULL
, " ", &last
)) {
8098 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8099 if (cmds
[j
].level
< dep
)
8101 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8103 if (cmds
[j
].level
== dep
&&
8104 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8108 if (len
== strlen(cmds
[j
].cmd
)) {
8114 if (matchcount
== 1) {
8119 show_oops(t
, "Invalid command: %s", str
);
8127 arg
.precount
= prefix
;
8128 else if (cmd_prefix
> 0)
8129 arg
.precount
= cmd_prefix
;
8131 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8132 show_oops(t
, "No prefix allowed: %s", str
);
8136 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8137 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8138 arg
.precount
= atoi(arg
.s
);
8139 if (arg
.precount
<= 0) {
8140 if (arg
.s
[0] == '0')
8141 show_oops(t
, "Zero count");
8143 show_oops(t
, "Trailing characters");
8148 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8149 __func__
, arg
.precount
, arg
.s
);
8165 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8168 show_oops(NULL
, "entry_key_cb invalid parameters");
8169 return (XT_CB_PASSTHROUGH
);
8172 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8173 e
->keyval
, e
->state
, t
);
8177 if (e
->keyval
== GDK_Escape
) {
8178 /* don't use focus_webview(t) because we want to type :cmds */
8179 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8182 return (handle_keypress(t
, e
, 1));
8185 struct command_entry
*
8186 history_prev(struct command_list
*l
, struct command_entry
*at
)
8189 at
= TAILQ_LAST(l
, command_list
);
8191 at
= TAILQ_PREV(at
, command_list
, entry
);
8193 at
= TAILQ_LAST(l
, command_list
);
8199 struct command_entry
*
8200 history_next(struct command_list
*l
, struct command_entry
*at
)
8203 at
= TAILQ_FIRST(l
);
8205 at
= TAILQ_NEXT(at
, entry
);
8207 at
= TAILQ_FIRST(l
);
8214 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8216 int rv
= XT_CB_HANDLED
;
8217 const gchar
*c
= gtk_entry_get_text(w
);
8220 show_oops(NULL
, "cmd_keypress_cb parameters");
8221 return (XT_CB_PASSTHROUGH
);
8224 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8225 e
->keyval
, e
->state
, t
);
8229 e
->keyval
= GDK_Escape
;
8230 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8231 e
->keyval
= GDK_Escape
;
8233 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8234 e
->keyval
!= GDK_ISO_Left_Tab
)
8235 cmd_status
.index
= -1;
8237 switch (e
->keyval
) {
8240 cmd_complete(t
, (char *)&c
[1], 1);
8242 case GDK_ISO_Left_Tab
:
8244 cmd_complete(t
, (char *)&c
[1], -1);
8249 if ((search_at
= history_next(&shl
, search_at
))) {
8250 search_at
->line
[0] = c
[0];
8251 gtk_entry_set_text(w
, search_at
->line
);
8252 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8255 if ((history_at
= history_prev(&chl
, history_at
))) {
8256 history_at
->line
[0] = c
[0];
8257 gtk_entry_set_text(w
, history_at
->line
);
8258 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8265 if ((search_at
= history_next(&shl
, search_at
))) {
8266 search_at
->line
[0] = c
[0];
8267 gtk_entry_set_text(w
, search_at
->line
);
8268 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8271 if ((history_at
= history_next(&chl
, history_at
))) {
8272 history_at
->line
[0] = c
[0];
8273 gtk_entry_set_text(w
, history_at
->line
);
8274 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8280 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8288 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8289 webkit_web_view_unmark_text_matches(t
->wv
);
8293 rv
= XT_CB_PASSTHROUGH
;
8299 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8301 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8305 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8307 /* popup menu enabled */
8312 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8315 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8316 return (XT_CB_PASSTHROUGH
);
8319 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8320 t
->tab_id
, t
->popup
);
8322 /* if popup is enabled don't lose focus */
8325 return (XT_CB_PASSTHROUGH
);
8331 if (show_url
== 0 || t
->focus_wv
)
8334 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8336 return (XT_CB_PASSTHROUGH
);
8340 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8343 const gchar
*c
= gtk_entry_get_text(entry
);
8346 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8350 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8357 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8363 if (c
[0] == '/' || c
[0] == '?') {
8364 /* see if there is a timer pending */
8366 g_source_remove(t
->search_id
);
8371 if (t
->search_text
) {
8372 g_free(t
->search_text
);
8373 t
->search_text
= NULL
;
8376 t
->search_text
= g_strdup(s
);
8378 g_free(global_search
);
8379 global_search
= g_strdup(s
);
8380 t
->search_forward
= c
[0] == '/';
8382 history_add(&shl
, search_file
, s
, &search_history_count
);
8386 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8393 backward_cb(GtkWidget
*w
, struct tab
*t
)
8398 show_oops(NULL
, "backward_cb invalid parameters");
8402 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8409 forward_cb(GtkWidget
*w
, struct tab
*t
)
8414 show_oops(NULL
, "forward_cb invalid parameters");
8418 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8420 a
.i
= XT_NAV_FORWARD
;
8425 home_cb(GtkWidget
*w
, struct tab
*t
)
8428 show_oops(NULL
, "home_cb invalid parameters");
8432 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8438 stop_cb(GtkWidget
*w
, struct tab
*t
)
8440 WebKitWebFrame
*frame
;
8443 show_oops(NULL
, "stop_cb invalid parameters");
8447 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8449 frame
= webkit_web_view_get_main_frame(t
->wv
);
8450 if (frame
== NULL
) {
8451 show_oops(t
, "stop_cb: no frame");
8455 webkit_web_frame_stop_loading(frame
);
8456 abort_favicon_download(t
);
8460 setup_webkit(struct tab
*t
)
8462 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8463 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8464 FALSE
, (char *)NULL
);
8466 warnx("webkit does not have \"enable-dns-prefetching\" property");
8467 g_object_set(G_OBJECT(t
->settings
),
8468 "user-agent", t
->user_agent
, (char *)NULL
);
8469 g_object_set(G_OBJECT(t
->settings
),
8470 "enable-scripts", enable_scripts
, (char *)NULL
);
8471 g_object_set(G_OBJECT(t
->settings
),
8472 "enable-plugins", enable_plugins
, (char *)NULL
);
8473 g_object_set(G_OBJECT(t
->settings
),
8474 "javascript-can-open-windows-automatically", enable_scripts
,
8476 g_object_set(G_OBJECT(t
->settings
),
8477 "enable-html5-database", FALSE
, (char *)NULL
);
8478 g_object_set(G_OBJECT(t
->settings
),
8479 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8480 g_object_set(G_OBJECT(t
->settings
),
8481 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8482 g_object_set(G_OBJECT(t
->settings
),
8483 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8484 g_object_set(G_OBJECT(t
->wv
),
8485 "full-content-zoom", TRUE
, (char *)NULL
);
8487 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8491 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8493 struct tab
*ti
, *t
= NULL
;
8494 gdouble view_size
, value
, max
;
8497 TAILQ_FOREACH(ti
, &tabs
, entry
)
8498 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8506 if (adjustment
== NULL
)
8507 adjustment
= gtk_scrolled_window_get_vadjustment(
8508 GTK_SCROLLED_WINDOW(t
->browser_win
));
8510 view_size
= gtk_adjustment_get_page_size(adjustment
);
8511 value
= gtk_adjustment_get_value(adjustment
);
8512 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8515 position
= g_strdup("All");
8516 else if (value
== max
)
8517 position
= g_strdup("Bot");
8518 else if (value
== 0)
8519 position
= g_strdup("Top");
8521 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8523 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8530 create_browser(struct tab
*t
)
8534 GtkAdjustment
*adjustment
;
8537 show_oops(NULL
, "create_browser invalid parameters");
8541 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8542 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8543 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8544 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8546 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8547 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8548 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8550 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8551 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8554 t
->settings
= webkit_web_settings_new();
8556 if (user_agent
== NULL
) {
8557 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8559 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8562 t
->user_agent
= g_strdup(user_agent
);
8564 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8567 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8568 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8569 G_CALLBACK(update_statusbar_position
), NULL
);
8581 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8582 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8583 gtk_widget_set_name(w
, "xxxterm");
8584 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8585 g_signal_connect(G_OBJECT(w
), "delete_event",
8586 G_CALLBACK (gtk_main_quit
), NULL
);
8592 create_kiosk_toolbar(struct tab
*t
)
8594 GtkWidget
*toolbar
= NULL
, *b
;
8596 b
= gtk_hbox_new(FALSE
, 0);
8598 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8600 /* backward button */
8601 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8602 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8603 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8604 G_CALLBACK(backward_cb
), t
);
8605 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8607 /* forward button */
8608 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8609 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8610 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8611 G_CALLBACK(forward_cb
), t
);
8612 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8615 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8616 gtk_widget_set_sensitive(t
->gohome
, true);
8617 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8618 G_CALLBACK(home_cb
), t
);
8619 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8621 /* create widgets but don't use them */
8622 t
->uri_entry
= gtk_entry_new();
8623 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8624 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8625 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8631 create_toolbar(struct tab
*t
)
8633 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8635 b
= gtk_hbox_new(FALSE
, 0);
8637 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8639 /* backward button */
8640 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8641 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8642 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8643 G_CALLBACK(backward_cb
), t
);
8644 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8646 /* forward button */
8647 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8648 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8649 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8650 G_CALLBACK(forward_cb
), t
);
8651 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8655 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8656 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8657 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8658 G_CALLBACK(stop_cb
), t
);
8659 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8663 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8664 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8665 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8666 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8667 G_CALLBACK(js_toggle_cb
), t
);
8668 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8670 t
->uri_entry
= gtk_entry_new();
8671 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8672 G_CALLBACK(activate_uri_entry_cb
), t
);
8673 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8674 G_CALLBACK(entry_key_cb
), t
);
8676 eb1
= gtk_hbox_new(FALSE
, 0);
8677 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8678 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8679 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8682 if (search_string
) {
8684 t
->search_entry
= gtk_entry_new();
8685 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8686 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8687 G_CALLBACK(activate_search_entry_cb
), t
);
8688 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8689 G_CALLBACK(entry_key_cb
), t
);
8690 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8691 eb2
= gtk_hbox_new(FALSE
, 0);
8692 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8693 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8695 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8702 create_buffers(struct tab
*t
)
8704 GtkCellRenderer
*renderer
;
8707 view
= gtk_tree_view_new();
8709 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8711 renderer
= gtk_cell_renderer_text_new();
8712 gtk_tree_view_insert_column_with_attributes
8713 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8715 renderer
= gtk_cell_renderer_text_new();
8716 gtk_tree_view_insert_column_with_attributes
8717 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8720 gtk_tree_view_set_model
8721 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8727 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8728 GtkTreeViewColumn
*col
, struct tab
*t
)
8733 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8735 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8738 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8739 set_current_tab(id
- 1);
8745 /* after tab reordering/creation/removal */
8752 TAILQ_FOREACH(t
, &tabs
, entry
) {
8753 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8754 if (t
->tab_id
> maxid
)
8757 gtk_widget_show(t
->tab_elems
.sep
);
8760 TAILQ_FOREACH(t
, &tabs
, entry
) {
8761 if (t
->tab_id
== maxid
) {
8762 gtk_widget_hide(t
->tab_elems
.sep
);
8768 /* after active tab change */
8770 recolor_compact_tabs(void)
8776 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8777 TAILQ_FOREACH(t
, &tabs
, entry
)
8778 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8781 curid
= gtk_notebook_get_current_page(notebook
);
8782 TAILQ_FOREACH(t
, &tabs
, entry
)
8783 if (t
->tab_id
== curid
) {
8784 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8785 gtk_widget_modify_fg(t
->tab_elems
.label
,
8786 GTK_STATE_NORMAL
, &color
);
8792 set_current_tab(int page_num
)
8794 buffercmd_abort(get_current_tab());
8795 gtk_notebook_set_current_page(notebook
, page_num
);
8796 recolor_compact_tabs();
8800 undo_close_tab_save(struct tab
*t
)
8804 struct undo
*u1
, *u2
;
8806 WebKitWebHistoryItem
*item
;
8808 if ((uri
= get_uri(t
)) == NULL
)
8811 u1
= g_malloc0(sizeof(struct undo
));
8812 u1
->uri
= g_strdup(uri
);
8814 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8816 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8817 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8820 /* forward history */
8821 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8825 u1
->history
= g_list_prepend(u1
->history
,
8826 webkit_web_history_item_copy(item
));
8827 items
= g_list_next(items
);
8832 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8833 u1
->history
= g_list_prepend(u1
->history
,
8834 webkit_web_history_item_copy(item
));
8838 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8842 u1
->history
= g_list_prepend(u1
->history
,
8843 webkit_web_history_item_copy(item
));
8844 items
= g_list_next(items
);
8847 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8849 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8850 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8851 TAILQ_REMOVE(&undos
, u2
, entry
);
8853 g_list_free(u2
->history
);
8862 delete_tab(struct tab
*t
)
8866 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8872 TAILQ_REMOVE(&tabs
, t
, entry
);
8874 /* Halt all webkit activity. */
8875 abort_favicon_download(t
);
8876 webkit_web_view_stop_loading(t
->wv
);
8878 /* Save the tab, so we can undo the close. */
8879 undo_close_tab_save(t
);
8881 if (browser_mode
== XT_BM_KIOSK
) {
8882 gtk_widget_destroy(t
->uri_entry
);
8883 gtk_widget_destroy(t
->stop
);
8884 gtk_widget_destroy(t
->js_toggle
);
8887 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8888 gtk_widget_destroy(t
->vbox
);
8892 g_source_remove(t
->search_id
);
8894 g_free(t
->user_agent
);
8895 g_free(t
->stylesheet
);
8899 if (TAILQ_EMPTY(&tabs
)) {
8900 if (browser_mode
== XT_BM_KIOSK
)
8901 create_new_tab(home
, NULL
, 1, -1);
8903 create_new_tab(NULL
, NULL
, 1, -1);
8906 /* recreate session */
8907 if (session_autosave
) {
8913 recolor_compact_tabs();
8917 update_statusbar_zoom(struct tab
*t
)
8920 char s
[16] = { '\0' };
8922 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8923 if ((zoom
<= 0.99 || zoom
>= 1.01))
8924 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8925 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8929 setzoom_webkit(struct tab
*t
, int adjust
)
8931 #define XT_ZOOMPERCENT 0.04
8936 show_oops(NULL
, "setzoom_webkit invalid parameters");
8940 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8941 if (adjust
== XT_ZOOM_IN
)
8942 zoom
+= XT_ZOOMPERCENT
;
8943 else if (adjust
== XT_ZOOM_OUT
)
8944 zoom
-= XT_ZOOMPERCENT
;
8945 else if (adjust
> 0)
8946 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8948 show_oops(t
, "setzoom_webkit invalid zoom value");
8952 if (zoom
< XT_ZOOMPERCENT
)
8953 zoom
= XT_ZOOMPERCENT
;
8954 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8955 update_statusbar_zoom(t
);
8959 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8961 struct tab
*t
= (struct tab
*) data
;
8963 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8965 switch (event
->button
) {
8967 set_current_tab(t
->tab_id
);
8978 append_tab(struct tab
*t
)
8983 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8984 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8988 create_sbe(int width
)
8992 sbe
= gtk_entry_new();
8993 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8994 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8995 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8996 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8997 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8998 gtk_widget_set_size_request(sbe
, width
, -1);
9004 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9009 WebKitWebHistoryItem
*item
;
9013 int sbe_p
= 0, sbe_b
= 0,
9016 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9018 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9019 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9023 t
= g_malloc0(sizeof *t
);
9025 if (title
== NULL
) {
9026 title
= "(untitled)";
9030 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9032 /* label + button for tab */
9033 b
= gtk_hbox_new(FALSE
, 0);
9036 #if GTK_CHECK_VERSION(2, 20, 0)
9037 t
->spinner
= gtk_spinner_new();
9039 t
->label
= gtk_label_new(title
);
9040 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9041 gtk_widget_set_size_request(t
->label
, 100, 0);
9042 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9043 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9044 gtk_widget_set_size_request(b
, 130, 0);
9046 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9047 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9048 #if GTK_CHECK_VERSION(2, 20, 0)
9049 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9053 if (browser_mode
== XT_BM_KIOSK
) {
9054 t
->toolbar
= create_kiosk_toolbar(t
);
9055 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9058 t
->toolbar
= create_toolbar(t
);
9060 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9068 t
->browser_win
= create_browser(t
);
9069 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9071 /* oops message for user feedback */
9072 t
->oops
= gtk_entry_new();
9073 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9074 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9075 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9076 gdk_color_parse(XT_COLOR_RED
, &color
);
9077 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9078 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9079 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9082 t
->cmd
= gtk_entry_new();
9083 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9084 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9085 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9086 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9089 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9091 t
->sbe
.statusbar
= gtk_entry_new();
9092 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9093 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9094 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9095 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9097 /* create these widgets only if specified in statusbar_elems */
9099 t
->sbe
.position
= create_sbe(40);
9100 t
->sbe
.zoom
= create_sbe(40);
9101 t
->sbe
.buffercmd
= create_sbe(60);
9103 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9105 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9108 /* gtk widgets cannot be added to a box twice. sbe_* variables
9109 make sure of this */
9110 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9114 GtkWidget
*sep
= gtk_vseparator_new();
9116 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9117 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9118 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9119 FALSE
, FALSE
, FALSE
);
9124 warnx("flag \"%c\" specified more than "
9125 "once in statusbar_elems\n", *p
);
9129 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9130 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9134 warnx("flag \"%c\" specified more than "
9135 "once in statusbar_elems\n", *p
);
9139 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9140 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9144 warnx("flag \"%c\" specified more than "
9145 "once in statusbar_elems\n", *p
);
9149 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9150 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9153 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9158 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9161 t
->buffers
= create_buffers(t
);
9162 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9164 /* xtp meaning is normal by default */
9165 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9167 /* set empty favicon */
9168 xt_icon_from_name(t
, "text-html");
9170 /* and show it all */
9171 gtk_widget_show_all(b
);
9172 gtk_widget_show_all(t
->vbox
);
9174 /* compact tab bar */
9175 t
->tab_elems
.label
= gtk_label_new(title
);
9176 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9177 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9178 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9179 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9181 t
->tab_elems
.eventbox
= gtk_event_box_new();
9182 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9183 t
->tab_elems
.sep
= gtk_vseparator_new();
9185 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9186 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9187 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9188 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9189 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9190 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9192 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9194 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9196 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9199 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9201 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9203 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9206 id
= position
>= 0 ? position
:
9207 gtk_notebook_get_current_page(notebook
) + 1;
9208 if (id
> gtk_notebook_get_n_pages(notebook
))
9211 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9212 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9213 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9214 t
->tab_elems
.eventbox
, id
);
9219 #if GTK_CHECK_VERSION(2, 20, 0)
9220 /* turn spinner off if we are a new tab without uri */
9222 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9223 gtk_widget_hide(t
->spinner
);
9226 /* make notebook tabs reorderable */
9227 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9229 /* compact tabs clickable */
9230 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9231 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9233 g_object_connect(G_OBJECT(t
->cmd
),
9234 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9235 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9236 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9237 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9238 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9241 /* reuse wv_button_cb to hide oops */
9242 g_object_connect(G_OBJECT(t
->oops
),
9243 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9246 g_signal_connect(t
->buffers
,
9247 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9248 g_object_connect(G_OBJECT(t
->buffers
),
9249 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9251 g_object_connect(G_OBJECT(t
->wv
),
9252 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9253 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9254 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9255 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9256 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9257 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9258 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9259 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9260 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9261 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9262 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9263 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9264 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9265 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9266 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9267 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9269 g_signal_connect(t
->wv
,
9270 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9272 * XXX this puts invalid url in uri_entry and that is undesirable
9275 g_signal_connect(t
->wv
,
9276 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
9278 g_signal_connect(t
->wv
,
9279 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9281 /* hijack the unused keys as if we were the browser */
9282 g_object_connect(G_OBJECT(t
->toolbar
),
9283 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9286 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9287 G_CALLBACK(tab_close_cb
), t
);
9290 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9291 /* restore the tab's history */
9292 if (u
&& u
->history
) {
9296 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9297 items
= g_list_next(items
);
9300 item
= g_list_nth_data(u
->history
, u
->back
);
9302 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9305 g_list_free(u
->history
);
9307 webkit_web_back_forward_list_clear(t
->bfl
);
9313 url_set_visibility();
9314 statusbar_set_visibility();
9317 set_current_tab(t
->tab_id
);
9318 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9323 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9327 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9332 recolor_compact_tabs();
9333 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9338 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9344 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9346 if (gtk_notebook_get_current_page(notebook
) == -1)
9349 TAILQ_FOREACH(t
, &tabs
, entry
) {
9350 if (t
->tab_id
== pn
) {
9351 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9354 uri
= get_title(t
, TRUE
);
9355 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9361 /* can't use focus_webview here */
9362 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9369 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9372 struct tab
*t
= NULL
, *tt
;
9376 TAILQ_FOREACH(tt
, &tabs
, entry
)
9377 if (tt
->tab_id
== pn
) {
9382 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9384 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9389 menuitem_response(struct tab
*t
)
9391 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9395 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9397 GtkWidget
*menu
, *menu_items
;
9398 GdkEventButton
*bevent
;
9402 if (event
->type
== GDK_BUTTON_PRESS
) {
9403 bevent
= (GdkEventButton
*) event
;
9404 menu
= gtk_menu_new();
9406 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9407 if ((uri
= get_uri(ti
)) == NULL
)
9408 /* XXX make sure there is something to print */
9409 /* XXX add gui pages in here to look purdy */
9411 menu_items
= gtk_menu_item_new_with_label(uri
);
9412 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9413 gtk_widget_show(menu_items
);
9415 g_signal_connect_swapped((menu_items
),
9416 "activate", G_CALLBACK(menuitem_response
),
9420 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9421 bevent
->button
, bevent
->time
);
9423 /* unref object so it'll free itself when popped down */
9424 #if !GTK_CHECK_VERSION(3, 0, 0)
9425 /* XXX does not need unref with gtk+3? */
9426 g_object_ref_sink(menu
);
9427 g_object_unref(menu
);
9430 return (TRUE
/* eat event */);
9433 return (FALSE
/* propagate */);
9437 icon_size_map(int icon_size
)
9439 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9440 icon_size
> GTK_ICON_SIZE_DIALOG
)
9441 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9447 create_button(char *name
, char *stockid
, int size
)
9449 GtkWidget
*button
, *image
;
9453 rcstring
= g_strdup_printf(
9454 "style \"%s-style\"\n"
9456 " GtkWidget::focus-padding = 0\n"
9457 " GtkWidget::focus-line-width = 0\n"
9461 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9462 gtk_rc_parse_string(rcstring
);
9464 button
= gtk_button_new();
9465 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9466 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9468 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9469 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9470 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9471 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9472 gtk_widget_set_name(button
, name
);
9473 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9479 button_set_stockid(GtkWidget
*button
, char *stockid
)
9483 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9484 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9485 gtk_button_set_image(GTK_BUTTON(button
), image
);
9489 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9492 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9495 if (xterm_workaround
== 0)
9499 * xterm doesn't play nice with clipboards because it clears the
9500 * primary when clicked. We rely on primary being set to properly
9501 * handle middle mouse button clicks (paste). So when someone clears
9502 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9503 * other application behavior (as in DON'T clear primary).
9506 p
= gtk_clipboard_wait_for_text(primary
);
9508 if (gdk_property_get(gdk_get_default_root_window(),
9510 gdk_atom_intern("STRING", FALSE
),
9512 1024 * 1024 /* picked out of my butt */,
9518 /* yes sir, we need to NUL the string */
9520 gtk_clipboard_set_text(primary
, p
, -1);
9534 char file
[PATH_MAX
];
9537 vbox
= gtk_vbox_new(FALSE
, 0);
9538 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9539 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9540 #if !GTK_CHECK_VERSION(3, 0, 0)
9541 /* XXX seems to be needed with gtk+2 */
9542 gtk_notebook_set_tab_hborder(notebook
, 0);
9543 gtk_notebook_set_tab_vborder(notebook
, 0);
9545 gtk_notebook_set_scrollable(notebook
, TRUE
);
9546 gtk_notebook_set_show_border(notebook
, FALSE
);
9547 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9549 abtn
= gtk_button_new();
9550 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9551 gtk_widget_set_size_request(arrow
, -1, -1);
9552 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9553 gtk_widget_set_size_request(abtn
, -1, 20);
9555 #if GTK_CHECK_VERSION(2, 20, 0)
9556 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9558 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9560 /* compact tab bar */
9561 tab_bar
= gtk_hbox_new(TRUE
, 0);
9563 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9564 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9565 gtk_widget_set_size_request(vbox
, -1, -1);
9567 g_object_connect(G_OBJECT(notebook
),
9568 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9570 g_object_connect(G_OBJECT(notebook
),
9571 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9572 NULL
, (char *)NULL
);
9573 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9574 G_CALLBACK(arrow_cb
), NULL
);
9576 main_window
= create_window();
9577 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9580 for (i
= 0; i
< LENGTH(icons
); i
++) {
9581 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9582 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9583 l
= g_list_append(l
, pb
);
9585 gtk_window_set_default_icon_list(l
);
9587 /* clipboard work around */
9588 if (xterm_workaround
)
9590 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9591 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9593 gtk_widget_show_all(abtn
);
9594 gtk_widget_show_all(main_window
);
9595 notebook_tab_set_visibility();
9599 set_hook(void **hook
, char *name
)
9602 errx(1, "set_hook");
9604 if (*hook
== NULL
) {
9605 *hook
= dlsym(RTLD_NEXT
, name
);
9607 errx(1, "can't hook %s", name
);
9611 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9613 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9615 g_return_val_if_fail(cookie1
, FALSE
);
9616 g_return_val_if_fail(cookie2
, FALSE
);
9618 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9619 !strcmp (cookie1
->value
, cookie2
->value
) &&
9620 !strcmp (cookie1
->path
, cookie2
->path
) &&
9621 !strcmp (cookie1
->domain
, cookie2
->domain
));
9625 transfer_cookies(void)
9628 SoupCookie
*sc
, *pc
;
9630 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9632 for (;cf
; cf
= cf
->next
) {
9634 sc
= soup_cookie_copy(pc
);
9635 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9638 soup_cookies_free(cf
);
9642 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9647 print_cookie("soup_cookie_jar_delete_cookie", c
);
9649 if (cookies_enabled
== 0)
9652 if (jar
== NULL
|| c
== NULL
)
9655 /* find and remove from persistent jar */
9656 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9658 for (;cf
; cf
= cf
->next
) {
9660 if (soup_cookie_equal(ci
, c
)) {
9661 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9666 soup_cookies_free(cf
);
9668 /* delete from session jar */
9669 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9673 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9675 struct domain
*d
= NULL
;
9679 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9680 jar
, p_cookiejar
, s_cookiejar
);
9682 if (cookies_enabled
== 0)
9685 /* see if we are up and running */
9686 if (p_cookiejar
== NULL
) {
9687 _soup_cookie_jar_add_cookie(jar
, cookie
);
9690 /* disallow p_cookiejar adds, shouldn't happen */
9691 if (jar
== p_cookiejar
)
9695 if (jar
== NULL
|| cookie
== NULL
)
9698 if (enable_cookie_whitelist
&&
9699 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9701 DNPRINTF(XT_D_COOKIE
,
9702 "soup_cookie_jar_add_cookie: reject %s\n",
9704 if (save_rejected_cookies
) {
9705 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9706 show_oops(NULL
, "can't open reject cookie file");
9709 fseek(r_cookie_f
, 0, SEEK_END
);
9710 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9711 cookie
->http_only
? "#HttpOnly_" : "",
9713 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9715 cookie
->secure
? "TRUE" : "FALSE",
9717 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9724 if (!allow_volatile_cookies
)
9728 if (cookie
->expires
== NULL
&& session_timeout
) {
9729 soup_cookie_set_expires(cookie
,
9730 soup_date_new_from_now(session_timeout
));
9731 print_cookie("modified add cookie", cookie
);
9734 /* see if we are white listed for persistence */
9735 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9736 /* add to persistent jar */
9737 c
= soup_cookie_copy(cookie
);
9738 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9739 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9742 /* add to session jar */
9743 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9744 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9750 char file
[PATH_MAX
];
9752 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9753 "soup_cookie_jar_add_cookie");
9754 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9755 "soup_cookie_jar_delete_cookie");
9757 if (cookies_enabled
== 0)
9761 * the following code is intricate due to overriding several libsoup
9763 * do not alter order of these operations.
9766 /* rejected cookies */
9767 if (save_rejected_cookies
)
9768 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9771 /* persistent cookies */
9772 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9773 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9775 /* session cookies */
9776 s_cookiejar
= soup_cookie_jar_new();
9777 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9778 cookie_policy
, (void *)NULL
);
9781 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9785 setup_proxy(char *uri
)
9788 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9789 soup_uri_free(proxy_uri
);
9793 if (http_proxy
!= uri
) {
9800 http_proxy
= g_strdup(uri
);
9801 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9802 proxy_uri
= soup_uri_new(http_proxy
);
9803 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
9804 g_object_set(session
, "proxy-uri", proxy_uri
,
9810 set_http_proxy(char *proxy
)
9817 /* see if we need to clear it instead */
9818 if (strlen(proxy
) == 0) {
9823 uri
= soup_uri_new(proxy
);
9824 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9835 send_cmd_to_socket(char *cmd
)
9838 struct sockaddr_un sa
;
9840 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9841 warnx("%s: socket", __func__
);
9845 sa
.sun_family
= AF_UNIX
;
9846 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9847 work_dir
, XT_SOCKET_FILE
);
9850 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9851 warnx("%s: connect", __func__
);
9855 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9856 warnx("%s: send", __func__
);
9867 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9870 char str
[XT_MAX_URL_LENGTH
];
9871 socklen_t t
= sizeof(struct sockaddr_un
);
9872 struct sockaddr_un sa
;
9877 gint fd
= g_io_channel_unix_get_fd(source
);
9879 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9884 if (getpeereid(s
, &uid
, &gid
) == -1) {
9888 if (uid
!= getuid() || gid
!= getgid()) {
9889 warnx("unauthorized user");
9895 warnx("not a valid user");
9899 n
= recv(s
, str
, sizeof(str
), 0);
9903 tt
= TAILQ_LAST(&tabs
, tab_list
);
9904 cmd_execute(tt
, str
);
9912 struct sockaddr_un sa
;
9914 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9915 warn("is_running: socket");
9919 sa
.sun_family
= AF_UNIX
;
9920 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9921 work_dir
, XT_SOCKET_FILE
);
9924 /* connect to see if there is a listener */
9925 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9926 rv
= 0; /* not running */
9928 rv
= 1; /* already running */
9939 struct sockaddr_un sa
;
9941 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9942 warn("build_socket: socket");
9946 sa
.sun_family
= AF_UNIX
;
9947 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9948 work_dir
, XT_SOCKET_FILE
);
9951 /* connect to see if there is a listener */
9952 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9953 /* no listener so we will */
9954 unlink(sa
.sun_path
);
9956 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9957 warn("build_socket: bind");
9961 if (listen(s
, 1) == -1) {
9962 warn("build_socket: listen");
9975 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9976 GtkTreeIter
*iter
, struct tab
*t
)
9980 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9988 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9989 GtkTreeIter
*iter
, struct tab
*t
)
9993 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9994 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9995 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10002 completion_add_uri(const gchar
*uri
)
10006 /* add uri to list_store */
10007 gtk_list_store_append(completion_model
, &iter
);
10008 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10012 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10013 GtkTreeIter
*iter
, gpointer user_data
)
10016 gboolean match
= FALSE
;
10018 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10024 match
= match_uri(value
, key
);
10031 completion_add(struct tab
*t
)
10033 /* enable completion for tab */
10034 t
->completion
= gtk_entry_completion_new();
10035 gtk_entry_completion_set_text_column(t
->completion
, 0);
10036 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10037 gtk_entry_completion_set_model(t
->completion
,
10038 GTK_TREE_MODEL(completion_model
));
10039 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10041 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10042 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10043 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10044 G_CALLBACK(completion_select_cb
), t
);
10045 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10046 G_CALLBACK(completion_hover_cb
), t
);
10054 if (stat(dir
, &sb
)) {
10055 if (mkdir(dir
, S_IRWXU
) == -1)
10056 err(1, "mkdir %s", dir
);
10057 if (stat(dir
, &sb
))
10058 err(1, "stat %s", dir
);
10060 if (S_ISDIR(sb
.st_mode
) == 0)
10061 errx(1, "%s not a dir", dir
);
10062 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10063 warnx("fixing invalid permissions on %s", dir
);
10064 if (chmod(dir
, S_IRWXU
) == -1)
10065 err(1, "chmod %s", dir
);
10073 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10079 main(int argc
, char *argv
[])
10082 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10083 char conf
[PATH_MAX
] = { '\0' };
10084 char file
[PATH_MAX
];
10085 char *env_proxy
= NULL
;
10089 struct sigaction sact
;
10090 GIOChannel
*channel
;
10096 gtk_init(&argc
, &argv
);
10098 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10102 RB_INIT(&downloads
);
10104 TAILQ_INIT(&sessions
);
10107 TAILQ_INIT(&aliases
);
10108 TAILQ_INIT(&undos
);
10114 /* fiddle with ulimits */
10115 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10118 /* just use them all */
10119 rlp
.rlim_cur
= rlp
.rlim_max
;
10120 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10122 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10124 else if (rlp
.rlim_cur
<= 256)
10125 startpage_add("%s requires at least 256 file "
10126 "descriptors, currently it has up to %d available",
10127 __progname
, rlp
.rlim_cur
);
10130 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10139 errx(0 , "Version: %s", version
);
10142 strlcpy(conf
, optarg
, sizeof(conf
));
10145 strlcpy(named_session
, optarg
, sizeof(named_session
));
10164 init_keybindings();
10166 gnutls_global_init();
10168 /* generate session keys for xtp pages */
10169 generate_xtp_session_key(&dl_session_key
);
10170 generate_xtp_session_key(&hl_session_key
);
10171 generate_xtp_session_key(&cl_session_key
);
10172 generate_xtp_session_key(&fl_session_key
);
10175 bzero(&sact
, sizeof(sact
));
10176 sigemptyset(&sact
.sa_mask
);
10177 sact
.sa_handler
= sigchild
;
10178 sact
.sa_flags
= SA_NOCLDSTOP
;
10179 sigaction(SIGCHLD
, &sact
, NULL
);
10181 /* set download dir */
10182 pwd
= getpwuid(getuid());
10184 errx(1, "invalid user %d", getuid());
10185 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10187 /* compile buffer command regexes */
10190 /* set default string settings */
10191 home
= g_strdup("https://www.cyphertite.com");
10192 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10193 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10194 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10195 cmd_font_name
= g_strdup("monospace normal 9");
10196 oops_font_name
= g_strdup("monospace normal 9");
10197 statusbar_font_name
= g_strdup("monospace normal 9");
10198 tabbar_font_name
= g_strdup("monospace normal 9");
10199 statusbar_elems
= g_strdup("BP");
10201 /* read config file */
10202 if (strlen(conf
) == 0)
10203 snprintf(conf
, sizeof conf
, "%s/.%s",
10204 pwd
->pw_dir
, XT_CONF_FILE
);
10205 config_parse(conf
, 0);
10208 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10209 oops_font
= pango_font_description_from_string(oops_font_name
);
10210 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10211 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10213 /* working directory */
10214 if (strlen(work_dir
) == 0)
10215 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10216 pwd
->pw_dir
, XT_DIR
);
10219 /* icon cache dir */
10220 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10221 xxx_dir(cache_dir
);
10224 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10225 xxx_dir(certs_dir
);
10228 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10229 work_dir
, XT_SESSIONS_DIR
);
10230 xxx_dir(sessions_dir
);
10232 /* runtime settings that can override config file */
10233 if (runtime_settings
[0] != '\0')
10234 config_parse(runtime_settings
, 1);
10237 if (!strcmp(download_dir
, pwd
->pw_dir
))
10238 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10239 xxx_dir(download_dir
);
10241 /* favorites file */
10242 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10243 if (stat(file
, &sb
)) {
10244 warnx("favorites file doesn't exist, creating it");
10245 if ((f
= fopen(file
, "w")) == NULL
)
10246 err(1, "favorites");
10250 /* quickmarks file */
10251 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10252 if (stat(file
, &sb
)) {
10253 warnx("quickmarks file doesn't exist, creating it");
10254 if ((f
= fopen(file
, "w")) == NULL
)
10255 err(1, "quickmarks");
10259 /* search history */
10260 if (history_autosave
) {
10261 snprintf(search_file
, sizeof search_file
, "%s/%s",
10262 work_dir
, XT_SEARCH_FILE
);
10263 if (stat(search_file
, &sb
)) {
10264 warnx("search history file doesn't exist, creating it");
10265 if ((f
= fopen(search_file
, "w")) == NULL
)
10266 err(1, "search_history");
10269 history_read(&shl
, search_file
, &search_history_count
);
10272 /* command history */
10273 if (history_autosave
) {
10274 snprintf(command_file
, sizeof command_file
, "%s/%s",
10275 work_dir
, XT_COMMAND_FILE
);
10276 if (stat(command_file
, &sb
)) {
10277 warnx("command history file doesn't exist, creating it");
10278 if ((f
= fopen(command_file
, "w")) == NULL
)
10279 err(1, "command_history");
10282 history_read(&chl
, command_file
, &cmd_history_count
);
10286 session
= webkit_get_default_session();
10291 if (stat(ssl_ca_file
, &sb
)) {
10292 warnx("no CA file: %s", ssl_ca_file
);
10293 g_free(ssl_ca_file
);
10294 ssl_ca_file
= NULL
;
10296 g_object_set(session
,
10297 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10298 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10302 /* guess_search regex */
10303 if (url_regex
== NULL
)
10304 url_regex
= g_strdup(XT_URL_REGEX
);
10306 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10307 startpage_add("invalid url regex %s", url_regex
);
10310 env_proxy
= getenv("http_proxy");
10312 setup_proxy(env_proxy
);
10314 setup_proxy(http_proxy
);
10317 send_cmd_to_socket(argv
[0]);
10321 /* set some connection parameters */
10322 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10323 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10326 /* see if there is already an xxxterm running */
10327 if (single_instance
&& is_running()) {
10329 warnx("already running");
10334 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10335 send_cmd_to_socket(cmd
);
10345 /* uri completion */
10346 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10349 buffers_store
= gtk_list_store_new
10350 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10356 notebook_tab_set_visibility();
10358 if (save_global_history
)
10359 restore_global_history();
10361 /* restore session list */
10362 restore_sessions_list();
10364 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10365 restore_saved_tabs();
10367 a
.s
= named_session
;
10368 a
.i
= XT_SES_DONOTHING
;
10369 open_tabs(NULL
, &a
);
10372 /* see if we have an exception */
10373 if (!TAILQ_EMPTY(&spl
)) {
10374 create_new_tab("about:startpage", NULL
, focus
, -1);
10379 create_new_tab(argv
[0], NULL
, focus
, -1);
10386 if (TAILQ_EMPTY(&tabs
))
10387 create_new_tab(home
, NULL
, 1, -1);
10390 if ((s
= build_socket()) != -1) {
10391 channel
= g_io_channel_unix_new(s
);
10392 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10397 gnutls_global_deinit();