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
];
601 char *encoding
= NULL
;
603 char *cmd_font_name
= NULL
;
604 char *oops_font_name
= NULL
;
605 char *statusbar_font_name
= NULL
;
606 char *tabbar_font_name
= NULL
;
607 PangoFontDescription
*cmd_font
;
608 PangoFontDescription
*oops_font
;
609 PangoFontDescription
*statusbar_font
;
610 PangoFontDescription
*tabbar_font
;
611 char *qmarks
[XT_NOMARKS
];
613 int btn_down
; /* M1 down in any wv */
614 regex_t url_re
; /* guess_search regex */
618 int set_browser_mode(struct settings
*, char *);
619 int set_cookie_policy(struct settings
*, char *);
620 int set_download_dir(struct settings
*, char *);
621 int set_default_script(struct settings
*, char *);
622 int set_runtime_dir(struct settings
*, char *);
623 int set_tab_style(struct settings
*, char *);
624 int set_work_dir(struct settings
*, char *);
625 int add_alias(struct settings
*, char *);
626 int add_mime_type(struct settings
*, char *);
627 int add_cookie_wl(struct settings
*, char *);
628 int add_js_wl(struct settings
*, char *);
629 int add_kb(struct settings
*, char *);
630 void button_set_stockid(GtkWidget
*, char *);
631 GtkWidget
* create_button(char *, char *, int);
633 char *get_browser_mode(struct settings
*);
634 char *get_cookie_policy(struct settings
*);
635 char *get_download_dir(struct settings
*);
636 char *get_default_script(struct settings
*);
637 char *get_runtime_dir(struct settings
*);
638 char *get_tab_style(struct settings
*);
639 char *get_work_dir(struct settings
*);
640 void startpage_add(const char *, ...);
642 void walk_alias(struct settings
*, void (*)(struct settings
*,
643 char *, void *), void *);
644 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
645 char *, void *), void *);
646 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
647 char *, void *), void *);
648 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
650 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
651 char *, void *), void *);
653 void recalc_tabs(void);
654 void recolor_compact_tabs(void);
655 void set_current_tab(int page_num
);
656 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
657 void marks_clear(struct tab
*t
);
659 int set_http_proxy(char *);
662 int (*set
)(struct settings
*, char *);
663 char *(*get
)(struct settings
*);
664 void (*walk
)(struct settings
*,
665 void (*cb
)(struct settings
*, char *, void *),
669 struct special s_browser_mode
= {
675 struct special s_cookie
= {
681 struct special s_alias
= {
687 struct special s_mime
= {
693 struct special s_js
= {
699 struct special s_kb
= {
705 struct special s_cookie_wl
= {
711 struct special s_default_script
= {
717 struct special s_download_dir
= {
723 struct special s_work_dir
= {
729 struct special s_tab_style
= {
738 #define XT_S_INVALID (0)
741 #define XT_S_FLOAT (3)
743 #define XT_SF_RESTART (1<<0)
744 #define XT_SF_RUNTIME (1<<1)
749 int (*activate
)(char *);
751 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
752 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
753 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
754 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
755 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
756 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
757 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
758 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
759 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
760 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
761 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
762 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
763 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
764 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
765 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
766 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
767 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
768 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
769 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
770 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
771 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
772 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
773 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
774 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
775 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
776 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
777 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
778 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
779 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
780 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
781 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
782 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
783 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
784 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
785 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
786 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
787 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
788 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
789 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
790 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
791 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
792 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
793 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
794 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
795 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
796 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
797 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
798 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
801 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
802 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
803 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
804 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
806 /* runtime settings */
807 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
808 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
809 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
810 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
811 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
814 int about(struct tab
*, struct karg
*);
815 int blank(struct tab
*, struct karg
*);
816 int ca_cmd(struct tab
*, struct karg
*);
817 int cookie_show_wl(struct tab
*, struct karg
*);
818 int js_show_wl(struct tab
*, struct karg
*);
819 int help(struct tab
*, struct karg
*);
820 int set(struct tab
*, struct karg
*);
821 int stats(struct tab
*, struct karg
*);
822 int marco(struct tab
*, struct karg
*);
823 int startpage(struct tab
*, struct karg
*);
824 const char * marco_message(int *);
825 int xtp_page_cl(struct tab
*, struct karg
*);
826 int xtp_page_dl(struct tab
*, struct karg
*);
827 int xtp_page_fl(struct tab
*, struct karg
*);
828 int xtp_page_hl(struct tab
*, struct karg
*);
829 void xt_icon_from_file(struct tab
*, char *);
830 const gchar
*get_uri(struct tab
*);
831 const gchar
*get_title(struct tab
*, bool);
833 #define XT_URI_ABOUT ("about:")
834 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
835 #define XT_URI_ABOUT_ABOUT ("about")
836 #define XT_URI_ABOUT_BLANK ("blank")
837 #define XT_URI_ABOUT_CERTS ("certs")
838 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
839 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
840 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
841 #define XT_URI_ABOUT_FAVORITES ("favorites")
842 #define XT_URI_ABOUT_HELP ("help")
843 #define XT_URI_ABOUT_HISTORY ("history")
844 #define XT_URI_ABOUT_JSWL ("jswl")
845 #define XT_URI_ABOUT_SET ("set")
846 #define XT_URI_ABOUT_STATS ("stats")
847 #define XT_URI_ABOUT_MARCO ("marco")
848 #define XT_URI_ABOUT_STARTPAGE ("startpage")
852 int (*func
)(struct tab
*, struct karg
*);
854 { XT_URI_ABOUT_ABOUT
, about
},
855 { XT_URI_ABOUT_BLANK
, blank
},
856 { XT_URI_ABOUT_CERTS
, ca_cmd
},
857 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
858 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
859 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
860 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
861 { XT_URI_ABOUT_HELP
, help
},
862 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
863 { XT_URI_ABOUT_JSWL
, js_show_wl
},
864 { XT_URI_ABOUT_SET
, set
},
865 { XT_URI_ABOUT_STATS
, stats
},
866 { XT_URI_ABOUT_MARCO
, marco
},
867 { XT_URI_ABOUT_STARTPAGE
, startpage
},
870 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
871 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
872 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
873 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
874 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
875 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
876 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
879 extern char *__progname
;
882 GtkWidget
*main_window
;
883 GtkNotebook
*notebook
;
885 GtkWidget
*arrow
, *abtn
;
886 struct tab_list tabs
;
887 struct history_list hl
;
888 struct session_list sessions
;
889 struct download_list downloads
;
890 struct domain_list c_wl
;
891 struct domain_list js_wl
;
892 struct undo_tailq undos
;
893 struct keybinding_list kbl
;
895 struct command_list chl
;
896 struct command_list shl
;
897 struct command_entry
*history_at
;
898 struct command_entry
*search_at
;
900 int updating_dl_tabs
= 0;
901 int updating_hl_tabs
= 0;
902 int updating_cl_tabs
= 0;
903 int updating_fl_tabs
= 0;
904 int cmd_history_count
= 0;
905 int search_history_count
= 0;
907 uint64_t blocked_cookies
= 0;
908 char named_session
[PATH_MAX
];
909 GtkListStore
*completion_model
;
910 GtkListStore
*buffers_store
;
912 void xxx_dir(char *);
913 int icon_size_map(int);
914 void completion_add(struct tab
*);
915 void completion_add_uri(const gchar
*);
916 void show_oops(struct tab
*, const char *, ...);
919 history_delete(struct command_list
*l
, int *counter
)
921 struct command_entry
*c
;
923 if (l
== NULL
|| counter
== NULL
)
926 c
= TAILQ_LAST(l
, command_list
);
930 TAILQ_REMOVE(l
, c
, entry
);
937 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
939 struct command_entry
*c
;
942 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
945 /* don't add the same line */
946 c
= TAILQ_FIRST(list
);
948 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
951 c
= g_malloc0(sizeof *c
);
952 c
->line
= g_strdup_printf(" %s", l
);
955 TAILQ_INSERT_HEAD(list
, c
, entry
);
958 history_delete(list
, counter
);
960 if (history_autosave
&& file
) {
961 f
= fopen(file
, "w");
963 show_oops(NULL
, "couldn't write history %s", file
);
967 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
969 fprintf(f
, "%s\n", c
->line
);
977 history_read(struct command_list
*list
, char *file
, int *counter
)
980 char *s
, line
[65536];
982 if (list
== NULL
|| file
== NULL
)
985 f
= fopen(file
, "r");
987 startpage_add("couldn't open history file %s", file
);
992 s
= fgets(line
, sizeof line
, f
);
993 if (s
== NULL
|| feof(f
) || ferror(f
))
995 if ((s
= strchr(line
, '\n')) == NULL
) {
996 startpage_add("invalid history file %s", file
);
1002 history_add(list
, NULL
, line
+ 1, counter
);
1010 /* marks and quickmarks array storage.
1011 * first a-z, then A-Z, then 0-9 */
1018 if (i
>= 0 && i
<= 'z' - 'a')
1022 if (i
>= 0 && i
<= 'Z' - 'A')
1037 if (m
>= 'a' && m
<= 'z')
1038 return ret
+ m
- 'a';
1040 ret
+= 'z' - 'a' + 1;
1041 if (m
>= 'A' && m
<= 'Z')
1042 return ret
+ m
- 'A';
1044 ret
+= 'Z' - 'A' + 1;
1045 if (m
>= '0' && m
<= '9')
1046 return ret
+ m
- '0';
1055 int saved_errno
, status
;
1058 saved_errno
= errno
;
1060 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1064 if (errno
!= ECHILD
) {
1066 clog_warn("sigchild: waitpid:");
1072 if (WIFEXITED(status
)) {
1073 if (WEXITSTATUS(status
) != 0) {
1075 clog_warnx("sigchild: child exit status: %d",
1076 WEXITSTATUS(status));
1081 clog_warnx("sigchild: child is terminated abnormally");
1086 errno
= saved_errno
;
1090 is_g_object_setting(GObject
*o
, char *str
)
1092 guint n_props
= 0, i
;
1093 GParamSpec
**proplist
;
1095 if (! G_IS_OBJECT(o
))
1098 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1101 for (i
=0; i
< n_props
; i
++) {
1102 if (! strcmp(proplist
[i
]->name
, str
))
1109 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1113 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1115 "<title>%s</title>\n"
1124 addstyles
? XT_PAGE_STYLE
: "",
1133 * Display a web page from a HTML string in memory, rather than from a URL
1136 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1138 char file
[PATH_MAX
];
1141 /* we set this to indicate we want to manually do navaction */
1143 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1145 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1147 /* set t->xtp_meaning */
1148 for (i
= 0; i
< LENGTH(about_list
); i
++)
1149 if (!strcmp(title
, about_list
[i
].name
)) {
1154 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
1156 #if GTK_CHECK_VERSION(2, 20, 0)
1157 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1158 gtk_widget_hide(t
->spinner
);
1160 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1161 xt_icon_from_file(t
, file
);
1166 get_current_tab(void)
1170 TAILQ_FOREACH(t
, &tabs
, entry
) {
1171 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1175 warnx("%s: no current tab", __func__
);
1181 set_status(struct tab
*t
, gchar
*s
, int status
)
1189 case XT_STATUS_LOADING
:
1190 type
= g_strdup_printf("Loading: %s", s
);
1193 case XT_STATUS_LINK
:
1194 type
= g_strdup_printf("Link: %s", s
);
1196 t
->status
= g_strdup(gtk_entry_get_text(
1197 GTK_ENTRY(t
->sbe
.statusbar
)));
1201 type
= g_strdup_printf("%s", s
);
1203 t
->status
= g_strdup(type
);
1207 t
->status
= g_strdup(s
);
1209 case XT_STATUS_NOTHING
:
1214 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1220 hide_cmd(struct tab
*t
)
1222 history_at
= NULL
; /* just in case */
1223 search_at
= NULL
; /* just in case */
1224 gtk_widget_hide(t
->cmd
);
1228 show_cmd(struct tab
*t
)
1232 gtk_widget_hide(t
->oops
);
1233 gtk_widget_show(t
->cmd
);
1237 hide_buffers(struct tab
*t
)
1239 gtk_widget_hide(t
->buffers
);
1240 gtk_list_store_clear(buffers_store
);
1250 sort_tabs_by_page_num(struct tab
***stabs
)
1255 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1257 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1259 TAILQ_FOREACH(t
, &tabs
, entry
)
1260 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1266 buffers_make_list(void)
1269 const gchar
*title
= NULL
;
1271 struct tab
**stabs
= NULL
;
1273 num_tabs
= sort_tabs_by_page_num(&stabs
);
1275 for (i
= 0; i
< num_tabs
; i
++)
1277 gtk_list_store_append(buffers_store
, &iter
);
1278 title
= get_title(stabs
[i
], FALSE
);
1279 gtk_list_store_set(buffers_store
, &iter
,
1280 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1290 show_buffers(struct tab
*t
)
1292 buffers_make_list();
1293 gtk_widget_show(t
->buffers
);
1294 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1298 toggle_buffers(struct tab
*t
)
1300 if (gtk_widget_get_visible(t
->buffers
))
1307 buffers(struct tab
*t
, struct karg
*args
)
1315 hide_oops(struct tab
*t
)
1317 gtk_widget_hide(t
->oops
);
1321 show_oops(struct tab
*at
, const char *fmt
, ...)
1325 struct tab
*t
= NULL
;
1331 if ((t
= get_current_tab()) == NULL
)
1337 if (vasprintf(&msg
, fmt
, ap
) == -1)
1338 errx(1, "show_oops failed");
1341 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1342 gtk_widget_hide(t
->cmd
);
1343 gtk_widget_show(t
->oops
);
1350 get_as_string(struct settings
*s
)
1361 warnx("get_as_string skip %s\n", s
->name
);
1362 } else if (s
->type
== XT_S_INT
)
1363 r
= g_strdup_printf("%d", *s
->ival
);
1364 else if (s
->type
== XT_S_STR
)
1365 r
= g_strdup(*s
->sval
);
1366 else if (s
->type
== XT_S_FLOAT
)
1367 r
= g_strdup_printf("%f", *s
->fval
);
1369 r
= g_strdup_printf("INVALID TYPE");
1375 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1380 for (i
= 0; i
< LENGTH(rs
); i
++) {
1381 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1382 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1384 s
= get_as_string(&rs
[i
]);
1385 cb(&rs
[i
], s
, cb_args
);
1392 set_browser_mode(struct settings
*s
, char *val
)
1394 if (!strcmp(val
, "whitelist")) {
1395 browser_mode
= XT_BM_WHITELIST
;
1396 allow_volatile_cookies
= 0;
1397 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1398 cookies_enabled
= 1;
1399 enable_cookie_whitelist
= 1;
1400 read_only_cookies
= 0;
1401 save_rejected_cookies
= 0;
1402 session_timeout
= 3600;
1404 enable_js_whitelist
= 1;
1405 enable_localstorage
= 0;
1406 } else if (!strcmp(val
, "normal")) {
1407 browser_mode
= XT_BM_NORMAL
;
1408 allow_volatile_cookies
= 0;
1409 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1410 cookies_enabled
= 1;
1411 enable_cookie_whitelist
= 0;
1412 read_only_cookies
= 0;
1413 save_rejected_cookies
= 0;
1414 session_timeout
= 3600;
1416 enable_js_whitelist
= 0;
1417 enable_localstorage
= 1;
1418 } else if (!strcmp(val
, "kiosk")) {
1419 browser_mode
= XT_BM_KIOSK
;
1420 allow_volatile_cookies
= 0;
1421 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1422 cookies_enabled
= 1;
1423 enable_cookie_whitelist
= 0;
1424 read_only_cookies
= 0;
1425 save_rejected_cookies
= 0;
1426 session_timeout
= 3600;
1428 enable_js_whitelist
= 0;
1429 enable_localstorage
= 1;
1439 get_browser_mode(struct settings
*s
)
1443 if (browser_mode
== XT_BM_WHITELIST
)
1444 r
= g_strdup("whitelist");
1445 else if (browser_mode
== XT_BM_NORMAL
)
1446 r
= g_strdup("normal");
1447 else if (browser_mode
== XT_BM_KIOSK
)
1448 r
= g_strdup("kiosk");
1456 set_cookie_policy(struct settings
*s
, char *val
)
1458 if (!strcmp(val
, "no3rdparty"))
1459 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1460 else if (!strcmp(val
, "accept"))
1461 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1462 else if (!strcmp(val
, "reject"))
1463 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1471 get_cookie_policy(struct settings
*s
)
1475 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1476 r
= g_strdup("no3rdparty");
1477 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1478 r
= g_strdup("accept");
1479 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1480 r
= g_strdup("reject");
1488 get_default_script(struct settings
*s
)
1490 if (default_script
[0] == '\0')
1492 return (g_strdup(default_script
));
1496 set_default_script(struct settings
*s
, char *val
)
1499 snprintf(default_script
, sizeof default_script
, "%s/%s",
1500 pwd
->pw_dir
, &val
[1]);
1502 strlcpy(default_script
, val
, sizeof default_script
);
1508 get_download_dir(struct settings
*s
)
1510 if (download_dir
[0] == '\0')
1512 return (g_strdup(download_dir
));
1516 set_download_dir(struct settings
*s
, char *val
)
1519 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1520 pwd
->pw_dir
, &val
[1]);
1522 strlcpy(download_dir
, val
, sizeof download_dir
);
1529 * We use these to prevent people putting xxxt:// URLs on
1530 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1532 #define XT_XTP_SES_KEY_SZ 8
1533 #define XT_XTP_SES_KEY_HEX_FMT \
1534 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1535 char *dl_session_key
; /* downloads */
1536 char *hl_session_key
; /* history list */
1537 char *cl_session_key
; /* cookie list */
1538 char *fl_session_key
; /* favorites list */
1540 char work_dir
[PATH_MAX
];
1541 char certs_dir
[PATH_MAX
];
1542 char cache_dir
[PATH_MAX
];
1543 char sessions_dir
[PATH_MAX
];
1544 char cookie_file
[PATH_MAX
];
1545 SoupURI
*proxy_uri
= NULL
;
1546 SoupSession
*session
;
1547 SoupCookieJar
*s_cookiejar
;
1548 SoupCookieJar
*p_cookiejar
;
1549 char rc_fname
[PATH_MAX
];
1551 struct mime_type_list mtl
;
1552 struct alias_list aliases
;
1555 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1556 void delete_tab(struct tab
*);
1557 void setzoom_webkit(struct tab
*, int);
1558 int run_script(struct tab
*, char *);
1559 int download_rb_cmp(struct download
*, struct download
*);
1560 gboolean
cmd_execute(struct tab
*t
, char *str
);
1563 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1565 return (strcmp(h1
->uri
, h2
->uri
));
1567 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1570 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1572 return (strcmp(d1
->d
, d2
->d
));
1574 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1577 get_work_dir(struct settings
*s
)
1579 if (work_dir
[0] == '\0')
1581 return (g_strdup(work_dir
));
1585 set_work_dir(struct settings
*s
, char *val
)
1588 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1589 pwd
->pw_dir
, &val
[1]);
1591 strlcpy(work_dir
, val
, sizeof work_dir
);
1597 get_tab_style(struct settings
*s
)
1599 if (tab_style
== XT_TABS_NORMAL
)
1600 return (g_strdup("normal"));
1602 return (g_strdup("compact"));
1606 set_tab_style(struct settings
*s
, char *val
)
1608 if (!strcmp(val
, "normal"))
1609 tab_style
= XT_TABS_NORMAL
;
1610 else if (!strcmp(val
, "compact"))
1611 tab_style
= XT_TABS_COMPACT
;
1619 * generate a session key to secure xtp commands.
1620 * pass in a ptr to the key in question and it will
1621 * be modified in place.
1624 generate_xtp_session_key(char **key
)
1626 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1632 /* make a new one */
1633 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1634 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1635 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1636 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1638 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1642 * validate a xtp session key.
1646 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1648 if (strcmp(trusted
, untrusted
) != 0) {
1649 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1658 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1660 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1662 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1664 struct valid_url_types
{
1675 valid_url_type(char *url
)
1679 for (i
= 0; i
< LENGTH(vut
); i
++)
1680 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1687 print_cookie(char *msg
, SoupCookie
*c
)
1693 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1694 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1695 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1696 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1697 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1698 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1699 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1700 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1701 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1702 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1706 walk_alias(struct settings
*s
,
1707 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1712 if (s
== NULL
|| cb
== NULL
) {
1713 show_oops(NULL
, "walk_alias invalid parameters");
1717 TAILQ_FOREACH(a
, &aliases
, entry
) {
1718 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1719 cb(s
, str
, cb_args
);
1725 match_alias(char *url_in
)
1729 char *url_out
= NULL
, *search
, *enc_arg
;
1731 search
= g_strdup(url_in
);
1733 if (strsep(&arg
, " \t") == NULL
) {
1734 show_oops(NULL
, "match_alias: NULL URL");
1738 TAILQ_FOREACH(a
, &aliases
, entry
) {
1739 if (!strcmp(search
, a
->a_name
))
1744 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1747 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1748 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1751 url_out
= g_strdup_printf(a
->a_uri
, "");
1759 guess_url_type(char *url_in
)
1762 char *url_out
= NULL
, *enc_search
= NULL
;
1765 /* substitute aliases */
1766 url_out
= match_alias(url_in
);
1767 if (url_out
!= NULL
)
1770 /* see if we are an about page */
1771 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1772 for (i
= 0; i
< LENGTH(about_list
); i
++)
1773 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1774 about_list
[i
].name
)) {
1775 url_out
= g_strdup(url_in
);
1779 if (guess_search
&& url_regex
&&
1780 !(g_str_has_prefix(url_in
, "http://") ||
1781 g_str_has_prefix(url_in
, "https://"))) {
1782 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1783 /* invalid URI so search instead */
1784 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1785 url_out
= g_strdup_printf(search_string
, enc_search
);
1791 /* XXX not sure about this heuristic */
1792 if (stat(url_in
, &sb
) == 0)
1793 url_out
= g_strdup_printf("file://%s", url_in
);
1795 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1797 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1803 load_uri(struct tab
*t
, gchar
*uri
)
1806 gchar
*newuri
= NULL
;
1812 /* Strip leading spaces. */
1813 while (*uri
&& isspace(*uri
))
1816 if (strlen(uri
) == 0) {
1821 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1823 if (valid_url_type(uri
)) {
1824 newuri
= guess_url_type(uri
);
1828 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1829 for (i
= 0; i
< LENGTH(about_list
); i
++)
1830 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1831 bzero(&args
, sizeof args
);
1832 about_list
[i
].func(t
, &args
);
1833 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1837 show_oops(t
, "invalid about page");
1841 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1843 webkit_web_view_load_uri(t
->wv
, uri
);
1850 get_uri(struct tab
*t
)
1852 const gchar
*uri
= NULL
;
1854 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1856 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1857 uri
= webkit_web_view_get_uri(t
->wv
);
1859 /* use tmp_uri to make sure it is g_freed */
1862 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1863 about_list
[t
->xtp_meaning
].name
);
1870 get_title(struct tab
*t
, bool window
)
1872 const gchar
*set
= NULL
, *title
= NULL
;
1873 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1875 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1876 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1879 title
= webkit_web_view_get_title(t
->wv
);
1880 if ((set
= title
? title
: get_uri(t
)))
1884 set
= window
? XT_NAME
: "(untitled)";
1890 add_alias(struct settings
*s
, char *line
)
1893 struct alias
*a
= NULL
;
1895 if (s
== NULL
|| line
== NULL
) {
1896 show_oops(NULL
, "add_alias invalid parameters");
1901 a
= g_malloc(sizeof(*a
));
1903 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1904 show_oops(NULL
, "add_alias: incomplete alias definition");
1907 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1908 show_oops(NULL
, "add_alias: invalid alias definition");
1912 a
->a_name
= g_strdup(alias
);
1913 a
->a_uri
= g_strdup(l
);
1915 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1917 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1927 add_mime_type(struct settings
*s
, char *line
)
1931 struct mime_type
*m
= NULL
;
1932 int downloadfirst
= 0;
1934 /* XXX this could be smarter */
1936 if (line
== NULL
|| strlen(line
) == 0) {
1937 show_oops(NULL
, "add_mime_type invalid parameters");
1946 m
= g_malloc(sizeof(*m
));
1948 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1949 show_oops(NULL
, "add_mime_type: invalid mime_type");
1952 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1953 mime_type
[strlen(mime_type
) - 1] = '\0';
1958 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1959 show_oops(NULL
, "add_mime_type: invalid mime_type");
1963 m
->mt_type
= g_strdup(mime_type
);
1964 m
->mt_action
= g_strdup(l
);
1965 m
->mt_download
= downloadfirst
;
1967 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1968 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1970 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1980 find_mime_type(char *mime_type
)
1982 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1984 TAILQ_FOREACH(m
, &mtl
, entry
) {
1985 if (m
->mt_default
&&
1986 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1989 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
2002 walk_mime_type(struct settings
*s
,
2003 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2005 struct mime_type
*m
;
2008 if (s
== NULL
|| cb
== NULL
) {
2009 show_oops(NULL
, "walk_mime_type invalid parameters");
2013 TAILQ_FOREACH(m
, &mtl
, entry
) {
2014 str
= g_strdup_printf("%s%s --> %s",
2016 m
->mt_default
? "*" : "",
2018 cb(s
, str
, cb_args
);
2024 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2030 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2033 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2035 /* treat *.moo.com the same as .moo.com */
2036 if (str
[0] == '*' && str
[1] == '.')
2038 else if (str
[0] == '.')
2043 /* slice off port number */
2044 p
= g_strrstr(str
, ":");
2048 d
= g_malloc(sizeof *d
);
2050 d
->d
= g_strdup_printf(".%s", str
);
2052 d
->d
= g_strdup(str
);
2055 if (RB_INSERT(domain_list
, wl
, d
))
2058 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2069 add_cookie_wl(struct settings
*s
, char *entry
)
2071 wl_add(entry
, &c_wl
, 1);
2076 walk_cookie_wl(struct settings
*s
,
2077 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2081 if (s
== NULL
|| cb
== NULL
) {
2082 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2086 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2087 cb(s
, d
->d
, cb_args
);
2091 walk_js_wl(struct settings
*s
,
2092 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2096 if (s
== NULL
|| cb
== NULL
) {
2097 show_oops(NULL
, "walk_js_wl invalid parameters");
2101 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2102 cb(s
, d
->d
, cb_args
);
2106 add_js_wl(struct settings
*s
, char *entry
)
2108 wl_add(entry
, &js_wl
, 1 /* persistent */);
2113 wl_find(const gchar
*search
, struct domain_list
*wl
)
2116 struct domain
*d
= NULL
, dfind
;
2119 if (search
== NULL
|| wl
== NULL
)
2121 if (strlen(search
) < 2)
2124 if (search
[0] != '.')
2125 s
= g_strdup_printf(".%s", search
);
2127 s
= g_strdup(search
);
2129 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2132 d
= RB_FIND(domain_list
, wl
, &dfind
);
2146 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2152 if (s
== NULL
|| wl
== NULL
)
2155 if (!strncmp(s
, "http://", strlen("http://")))
2156 s
= &s
[strlen("http://")];
2157 else if (!strncmp(s
, "https://", strlen("https://")))
2158 s
= &s
[strlen("https://")];
2163 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2164 /* chop string at first slash */
2165 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2168 r
= wl_find(ss
, wl
);
2177 settings_add(char *var
, char *val
)
2184 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2185 if (strcmp(var
, rs
[i
].name
))
2189 if (rs
[i
].s
->set(&rs
[i
], val
))
2190 errx(1, "invalid value for %s: %s", var
, val
);
2194 switch (rs
[i
].type
) {
2203 errx(1, "invalid sval for %s",
2217 errx(1, "invalid type for %s", var
);
2226 config_parse(char *filename
, int runtime
)
2229 char *line
, *cp
, *var
, *val
;
2230 size_t len
, lineno
= 0;
2232 char file
[PATH_MAX
];
2235 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2237 if (filename
== NULL
)
2240 if (runtime
&& runtime_settings
[0] != '\0') {
2241 snprintf(file
, sizeof file
, "%s/%s",
2242 work_dir
, runtime_settings
);
2243 if (stat(file
, &sb
)) {
2244 warnx("runtime file doesn't exist, creating it");
2245 if ((f
= fopen(file
, "w")) == NULL
)
2247 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2251 strlcpy(file
, filename
, sizeof file
);
2253 if ((config
= fopen(file
, "r")) == NULL
) {
2254 warn("config_parse: cannot open %s", filename
);
2259 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2260 if (feof(config
) || ferror(config
))
2264 cp
+= (long)strspn(cp
, WS
);
2265 if (cp
[0] == '\0') {
2271 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2272 startpage_add("invalid configuration file entry: %s",
2275 cp
+= (long)strspn(cp
, WS
);
2277 if ((val
= strsep(&cp
, "\0")) == NULL
)
2280 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2281 handled
= settings_add(var
, val
);
2283 startpage_add("invalid configuration file entry: %s=%s",
2293 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2299 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2303 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2306 JSStringGetUTF8CString(jsref
, s
, l
);
2307 JSStringRelease(jsref
);
2313 disable_hints(struct tab
*t
)
2315 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2316 bzero(t
->hint_num
, sizeof t
->hint_num
);
2317 run_script(t
, "vimprobable_clear()");
2319 t
->hint_mode
= XT_HINT_NONE
;
2323 enable_hints(struct tab
*t
)
2325 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2326 run_script(t
, "vimprobable_show_hints()");
2328 t
->hint_mode
= XT_HINT_NONE
;
2331 #define XT_JS_OPEN ("open;")
2332 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2333 #define XT_JS_FIRE ("fire;")
2334 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2335 #define XT_JS_FOUND ("found;")
2336 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2339 run_script(struct tab
*t
, char *s
)
2341 JSGlobalContextRef ctx
;
2342 WebKitWebFrame
*frame
;
2344 JSValueRef val
, exception
;
2347 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2348 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2350 frame
= webkit_web_view_get_main_frame(t
->wv
);
2351 ctx
= webkit_web_frame_get_global_context(frame
);
2353 str
= JSStringCreateWithUTF8CString(s
);
2354 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2355 NULL
, 0, &exception
);
2356 JSStringRelease(str
);
2358 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2360 es
= js_ref_to_string(ctx
, exception
);
2361 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2365 es
= js_ref_to_string(ctx
, val
);
2366 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2368 /* handle return value right here */
2369 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2372 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2375 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2376 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2377 &es
[XT_JS_FIRE_LEN
]);
2382 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2383 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2394 hint(struct tab
*t
, struct karg
*args
)
2397 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2399 if (t
->hints_on
== 0)
2408 apply_style(struct tab
*t
)
2410 g_object_set(G_OBJECT(t
->settings
),
2411 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2415 userstyle(struct tab
*t
, struct karg
*args
)
2417 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2421 g_object_set(G_OBJECT(t
->settings
),
2422 "user-stylesheet-uri", NULL
, (char *)NULL
);
2431 * Doesn't work fully, due to the following bug:
2432 * https://bugs.webkit.org/show_bug.cgi?id=51747
2435 restore_global_history(void)
2437 char file
[PATH_MAX
];
2442 const char delim
[3] = {'\\', '\\', '\0'};
2444 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2446 if ((f
= fopen(file
, "r")) == NULL
) {
2447 warnx("%s: fopen", __func__
);
2452 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2453 if (feof(f
) || ferror(f
))
2456 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2457 if (feof(f
) || ferror(f
)) {
2459 warnx("%s: broken history file\n", __func__
);
2463 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2464 webkit_web_history_item_new_with_data(uri
, title
);
2465 h
= g_malloc(sizeof(struct history
));
2466 h
->uri
= g_strdup(uri
);
2467 h
->title
= g_strdup(title
);
2468 RB_INSERT(history_list
, &hl
, h
);
2469 completion_add_uri(h
->uri
);
2471 warnx("%s: failed to restore history\n", __func__
);
2487 save_global_history_to_disk(struct tab
*t
)
2489 char file
[PATH_MAX
];
2493 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2495 if ((f
= fopen(file
, "w")) == NULL
) {
2496 show_oops(t
, "%s: global history file: %s",
2497 __func__
, strerror(errno
));
2501 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2502 if (h
->uri
&& h
->title
)
2503 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2512 quit(struct tab
*t
, struct karg
*args
)
2514 if (save_global_history
)
2515 save_global_history_to_disk(t
);
2523 restore_sessions_list(void)
2526 struct dirent
*dp
= NULL
;
2529 sdir
= opendir(sessions_dir
);
2531 while ((dp
= readdir(sdir
)) != NULL
)
2532 if (dp
->d_type
== DT_REG
) {
2533 s
= g_malloc(sizeof(struct session
));
2534 s
->name
= g_strdup(dp
->d_name
);
2535 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2542 open_tabs(struct tab
*t
, struct karg
*a
)
2544 char file
[PATH_MAX
];
2548 struct tab
*ti
, *tt
;
2553 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2554 if ((f
= fopen(file
, "r")) == NULL
)
2557 ti
= TAILQ_LAST(&tabs
, tab_list
);
2560 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2561 if (feof(f
) || ferror(f
))
2564 /* retrieve session name */
2565 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2566 strlcpy(named_session
,
2567 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2568 sizeof named_session
);
2572 if (uri
&& strlen(uri
))
2573 create_new_tab(uri
, NULL
, 1, -1);
2579 /* close open tabs */
2580 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2582 tt
= TAILQ_FIRST(&tabs
);
2602 restore_saved_tabs(void)
2604 char file
[PATH_MAX
];
2605 int unlink_file
= 0;
2610 snprintf(file
, sizeof file
, "%s/%s",
2611 sessions_dir
, XT_RESTART_TABS_FILE
);
2612 if (stat(file
, &sb
) == -1)
2613 a
.s
= XT_SAVED_TABS_FILE
;
2616 a
.s
= XT_RESTART_TABS_FILE
;
2619 a
.i
= XT_SES_DONOTHING
;
2620 rv
= open_tabs(NULL
, &a
);
2629 save_tabs(struct tab
*t
, struct karg
*a
)
2631 char file
[PATH_MAX
];
2633 int num_tabs
= 0, i
;
2634 struct tab
**stabs
= NULL
;
2639 snprintf(file
, sizeof file
, "%s/%s",
2640 sessions_dir
, named_session
);
2642 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2644 if ((f
= fopen(file
, "w")) == NULL
) {
2645 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2649 /* save session name */
2650 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2652 /* Save tabs, in the order they are arranged in the notebook. */
2653 num_tabs
= sort_tabs_by_page_num(&stabs
);
2655 for (i
= 0; i
< num_tabs
; i
++)
2657 if (get_uri(stabs
[i
]) != NULL
)
2658 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2659 else if (gtk_entry_get_text(GTK_ENTRY(
2660 stabs
[i
]->uri_entry
)))
2661 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2662 stabs
[i
]->uri_entry
)));
2667 /* try and make sure this gets to disk NOW. XXX Backup first? */
2668 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2669 show_oops(t
, "May not have managed to save session: %s",
2679 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2691 run_page_script(struct tab
*t
, struct karg
*args
)
2694 char *tmp
, script
[PATH_MAX
];
2696 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2697 if (tmp
[0] == '\0') {
2698 show_oops(t
, "no script specified");
2702 if ((uri
= get_uri(t
)) == NULL
) {
2703 show_oops(t
, "tab is empty, not running script");
2708 snprintf(script
, sizeof script
, "%s/%s",
2709 pwd
->pw_dir
, &tmp
[1]);
2711 strlcpy(script
, tmp
, sizeof script
);
2715 show_oops(t
, "can't fork to run script");
2725 execlp(script
, script
, uri
, (void *)NULL
);
2735 yank_uri(struct tab
*t
, struct karg
*args
)
2738 GtkClipboard
*clipboard
;
2740 if ((uri
= get_uri(t
)) == NULL
)
2743 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2744 gtk_clipboard_set_text(clipboard
, uri
, -1);
2750 paste_uri(struct tab
*t
, struct karg
*args
)
2752 GtkClipboard
*clipboard
;
2753 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2755 gchar
*p
= NULL
, *uri
;
2757 /* try primary clipboard first */
2758 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2759 p
= gtk_clipboard_wait_for_text(clipboard
);
2761 /* if it failed get whatever text is in cut_buffer0 */
2762 if (p
== NULL
&& xterm_workaround
)
2763 if (gdk_property_get(gdk_get_default_root_window(),
2765 gdk_atom_intern("STRING", FALSE
),
2767 1024 * 1024 /* picked out of my butt */,
2773 /* yes sir, we need to NUL the string */
2779 while (*uri
&& isspace(*uri
))
2781 if (strlen(uri
) == 0) {
2782 show_oops(t
, "empty paste buffer");
2785 if (guess_search
== 0 && valid_url_type(uri
)) {
2786 /* we can be clever and paste this in search box */
2787 show_oops(t
, "not a valid URL");
2791 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2793 else if (args
->i
== XT_PASTE_NEW_TAB
)
2794 create_new_tab(uri
, NULL
, 1, -1);
2805 find_domain(const gchar
*s
, int toplevel
)
2813 uri
= soup_uri_new(s
);
2815 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2819 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2820 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2821 while(--p
>= uri
->host
&& *p
!= '.');
2828 ret
= g_strdup_printf(".%s", p
);
2836 toggle_cwl(struct tab
*t
, struct karg
*args
)
2847 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2849 if (uri
== NULL
|| dom
== NULL
||
2850 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2851 show_oops(t
, "Can't toggle domain in cookie white list");
2854 d
= wl_find(dom
, &c_wl
);
2861 if (args
->i
& XT_WL_TOGGLE
)
2863 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2865 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2869 /* enable cookies for domain */
2870 wl_add(dom
, &c_wl
, 0);
2872 /* disable cookies for domain */
2873 RB_REMOVE(domain_list
, &c_wl
, d
);
2875 if (args
->i
& XT_WL_RELOAD
)
2876 webkit_web_view_reload(t
->wv
);
2884 toggle_js(struct tab
*t
, struct karg
*args
)
2894 g_object_get(G_OBJECT(t
->settings
),
2895 "enable-scripts", &es
, (char *)NULL
);
2896 if (args
->i
& XT_WL_TOGGLE
)
2898 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2900 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2906 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2908 if (uri
== NULL
|| dom
== NULL
||
2909 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2910 show_oops(t
, "Can't toggle domain in JavaScript white list");
2915 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2916 wl_add(dom
, &js_wl
, 0 /* session */);
2918 d
= wl_find(dom
, &js_wl
);
2920 RB_REMOVE(domain_list
, &js_wl
, d
);
2921 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2923 g_object_set(G_OBJECT(t
->settings
),
2924 "enable-scripts", es
, (char *)NULL
);
2925 g_object_set(G_OBJECT(t
->settings
),
2926 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2927 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2929 if (args
->i
& XT_WL_RELOAD
)
2930 webkit_web_view_reload(t
->wv
);
2938 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2942 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2945 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2950 toggle_src(struct tab
*t
, struct karg
*args
)
2957 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2958 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2959 webkit_web_view_reload(t
->wv
);
2965 focus_webview(struct tab
*t
)
2970 /* only grab focus if we are visible */
2971 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2972 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2976 focus(struct tab
*t
, struct karg
*args
)
2978 if (t
== NULL
|| args
== NULL
)
2984 if (args
->i
== XT_FOCUS_URI
)
2985 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2986 else if (args
->i
== XT_FOCUS_SEARCH
)
2987 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2993 stats(struct tab
*t
, struct karg
*args
)
2995 char *page
, *body
, *s
, line
[64 * 1024];
2996 uint64_t line_count
= 0;
3000 show_oops(NULL
, "stats invalid parameters");
3003 if (save_rejected_cookies
) {
3004 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
3006 s
= fgets(line
, sizeof line
, r_cookie_f
);
3007 if (s
== NULL
|| feof(r_cookie_f
) ||
3013 snprintf(line
, sizeof line
,
3014 "<br/>Cookies blocked(*) total: %llu", line_count
);
3016 show_oops(t
, "Can't open blocked cookies file: %s",
3020 body
= g_strdup_printf(
3021 "Cookies blocked(*) this session: %llu"
3023 "<p><small><b>*</b> results vary based on settings</small></p>",
3027 page
= get_html_page("Statistics", body
, "", 0);
3030 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3037 marco(struct tab
*t
, struct karg
*args
)
3039 char *page
, line
[64 * 1024];
3043 show_oops(NULL
, "marco invalid parameters");
3046 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3048 page
= get_html_page("Marco Sez...", line
, "", 0);
3050 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3057 blank(struct tab
*t
, struct karg
*args
)
3060 show_oops(NULL
, "blank invalid parameters");
3062 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3068 about(struct tab
*t
, struct karg
*args
)
3073 show_oops(NULL
, "about invalid parameters");
3075 body
= g_strdup_printf("<b>Version: %s</b>"
3076 #ifdef XXXTERM_BUILDSTR
3077 "<br><b>Build: %s</b>"
3082 "<li>Marco Peereboom <marco@peereboom.us></li>"
3083 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3084 "<li>Edd Barrett <vext01@gmail.com> </li>"
3085 "<li>Todd T. Fries <todd@fries.net> </li>"
3086 "<li>Raphael Graf <r@undefined.ch> </li>"
3088 "Copyrights and licenses can be found on the XXXTerm "
3089 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3091 #ifdef XXXTERM_BUILDSTR
3092 version
, XXXTERM_BUILDSTR
3098 page
= get_html_page("About", body
, "", 0);
3101 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3108 help(struct tab
*t
, struct karg
*args
)
3110 char *page
, *head
, *body
;
3113 show_oops(NULL
, "help invalid parameters");
3115 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3116 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3118 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3119 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3120 "cgi-bin/man-cgi?xxxterm</a>";
3122 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3124 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3131 startpage(struct tab
*t
, struct karg
*args
)
3133 char *page
, *body
, *b
;
3137 show_oops(NULL
, "startpage invalid parameters");
3139 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3141 TAILQ_FOREACH(s
, &spl
, entry
) {
3143 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3147 page
= get_html_page("Startup Exception", body
, "", 0);
3150 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3157 startpage_add(const char *fmt
, ...)
3167 if (vasprintf(&msg
, fmt
, ap
) == -1)
3168 errx(1, "startpage_add failed");
3171 s
= g_malloc0(sizeof *s
);
3174 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3178 * update all favorite tabs apart from one. Pass NULL if
3179 * you want to update all.
3182 update_favorite_tabs(struct tab
*apart_from
)
3185 if (!updating_fl_tabs
) {
3186 updating_fl_tabs
= 1; /* stop infinite recursion */
3187 TAILQ_FOREACH(t
, &tabs
, entry
)
3188 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3189 && (t
!= apart_from
))
3190 xtp_page_fl(t
, NULL
);
3191 updating_fl_tabs
= 0;
3195 /* show a list of favorites (bookmarks) */
3197 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3199 char file
[PATH_MAX
];
3201 char *uri
= NULL
, *title
= NULL
;
3202 size_t len
, lineno
= 0;
3204 char *body
, *tmp
, *page
= NULL
;
3205 const char delim
[3] = {'\\', '\\', '\0'};
3207 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3210 warn("%s: bad param", __func__
);
3212 /* new session key */
3213 if (!updating_fl_tabs
)
3214 generate_xtp_session_key(&fl_session_key
);
3216 /* open favorites */
3217 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3218 if ((f
= fopen(file
, "r")) == NULL
) {
3219 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3224 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3225 "<th style='width: 40px'>#</th><th>Link</th>"
3226 "<th style='width: 40px'>Rm</th></tr>\n");
3229 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3230 if (feof(f
) || ferror(f
))
3232 if (strlen(title
) == 0 || title
[0] == '#') {
3238 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3239 if (feof(f
) || ferror(f
)) {
3240 show_oops(t
, "favorites file corrupt");
3246 body
= g_strdup_printf("%s<tr>"
3248 "<td><a href='%s'>%s</a></td>"
3249 "<td style='text-align: center'>"
3250 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3252 body
, i
, uri
, title
,
3253 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3265 /* if none, say so */
3268 body
= g_strdup_printf("%s<tr>"
3269 "<td colspan='3' style='text-align: center'>"
3270 "No favorites - To add one use the 'favadd' command."
3271 "</td></tr>", body
);
3276 body
= g_strdup_printf("%s</table>", body
);
3286 page
= get_html_page("Favorites", body
, "", 1);
3287 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3291 update_favorite_tabs(t
);
3300 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3301 size_t cert_count
, char *title
)
3303 gnutls_datum_t cinfo
;
3307 body
= g_strdup("");
3309 for (i
= 0; i
< cert_count
; i
++) {
3310 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3315 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3316 body
, i
, cinfo
.data
);
3317 gnutls_free(cinfo
.data
);
3321 tmp
= get_html_page(title
, body
, "", 0);
3324 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3329 ca_cmd(struct tab
*t
, struct karg
*args
)
3332 int rv
= 1, certs
= 0, certs_read
;
3335 gnutls_x509_crt_t
*c
= NULL
;
3336 char *certs_buf
= NULL
, *s
;
3338 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3339 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3343 if (fstat(fileno(f
), &sb
) == -1) {
3344 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3348 certs_buf
= g_malloc(sb
.st_size
+ 1);
3349 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3350 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3353 certs_buf
[sb
.st_size
] = '\0';
3356 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3358 s
+= strlen("BEGIN CERTIFICATE");
3361 bzero(&dt
, sizeof dt
);
3362 dt
.data
= (unsigned char *)certs_buf
;
3363 dt
.size
= sb
.st_size
;
3364 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3365 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3366 GNUTLS_X509_FMT_PEM
, 0);
3367 if (certs_read
<= 0) {
3368 show_oops(t
, "No cert(s) available");
3371 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3384 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3388 struct addrinfo hints
, *res
= NULL
, *ai
;
3389 int rv
= -1, s
= -1, on
, error
;
3392 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3393 show_oops(t
, "invalid URI");
3397 su
= soup_uri_new(uri
);
3399 show_oops(t
, "invalid soup URI");
3402 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3403 show_oops(t
, "invalid HTTPS URI");
3407 snprintf(port
, sizeof port
, "%d", su
->port
);
3408 bzero(&hints
, sizeof(struct addrinfo
));
3409 hints
.ai_flags
= AI_CANONNAME
;
3410 hints
.ai_family
= AF_UNSPEC
;
3411 hints
.ai_socktype
= SOCK_STREAM
;
3413 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3414 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3418 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3424 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3426 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3429 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3432 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3436 show_oops(t
, "could not obtain certificates from: %s",
3442 strlcpy(domain
, su
->host
, domain_sz
);
3449 if (rv
== -1 && s
!= -1)
3456 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3459 gnutls_deinit(gsession
);
3461 gnutls_certificate_free_credentials(xcred
);
3467 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3468 gnutls_certificate_credentials_t
*xc
)
3470 gnutls_certificate_credentials_t xcred
;
3471 gnutls_session_t gsession
;
3474 if (gs
== NULL
|| xc
== NULL
)
3480 gnutls_certificate_allocate_credentials(&xcred
);
3481 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3482 GNUTLS_X509_FMT_PEM
);
3484 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3485 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3486 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3487 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3488 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3489 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3491 gnutls_error_is_fatal(rv
),
3492 gnutls_strerror_name(rv
));
3493 stop_tls(gsession
, xcred
);
3497 gnutls_credentials_type_t cred
;
3498 cred
= gnutls_auth_get_type(gsession
);
3499 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3500 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3501 stop_tls(gsession
, xcred
);
3513 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3517 const gnutls_datum_t
*cl
;
3518 gnutls_x509_crt_t
*all_certs
;
3521 if (certs
== NULL
|| cert_count
== NULL
)
3523 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3525 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3529 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3530 for (i
= 0; i
< len
; i
++) {
3531 gnutls_x509_crt_init(&all_certs
[i
]);
3532 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3533 GNUTLS_X509_FMT_PEM
< 0)) {
3547 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3551 for (i
= 0; i
< cert_count
; i
++)
3552 gnutls_x509_crt_deinit(certs
[i
]);
3557 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3559 GdkColor c_text
, c_base
;
3561 gdk_color_parse(text
, &c_text
);
3562 gdk_color_parse(base
, &c_base
);
3564 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3565 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3566 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3567 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3569 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3570 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3571 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3572 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3576 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3577 size_t cert_count
, char *domain
)
3580 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3585 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3588 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3589 if ((f
= fopen(file
, "w")) == NULL
) {
3590 show_oops(t
, "Can't create cert file %s %s",
3591 file
, strerror(errno
));
3595 for (i
= 0; i
< cert_count
; i
++) {
3596 cert_buf_sz
= sizeof cert_buf
;
3597 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3598 cert_buf
, &cert_buf_sz
)) {
3599 show_oops(t
, "gnutls_x509_crt_export failed");
3602 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3603 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3608 /* not the best spot but oh well */
3609 gdk_color_parse("lightblue", &color
);
3610 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3611 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3624 load_compare_cert(struct tab
*t
, struct karg
*args
)
3627 char domain
[8182], file
[PATH_MAX
];
3628 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3632 size_t cert_buf_sz
, cert_count
;
3633 enum cert_trust rv
= CERT_UNTRUSTED
;
3635 gnutls_session_t gsession
;
3636 gnutls_x509_crt_t
*certs
;
3637 gnutls_certificate_credentials_t xcred
;
3639 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3644 if ((uri
= get_uri(t
)) == NULL
)
3646 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3648 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3650 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3653 if (start_tls(t
, s
, &gsession
, &xcred
))
3655 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3657 /* verify certs in case cert file doesn't exist */
3658 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3660 show_oops(t
, "Invalid certificates");
3665 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3666 show_oops(t
, "Can't get connection certificates");
3670 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3671 if ((f
= fopen(file
, "r")) == NULL
) {
3677 for (i
= 0; i
< cert_count
; i
++) {
3678 cert_buf_sz
= sizeof cert_buf
;
3679 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3680 cert_buf
, &cert_buf_sz
)) {
3683 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3684 rv
= CERT_BAD
; /* critical */
3687 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3688 rv
= CERT_BAD
; /* critical */
3697 free_connection_certs(certs
, cert_count
);
3699 /* we close the socket first for speed */
3703 /* only complain if we didn't save it locally */
3704 if (error
&& rv
!= CERT_LOCAL
) {
3705 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3706 if (error
& GNUTLS_CERT_INVALID
)
3707 strlcat(serr
, "invalid, ", sizeof serr
);
3708 if (error
& GNUTLS_CERT_REVOKED
)
3709 strlcat(serr
, "revoked, ", sizeof serr
);
3710 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3711 strlcat(serr
, "signer not found, ", sizeof serr
);
3712 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3713 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3714 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3715 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3716 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3717 strlcat(serr
, "not activated, ", sizeof serr
);
3718 if (error
& GNUTLS_CERT_EXPIRED
)
3719 strlcat(serr
, "expired, ", sizeof serr
);
3720 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3721 if (serr
[i
] == ',') {
3728 stop_tls(gsession
, xcred
);
3734 cert_cmd(struct tab
*t
, struct karg
*args
)
3740 gnutls_session_t gsession
;
3741 gnutls_x509_crt_t
*certs
;
3742 gnutls_certificate_credentials_t xcred
;
3747 if (ssl_ca_file
== NULL
) {
3748 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3752 if ((uri
= get_uri(t
)) == NULL
) {
3753 show_oops(t
, "Invalid URI");
3757 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3758 show_oops(t
, "Invalid certificate URI: %s", uri
);
3763 if (start_tls(t
, s
, &gsession
, &xcred
))
3767 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3768 show_oops(t
, "get_connection_certs failed");
3772 if (args
->i
& XT_SHOW
)
3773 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3774 else if (args
->i
& XT_SAVE
)
3775 save_certs(t
, certs
, cert_count
, domain
);
3777 free_connection_certs(certs
, cert_count
);
3779 /* we close the socket first for speed */
3782 stop_tls(gsession
, xcred
);
3788 remove_cookie(int index
)
3794 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3796 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3798 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3802 print_cookie("remove cookie", c
);
3803 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3808 soup_cookies_free(cf
);
3814 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3819 body
= g_strdup("");
3822 if (args
->i
& XT_WL_PERSISTENT
) {
3824 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3826 RB_FOREACH(d
, domain_list
, wl
) {
3830 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3836 if (args
->i
& XT_WL_SESSION
) {
3838 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3840 RB_FOREACH(d
, domain_list
, wl
) {
3844 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3849 tmp
= get_html_page(title
, body
, "", 0);
3852 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3854 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3860 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3862 char file
[PATH_MAX
];
3864 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3872 if (t
== NULL
|| args
== NULL
)
3875 if (runtime_settings
[0] == '\0')
3878 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3879 if ((f
= fopen(file
, "r+")) == NULL
)
3883 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3884 if (uri
== NULL
|| dom
== NULL
||
3885 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3886 show_oops(t
, "Can't add domain to %s white list",
3887 js
? "JavaScript" : "cookie");
3891 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3894 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3897 if (!strcmp(line
, lt
))
3903 fprintf(f
, "%s\n", lt
);
3908 d
= wl_find(dom
, &js_wl
);
3910 settings_add("js_wl", dom
);
3911 d
= wl_find(dom
, &js_wl
);
3915 d
= wl_find(dom
, &c_wl
);
3917 settings_add("cookie_wl", dom
);
3918 d
= wl_find(dom
, &c_wl
);
3922 /* find and add to persistent jar */
3923 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3924 for (;cf
; cf
= cf
->next
) {
3926 if (!strcmp(dom
, ci
->domain
) ||
3927 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3928 c
= soup_cookie_copy(ci
);
3929 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3932 soup_cookies_free(cf
);
3950 js_show_wl(struct tab
*t
, struct karg
*args
)
3952 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3953 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3959 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3961 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3962 wl_show(t
, args
, "Cookie White List", &c_wl
);
3968 cookie_cmd(struct tab
*t
, struct karg
*args
)
3970 if (args
->i
& XT_SHOW
)
3971 wl_show(t
, args
, "Cookie White List", &c_wl
);
3972 else if (args
->i
& XT_WL_TOGGLE
) {
3973 args
->i
|= XT_WL_RELOAD
;
3974 toggle_cwl(t
, args
);
3975 } else if (args
->i
& XT_SAVE
) {
3976 args
->i
|= XT_WL_RELOAD
;
3977 wl_save(t
, args
, 0);
3978 } else if (args
->i
& XT_DELETE
)
3979 show_oops(t
, "'cookie delete' currently unimplemented");
3985 js_cmd(struct tab
*t
, struct karg
*args
)
3987 if (args
->i
& XT_SHOW
)
3988 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3989 else if (args
->i
& XT_SAVE
) {
3990 args
->i
|= XT_WL_RELOAD
;
3991 wl_save(t
, args
, 1);
3992 } else if (args
->i
& XT_WL_TOGGLE
) {
3993 args
->i
|= XT_WL_RELOAD
;
3995 } else if (args
->i
& XT_DELETE
)
3996 show_oops(t
, "'js delete' currently unimplemented");
4002 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4004 js_toggle_cb(t
->js_toggle
, t
);
4010 add_favorite(struct tab
*t
, struct karg
*args
)
4012 char file
[PATH_MAX
];
4015 size_t urilen
, linelen
;
4016 const gchar
*uri
, *title
;
4021 /* don't allow adding of xtp pages to favorites */
4022 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4023 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4027 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4028 if ((f
= fopen(file
, "r+")) == NULL
) {
4029 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4033 title
= get_title(t
, FALSE
);
4036 if (title
== NULL
|| uri
== NULL
) {
4037 show_oops(t
, "can't add page to favorites");
4041 urilen
= strlen(uri
);
4044 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4045 if (feof(f
) || ferror(f
))
4048 if (linelen
== urilen
&& !strcmp(line
, uri
))
4055 fprintf(f
, "\n%s\n%s", title
, uri
);
4061 update_favorite_tabs(NULL
);
4067 can_go_back_for_real(struct tab
*t
)
4070 WebKitWebHistoryItem
*item
;
4076 /* rely on webkit to make sure we can go backward when on an about page */
4078 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4079 return (webkit_web_view_can_go_back(t
->wv
));
4081 /* the back/forwars list is stupid so help determine if we can go back */
4082 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4084 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4085 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4093 can_go_forward_for_real(struct tab
*t
)
4096 WebKitWebHistoryItem
*item
;
4102 /* rely on webkit to make sure we can go forward when on an about page */
4104 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4105 return (webkit_web_view_can_go_forward(t
->wv
));
4107 /* the back/forwars list is stupid so help selecting a different item */
4108 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4110 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4111 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4119 go_back_for_real(struct tab
*t
)
4122 WebKitWebHistoryItem
*item
;
4130 webkit_web_view_go_back(t
->wv
);
4133 /* the back/forwars list is stupid so help selecting a different item */
4134 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4136 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4137 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4138 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4145 go_forward_for_real(struct tab
*t
)
4148 WebKitWebHistoryItem
*item
;
4156 webkit_web_view_go_forward(t
->wv
);
4159 /* the back/forwars list is stupid so help selecting a different item */
4160 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4162 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4163 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4164 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4171 navaction(struct tab
*t
, struct karg
*args
)
4173 WebKitWebHistoryItem
*item
;
4174 WebKitWebFrame
*frame
;
4176 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4177 t
->tab_id
, args
->i
);
4179 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4181 if (args
->i
== XT_NAV_BACK
)
4182 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4184 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4186 return (XT_CB_PASSTHROUGH
);
4187 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4189 return (XT_CB_PASSTHROUGH
);
4195 go_back_for_real(t
);
4197 case XT_NAV_FORWARD
:
4199 go_forward_for_real(t
);
4202 frame
= webkit_web_view_get_main_frame(t
->wv
);
4203 webkit_web_frame_reload(frame
);
4206 return (XT_CB_PASSTHROUGH
);
4210 move(struct tab
*t
, struct karg
*args
)
4212 GtkAdjustment
*adjust
;
4213 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4219 case XT_MOVE_BOTTOM
:
4221 case XT_MOVE_PAGEDOWN
:
4222 case XT_MOVE_PAGEUP
:
4223 case XT_MOVE_HALFDOWN
:
4224 case XT_MOVE_HALFUP
:
4225 case XT_MOVE_PERCENT
:
4226 adjust
= t
->adjust_v
;
4229 adjust
= t
->adjust_h
;
4233 pos
= gtk_adjustment_get_value(adjust
);
4234 ps
= gtk_adjustment_get_page_size(adjust
);
4235 upper
= gtk_adjustment_get_upper(adjust
);
4236 lower
= gtk_adjustment_get_lower(adjust
);
4237 si
= gtk_adjustment_get_step_increment(adjust
);
4238 pi
= gtk_adjustment_get_page_increment(adjust
);
4241 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4242 "max %f si %f pi %f\n",
4243 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4244 pos
, ps
, upper
, lower
, max
, si
, pi
);
4250 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4255 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4257 case XT_MOVE_BOTTOM
:
4258 case XT_MOVE_FARRIGHT
:
4259 gtk_adjustment_set_value(adjust
, max
);
4262 case XT_MOVE_FARLEFT
:
4263 gtk_adjustment_set_value(adjust
, lower
);
4265 case XT_MOVE_PAGEDOWN
:
4267 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4269 case XT_MOVE_PAGEUP
:
4271 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4273 case XT_MOVE_HALFDOWN
:
4275 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4277 case XT_MOVE_HALFUP
:
4279 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4281 case XT_MOVE_PERCENT
:
4282 percent
= atoi(args
->s
) / 100.0;
4283 pos
= max
* percent
;
4284 if (pos
< 0.0 || pos
> max
)
4286 gtk_adjustment_set_value(adjust
, pos
);
4289 return (XT_CB_PASSTHROUGH
);
4292 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4294 return (XT_CB_HANDLED
);
4298 url_set_visibility(void)
4302 TAILQ_FOREACH(t
, &tabs
, entry
)
4303 if (show_url
== 0) {
4304 gtk_widget_hide(t
->toolbar
);
4307 gtk_widget_show(t
->toolbar
);
4311 notebook_tab_set_visibility(void)
4313 if (show_tabs
== 0) {
4314 gtk_widget_hide(tab_bar
);
4315 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4317 if (tab_style
== XT_TABS_NORMAL
) {
4318 gtk_widget_hide(tab_bar
);
4319 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4320 } else if (tab_style
== XT_TABS_COMPACT
) {
4321 gtk_widget_show(tab_bar
);
4322 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4328 statusbar_set_visibility(void)
4332 TAILQ_FOREACH(t
, &tabs
, entry
)
4333 if (show_statusbar
== 0) {
4334 gtk_widget_hide(t
->statusbar_box
);
4337 gtk_widget_show(t
->statusbar_box
);
4341 url_set(struct tab
*t
, int enable_url_entry
)
4346 show_url
= enable_url_entry
;
4348 if (enable_url_entry
) {
4349 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4350 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4351 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4353 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4354 GTK_ENTRY_ICON_PRIMARY
);
4356 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4357 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4358 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4359 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4365 fullscreen(struct tab
*t
, struct karg
*args
)
4367 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4370 return (XT_CB_PASSTHROUGH
);
4372 if (show_url
== 0) {
4380 url_set_visibility();
4381 notebook_tab_set_visibility();
4383 return (XT_CB_HANDLED
);
4387 statustoggle(struct tab
*t
, struct karg
*args
)
4389 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4391 if (show_statusbar
== 1) {
4393 statusbar_set_visibility();
4394 } else if (show_statusbar
== 0) {
4396 statusbar_set_visibility();
4398 return (XT_CB_HANDLED
);
4402 urlaction(struct tab
*t
, struct karg
*args
)
4404 int rv
= XT_CB_HANDLED
;
4406 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4409 return (XT_CB_PASSTHROUGH
);
4413 if (show_url
== 0) {
4415 url_set_visibility();
4419 if (show_url
== 1) {
4421 url_set_visibility();
4429 tabaction(struct tab
*t
, struct karg
*args
)
4431 int rv
= XT_CB_HANDLED
;
4432 char *url
= args
->s
;
4436 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4439 return (XT_CB_PASSTHROUGH
);
4443 if (strlen(url
) > 0)
4444 create_new_tab(url
, NULL
, 1, args
->precount
);
4446 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4449 if (args
->precount
< 0)
4452 TAILQ_FOREACH(tt
, &tabs
, entry
)
4453 if (tt
->tab_id
== args
->precount
- 1) {
4458 case XT_TAB_DELQUIT
:
4459 if (gtk_notebook_get_n_pages(notebook
) > 1)
4465 if (strlen(url
) > 0)
4468 rv
= XT_CB_PASSTHROUGH
;
4474 if (show_tabs
== 0) {
4476 notebook_tab_set_visibility();
4480 if (show_tabs
== 1) {
4482 notebook_tab_set_visibility();
4485 case XT_TAB_NEXTSTYLE
:
4486 if (tab_style
== XT_TABS_NORMAL
) {
4487 tab_style
= XT_TABS_COMPACT
;
4488 recolor_compact_tabs();
4491 tab_style
= XT_TABS_NORMAL
;
4492 notebook_tab_set_visibility();
4494 case XT_TAB_UNDO_CLOSE
:
4495 if (undo_count
== 0) {
4496 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4501 u
= TAILQ_FIRST(&undos
);
4502 create_new_tab(u
->uri
, u
, 1, -1);
4504 TAILQ_REMOVE(&undos
, u
, entry
);
4506 /* u->history is freed in create_new_tab() */
4511 rv
= XT_CB_PASSTHROUGH
;
4525 resizetab(struct tab
*t
, struct karg
*args
)
4527 if (t
== NULL
|| args
== NULL
) {
4528 show_oops(NULL
, "resizetab invalid parameters");
4529 return (XT_CB_PASSTHROUGH
);
4532 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4533 t
->tab_id
, args
->i
);
4535 setzoom_webkit(t
, args
->i
);
4537 return (XT_CB_HANDLED
);
4541 movetab(struct tab
*t
, struct karg
*args
)
4545 if (t
== NULL
|| args
== NULL
) {
4546 show_oops(NULL
, "movetab invalid parameters");
4547 return (XT_CB_PASSTHROUGH
);
4550 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4551 t
->tab_id
, args
->i
);
4553 if (args
->i
>= XT_TAB_INVALID
)
4554 return (XT_CB_PASSTHROUGH
);
4556 if (TAILQ_EMPTY(&tabs
))
4557 return (XT_CB_PASSTHROUGH
);
4559 n
= gtk_notebook_get_n_pages(notebook
);
4560 dest
= gtk_notebook_get_current_page(notebook
);
4564 if (args
->precount
< 0)
4565 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4567 dest
= args
->precount
- 1;
4571 if (args
->precount
< 0)
4574 dest
-= args
->precount
% n
;
4587 return (XT_CB_PASSTHROUGH
);
4590 if (dest
< 0 || dest
>= n
)
4591 return (XT_CB_PASSTHROUGH
);
4592 if (t
->tab_id
== dest
) {
4593 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4594 return (XT_CB_HANDLED
);
4597 set_current_tab(dest
);
4599 return (XT_CB_HANDLED
);
4605 command(struct tab
*t
, struct karg
*args
)
4607 char *s
= NULL
, *ss
= NULL
;
4611 if (t
== NULL
|| args
== NULL
) {
4612 show_oops(NULL
, "command invalid parameters");
4613 return (XT_CB_PASSTHROUGH
);
4624 if (cmd_prefix
== 0)
4627 ss
= g_strdup_printf(":%d", cmd_prefix
);
4638 case XT_CMD_OPEN_CURRENT
:
4641 case XT_CMD_TABNEW_CURRENT
:
4642 if (!s
) /* FALL THROUGH? */
4644 if ((uri
= get_uri(t
)) != NULL
) {
4645 ss
= g_strdup_printf("%s%s", s
, uri
);
4650 show_oops(t
, "command: invalid opcode %d", args
->i
);
4651 return (XT_CB_PASSTHROUGH
);
4654 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4656 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4657 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4658 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4660 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4661 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4666 return (XT_CB_HANDLED
);
4670 * Return a new string with a download row (in html)
4671 * appended. Old string is freed.
4674 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4677 WebKitDownloadStatus stat
;
4678 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4680 char cur_sz
[FMT_SCALED_STRSIZE
];
4681 char tot_sz
[FMT_SCALED_STRSIZE
];
4684 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4686 /* All actions wil take this form:
4687 * xxxt://class/seskey
4689 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4690 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4692 stat
= webkit_download_get_status(dl
->download
);
4695 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4696 status_html
= g_strdup_printf("Finished");
4697 cmd_html
= g_strdup_printf(
4698 "<a href='%s%d/%d'>Remove</a>",
4699 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4701 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4702 /* gather size info */
4703 progress
= 100 * webkit_download_get_progress(dl
->download
);
4706 webkit_download_get_current_size(dl
->download
), cur_sz
);
4708 webkit_download_get_total_size(dl
->download
), tot_sz
);
4710 status_html
= g_strdup_printf(
4711 "<div style='width: 100%%' align='center'>"
4712 "<div class='progress-outer'>"
4713 "<div class='progress-inner' style='width: %.2f%%'>"
4714 "</div></div></div>"
4715 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4716 progress
, cur_sz
, tot_sz
, progress
);
4718 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4719 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4723 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4724 status_html
= g_strdup_printf("Cancelled");
4725 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4726 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4728 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4729 status_html
= g_strdup_printf("Error!");
4730 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4731 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4733 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4734 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4735 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4736 status_html
= g_strdup_printf("Starting");
4739 show_oops(t
, "%s: unknown download status", __func__
);
4742 new_html
= g_strdup_printf(
4743 "%s\n<tr><td>%s</td><td>%s</td>"
4744 "<td style='text-align:center'>%s</td></tr>\n",
4745 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4746 status_html
, cmd_html
);
4750 g_free(status_html
);
4761 * update all download tabs apart from one. Pass NULL if
4762 * you want to update all.
4765 update_download_tabs(struct tab
*apart_from
)
4768 if (!updating_dl_tabs
) {
4769 updating_dl_tabs
= 1; /* stop infinite recursion */
4770 TAILQ_FOREACH(t
, &tabs
, entry
)
4771 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4772 && (t
!= apart_from
))
4773 xtp_page_dl(t
, NULL
);
4774 updating_dl_tabs
= 0;
4779 * update all cookie tabs apart from one. Pass NULL if
4780 * you want to update all.
4783 update_cookie_tabs(struct tab
*apart_from
)
4786 if (!updating_cl_tabs
) {
4787 updating_cl_tabs
= 1; /* stop infinite recursion */
4788 TAILQ_FOREACH(t
, &tabs
, entry
)
4789 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4790 && (t
!= apart_from
))
4791 xtp_page_cl(t
, NULL
);
4792 updating_cl_tabs
= 0;
4797 * update all history tabs apart from one. Pass NULL if
4798 * you want to update all.
4801 update_history_tabs(struct tab
*apart_from
)
4805 if (!updating_hl_tabs
) {
4806 updating_hl_tabs
= 1; /* stop infinite recursion */
4807 TAILQ_FOREACH(t
, &tabs
, entry
)
4808 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4809 && (t
!= apart_from
))
4810 xtp_page_hl(t
, NULL
);
4811 updating_hl_tabs
= 0;
4815 /* cookie management XTP page */
4817 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4819 char *body
, *page
, *tmp
;
4820 int i
= 1; /* all ids start 1 */
4821 GSList
*sc
, *pc
, *pc_start
;
4823 char *type
, *table_headers
, *last_domain
;
4825 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4828 show_oops(NULL
, "%s invalid parameters", __func__
);
4832 /* Generate a new session key */
4833 if (!updating_cl_tabs
)
4834 generate_xtp_session_key(&cl_session_key
);
4837 table_headers
= g_strdup_printf("<table><tr>"
4840 "<th style='width:200px'>Value</th>"
4844 "<th>HTTP<br />only</th>"
4845 "<th style='width:40px'>Rm</th></tr>\n");
4847 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4848 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4852 last_domain
= strdup("");
4853 for (; sc
; sc
= sc
->next
) {
4856 if (strcmp(last_domain
, c
->domain
) != 0) {
4859 last_domain
= strdup(c
->domain
);
4863 body
= g_strdup_printf("%s</table>"
4865 body
, c
->domain
, table_headers
);
4869 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4870 c
->domain
, table_headers
);
4875 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4876 if (soup_cookie_equal(pc
->data
, c
)) {
4877 type
= "Session + Persistent";
4882 body
= g_strdup_printf(
4885 "<td style='word-wrap:normal'>%s</td>"
4887 " <textarea rows='4'>%s</textarea>"
4893 "<td style='text-align:center'>"
4894 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4901 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4916 soup_cookies_free(sc
);
4917 soup_cookies_free(pc
);
4919 /* small message if there are none */
4921 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4922 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4925 body
= g_strdup_printf("%s</table>", body
);
4928 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4930 g_free(table_headers
);
4931 g_free(last_domain
);
4933 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4934 update_cookie_tabs(t
);
4942 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4944 char *body
, *page
, *tmp
;
4946 int i
= 1; /* all ids start 1 */
4948 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4951 show_oops(NULL
, "%s invalid parameters", __func__
);
4955 /* Generate a new session key */
4956 if (!updating_hl_tabs
)
4957 generate_xtp_session_key(&hl_session_key
);
4960 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4961 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4963 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4965 body
= g_strdup_printf(
4967 "<td><a href='%s'>%s</a></td>"
4969 "<td style='text-align: center'>"
4970 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4971 body
, h
->uri
, h
->uri
, h
->title
,
4972 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4973 XT_XTP_HL_REMOVE
, i
);
4979 /* small message if there are none */
4982 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4983 "colspan='3'>No History</td></tr>\n", body
);
4988 body
= g_strdup_printf("%s</table>", body
);
4991 page
= get_html_page("History", body
, "", TRUE
);
4995 * update all history manager tabs as the xtp session
4996 * key has now changed. No need to update the current tab.
4997 * Already did that above.
4999 update_history_tabs(t
);
5001 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5008 * Generate a web page detailing the status of any downloads
5011 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5013 struct download
*dl
;
5014 char *body
, *page
, *tmp
;
5018 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5021 show_oops(NULL
, "%s invalid parameters", __func__
);
5026 * Generate a new session key for next page instance.
5027 * This only happens for the top level call to xtp_page_dl()
5028 * in which case updating_dl_tabs is 0.
5030 if (!updating_dl_tabs
)
5031 generate_xtp_session_key(&dl_session_key
);
5033 /* header - with refresh so as to update */
5034 if (refresh_interval
>= 1)
5035 ref
= g_strdup_printf(
5036 "<meta http-equiv='refresh' content='%u"
5037 ";url=%s%d/%s/%d' />\n",
5046 body
= g_strdup_printf("<div align='center'>"
5047 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5048 "</p><table><tr><th style='width: 60%%'>"
5049 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5050 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5052 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5053 body
= xtp_page_dl_row(t
, body
, dl
);
5057 /* message if no downloads in list */
5060 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5061 " style='text-align: center'>"
5062 "No downloads</td></tr>\n", body
);
5067 body
= g_strdup_printf("%s</table></div>", body
);
5070 page
= get_html_page("Downloads", body
, ref
, 1);
5075 * update all download manager tabs as the xtp session
5076 * key has now changed. No need to update the current tab.
5077 * Already did that above.
5079 update_download_tabs(t
);
5081 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5088 search(struct tab
*t
, struct karg
*args
)
5092 if (t
== NULL
|| args
== NULL
) {
5093 show_oops(NULL
, "search invalid parameters");
5098 case XT_SEARCH_NEXT
:
5099 d
= t
->search_forward
;
5101 case XT_SEARCH_PREV
:
5102 d
= !t
->search_forward
;
5105 return (XT_CB_PASSTHROUGH
);
5108 if (t
->search_text
== NULL
) {
5109 if (global_search
== NULL
)
5110 return (XT_CB_PASSTHROUGH
);
5112 d
= t
->search_forward
= TRUE
;
5113 t
->search_text
= g_strdup(global_search
);
5114 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5115 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5119 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5120 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5122 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5124 return (XT_CB_HANDLED
);
5127 struct settings_args
{
5133 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5136 struct settings_args
*sa
= cb_args
;
5141 if (s
->flags
& XT_SF_RUNTIME
)
5147 *sa
->body
= g_strdup_printf(
5149 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5150 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5162 set_show(struct tab
*t
, struct karg
*args
)
5164 char *body
, *page
, *tmp
;
5166 struct settings_args sa
;
5168 bzero(&sa
, sizeof sa
);
5172 body
= g_strdup_printf("<div align='center'><table><tr>"
5173 "<th align='left'>Setting</th>"
5174 "<th align='left'>Value</th></tr>\n");
5176 settings_walk(print_setting
, &sa
);
5179 /* small message if there are none */
5182 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5183 "colspan='2'>No settings</td></tr>\n", body
);
5188 body
= g_strdup_printf("%s</table></div>", body
);
5191 page
= get_html_page("Settings", body
, "", 0);
5195 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5199 return (XT_CB_PASSTHROUGH
);
5203 set(struct tab
*t
, struct karg
*args
)
5208 if (args
== NULL
|| args
->s
== NULL
)
5209 return (set_show(t
, args
));
5212 p
= g_strstrip(args
->s
);
5215 return (set_show(t
, args
));
5217 /* we got some sort of string */
5218 val
= g_strrstr(p
, "=");
5221 val
= g_strchomp(val
);
5224 for (i
= 0; i
< LENGTH(rs
); i
++) {
5225 if (strcmp(rs
[i
].name
, p
))
5228 if (rs
[i
].activate
) {
5229 if (rs
[i
].activate(val
))
5230 show_oops(t
, "%s invalid value %s",
5233 show_oops(t
, ":set %s = %s", p
, val
);
5236 show_oops(t
, "not a runtime option: %s", p
);
5240 show_oops(t
, "unknown option: %s", p
);
5244 for (i
= 0; i
< LENGTH(rs
); i
++) {
5245 if (strcmp(rs
[i
].name
, p
))
5248 /* XXX this could use some cleanup */
5249 switch (rs
[i
].type
) {
5252 show_oops(t
, "%s = %d",
5253 rs
[i
].name
, *rs
[i
].ival
);
5254 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5255 show_oops(t
, "%s = %s",
5257 rs
[i
].s
->get(&rs
[i
]));
5258 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5259 show_oops(t
, "%s = ...", rs
[i
].name
);
5261 show_oops(t
, "%s = ", rs
[i
].name
);
5265 show_oops(t
, "%s = %f",
5266 rs
[i
].name
, *rs
[i
].fval
);
5267 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5268 show_oops(t
, "%s = %s",
5270 rs
[i
].s
->get(&rs
[i
]));
5271 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5272 show_oops(t
, "%s = ...", rs
[i
].name
);
5274 show_oops(t
, "%s = ", rs
[i
].name
);
5277 if (rs
[i
].sval
&& *rs
[i
].sval
)
5278 show_oops(t
, "%s = %s",
5279 rs
[i
].name
, *rs
[i
].sval
);
5280 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5281 show_oops(t
, "%s = %s",
5283 rs
[i
].s
->get(&rs
[i
]));
5284 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5285 show_oops(t
, "%s = ...", rs
[i
].name
);
5287 show_oops(t
, "%s = ", rs
[i
].name
);
5290 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5296 show_oops(t
, "unknown option: %s", p
);
5299 return (XT_CB_PASSTHROUGH
);
5303 session_save(struct tab
*t
, char *filename
)
5309 if (strlen(filename
) == 0)
5312 if (filename
[0] == '.' || filename
[0] == '/')
5316 if (save_tabs(t
, &a
))
5318 strlcpy(named_session
, filename
, sizeof named_session
);
5320 /* add the new session to the list of sessions */
5321 s
= g_malloc(sizeof(struct session
));
5322 s
->name
= g_strdup(filename
);
5323 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5331 session_open(struct tab
*t
, char *filename
)
5336 if (strlen(filename
) == 0)
5339 if (filename
[0] == '.' || filename
[0] == '/')
5343 a
.i
= XT_SES_CLOSETABS
;
5344 if (open_tabs(t
, &a
))
5347 strlcpy(named_session
, filename
, sizeof named_session
);
5355 session_delete(struct tab
*t
, char *filename
)
5357 char file
[PATH_MAX
];
5361 if (strlen(filename
) == 0)
5364 if (filename
[0] == '.' || filename
[0] == '/')
5367 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5371 if (!strcmp(filename
, named_session
))
5372 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5373 sizeof named_session
);
5375 /* remove session from sessions list */
5376 TAILQ_FOREACH(s
, &sessions
, entry
) {
5377 if (!strcmp(s
->name
, filename
))
5380 TAILQ_REMOVE(&sessions
, s
, entry
);
5381 g_free((gpointer
) s
->name
);
5390 session_cmd(struct tab
*t
, struct karg
*args
)
5392 char *filename
= args
->s
;
5397 if (args
->i
& XT_SHOW
)
5398 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5399 XT_SAVED_TABS_FILE
: named_session
);
5400 else if (args
->i
& XT_SAVE
) {
5401 if (session_save(t
, filename
)) {
5402 show_oops(t
, "Can't save session: %s",
5403 filename
? filename
: "INVALID");
5406 } else if (args
->i
& XT_OPEN
) {
5407 if (session_open(t
, filename
)) {
5408 show_oops(t
, "Can't open session: %s",
5409 filename
? filename
: "INVALID");
5412 } else if (args
->i
& XT_DELETE
) {
5413 if (session_delete(t
, filename
)) {
5414 show_oops(t
, "Can't delete session: %s",
5415 filename
? filename
: "INVALID");
5420 return (XT_CB_PASSTHROUGH
);
5424 * Make a hardcopy of the page
5427 print_page(struct tab
*t
, struct karg
*args
)
5429 WebKitWebFrame
*frame
;
5431 GtkPrintOperation
*op
;
5432 GtkPrintOperationAction action
;
5433 GtkPrintOperationResult print_res
;
5434 GError
*g_err
= NULL
;
5435 int marg_l
, marg_r
, marg_t
, marg_b
;
5437 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5439 ps
= gtk_page_setup_new();
5440 op
= gtk_print_operation_new();
5441 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5442 frame
= webkit_web_view_get_main_frame(t
->wv
);
5444 /* the default margins are too small, so we will bump them */
5445 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5446 XT_PRINT_EXTRA_MARGIN
;
5447 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5448 XT_PRINT_EXTRA_MARGIN
;
5449 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5450 XT_PRINT_EXTRA_MARGIN
;
5451 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5452 XT_PRINT_EXTRA_MARGIN
;
5455 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5456 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5457 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5458 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5460 gtk_print_operation_set_default_page_setup(op
, ps
);
5462 /* this appears to free 'op' and 'ps' */
5463 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5465 /* check it worked */
5466 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5467 show_oops(NULL
, "can't print: %s", g_err
->message
);
5468 g_error_free (g_err
);
5476 go_home(struct tab
*t
, struct karg
*args
)
5483 set_encoding(struct tab
*t
, struct karg
*args
)
5487 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5488 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5490 e
= webkit_web_view_get_encoding(t
->wv
);
5491 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5493 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5499 restart(struct tab
*t
, struct karg
*args
)
5503 a
.s
= XT_RESTART_TABS_FILE
;
5505 execvp(start_argv
[0], start_argv
);
5511 #define CTRL GDK_CONTROL_MASK
5512 #define MOD1 GDK_MOD1_MASK
5513 #define SHFT GDK_SHIFT_MASK
5515 /* inherent to GTK not all keys will be caught at all times */
5516 /* XXX sort key bindings */
5517 struct key_binding
{
5522 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5524 { "cookiejar", MOD1
, 0, GDK_j
},
5525 { "downloadmgr", MOD1
, 0, GDK_d
},
5526 { "history", MOD1
, 0, GDK_h
},
5527 { "print", CTRL
, 0, GDK_p
},
5528 { "search", 0, 0, GDK_slash
},
5529 { "searchb", 0, 0, GDK_question
},
5530 { "statustoggle", CTRL
, 0, GDK_n
},
5531 { "command", 0, 0, GDK_colon
},
5532 { "qa", CTRL
, 0, GDK_q
},
5533 { "restart", MOD1
, 0, GDK_q
},
5534 { "js toggle", CTRL
, 0, GDK_j
},
5535 { "cookie toggle", MOD1
, 0, GDK_c
},
5536 { "togglesrc", CTRL
, 0, GDK_s
},
5537 { "yankuri", 0, 0, GDK_y
},
5538 { "pasteuricur", 0, 0, GDK_p
},
5539 { "pasteurinew", 0, 0, GDK_P
},
5540 { "toplevel toggle", 0, 0, GDK_F4
},
5541 { "help", 0, 0, GDK_F1
},
5542 { "run_script", MOD1
, 0, GDK_r
},
5545 { "searchnext", 0, 0, GDK_n
},
5546 { "searchprevious", 0, 0, GDK_N
},
5549 { "focusaddress", 0, 0, GDK_F6
},
5550 { "focussearch", 0, 0, GDK_F7
},
5553 { "hinting", 0, 0, GDK_f
},
5555 /* custom stylesheet */
5556 { "userstyle", 0, 0, GDK_i
},
5559 { "goback", 0, 0, GDK_BackSpace
},
5560 { "goback", MOD1
, 0, GDK_Left
},
5561 { "goforward", SHFT
, 0, GDK_BackSpace
},
5562 { "goforward", MOD1
, 0, GDK_Right
},
5563 { "reload", 0, 0, GDK_F5
},
5564 { "reload", CTRL
, 0, GDK_r
},
5565 { "reload", CTRL
, 0, GDK_l
},
5566 { "favorites", MOD1
, 1, GDK_f
},
5568 /* vertical movement */
5569 { "scrolldown", 0, 0, GDK_j
},
5570 { "scrolldown", 0, 0, GDK_Down
},
5571 { "scrollup", 0, 0, GDK_Up
},
5572 { "scrollup", 0, 0, GDK_k
},
5573 { "scrollbottom", 0, 0, GDK_G
},
5574 { "scrollbottom", 0, 0, GDK_End
},
5575 { "scrolltop", 0, 0, GDK_Home
},
5576 { "scrollpagedown", 0, 0, GDK_space
},
5577 { "scrollpagedown", CTRL
, 0, GDK_f
},
5578 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5579 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5580 { "scrollpageup", 0, 0, GDK_Page_Up
},
5581 { "scrollpageup", CTRL
, 0, GDK_b
},
5582 { "scrollhalfup", CTRL
, 0, GDK_u
},
5583 /* horizontal movement */
5584 { "scrollright", 0, 0, GDK_l
},
5585 { "scrollright", 0, 0, GDK_Right
},
5586 { "scrollleft", 0, 0, GDK_Left
},
5587 { "scrollleft", 0, 0, GDK_h
},
5588 { "scrollfarright", 0, 0, GDK_dollar
},
5589 { "scrollfarleft", 0, 0, GDK_0
},
5592 { "tabnew", CTRL
, 0, GDK_t
},
5593 { "999tabnew", CTRL
, 0, GDK_T
},
5594 { "tabclose", CTRL
, 1, GDK_w
},
5595 { "tabundoclose", 0, 0, GDK_U
},
5596 { "tabnext 1", CTRL
, 0, GDK_1
},
5597 { "tabnext 2", CTRL
, 0, GDK_2
},
5598 { "tabnext 3", CTRL
, 0, GDK_3
},
5599 { "tabnext 4", CTRL
, 0, GDK_4
},
5600 { "tabnext 5", CTRL
, 0, GDK_5
},
5601 { "tabnext 6", CTRL
, 0, GDK_6
},
5602 { "tabnext 7", CTRL
, 0, GDK_7
},
5603 { "tabnext 8", CTRL
, 0, GDK_8
},
5604 { "tabnext 9", CTRL
, 0, GDK_9
},
5605 { "tabfirst", CTRL
, 0, GDK_less
},
5606 { "tablast", CTRL
, 0, GDK_greater
},
5607 { "tabprevious", CTRL
, 0, GDK_Left
},
5608 { "tabnext", CTRL
, 0, GDK_Right
},
5609 { "focusout", CTRL
, 0, GDK_minus
},
5610 { "focusin", CTRL
, 0, GDK_plus
},
5611 { "focusin", CTRL
, 0, GDK_equal
},
5612 { "focusreset", CTRL
, 0, GDK_0
},
5614 /* command aliases (handy when -S flag is used) */
5615 { "promptopen", 0, 0, GDK_F9
},
5616 { "promptopencurrent", 0, 0, GDK_F10
},
5617 { "prompttabnew", 0, 0, GDK_F11
},
5618 { "prompttabnewcurrent",0, 0, GDK_F12
},
5620 TAILQ_HEAD(keybinding_list
, key_binding
);
5623 walk_kb(struct settings
*s
,
5624 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5626 struct key_binding
*k
;
5629 if (s
== NULL
|| cb
== NULL
) {
5630 show_oops(NULL
, "walk_kb invalid parameters");
5634 TAILQ_FOREACH(k
, &kbl
, entry
) {
5640 if (gdk_keyval_name(k
->key
) == NULL
)
5643 strlcat(str
, k
->cmd
, sizeof str
);
5644 strlcat(str
, ",", sizeof str
);
5646 if (k
->mask
& GDK_SHIFT_MASK
)
5647 strlcat(str
, "S-", sizeof str
);
5648 if (k
->mask
& GDK_CONTROL_MASK
)
5649 strlcat(str
, "C-", sizeof str
);
5650 if (k
->mask
& GDK_MOD1_MASK
)
5651 strlcat(str
, "M1-", sizeof str
);
5652 if (k
->mask
& GDK_MOD2_MASK
)
5653 strlcat(str
, "M2-", sizeof str
);
5654 if (k
->mask
& GDK_MOD3_MASK
)
5655 strlcat(str
, "M3-", sizeof str
);
5656 if (k
->mask
& GDK_MOD4_MASK
)
5657 strlcat(str
, "M4-", sizeof str
);
5658 if (k
->mask
& GDK_MOD5_MASK
)
5659 strlcat(str
, "M5-", sizeof str
);
5661 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5662 cb(s
, str
, cb_args
);
5667 init_keybindings(void)
5670 struct key_binding
*k
;
5672 for (i
= 0; i
< LENGTH(keys
); i
++) {
5673 k
= g_malloc0(sizeof *k
);
5674 k
->cmd
= keys
[i
].cmd
;
5675 k
->mask
= keys
[i
].mask
;
5676 k
->use_in_entry
= keys
[i
].use_in_entry
;
5677 k
->key
= keys
[i
].key
;
5678 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5680 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5681 k
->cmd
? k
->cmd
: "unnamed key");
5686 keybinding_clearall(void)
5688 struct key_binding
*k
, *next
;
5690 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5691 next
= TAILQ_NEXT(k
, entry
);
5695 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5696 k
->cmd
? k
->cmd
: "unnamed key");
5697 TAILQ_REMOVE(&kbl
, k
, entry
);
5703 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5705 struct key_binding
*k
;
5706 guint keyval
, mask
= 0;
5709 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5711 /* Keys which are to be used in entry have been prefixed with an
5712 * exclamation mark. */
5716 /* find modifier keys */
5717 if (strstr(key
, "S-"))
5718 mask
|= GDK_SHIFT_MASK
;
5719 if (strstr(key
, "C-"))
5720 mask
|= GDK_CONTROL_MASK
;
5721 if (strstr(key
, "M1-"))
5722 mask
|= GDK_MOD1_MASK
;
5723 if (strstr(key
, "M2-"))
5724 mask
|= GDK_MOD2_MASK
;
5725 if (strstr(key
, "M3-"))
5726 mask
|= GDK_MOD3_MASK
;
5727 if (strstr(key
, "M4-"))
5728 mask
|= GDK_MOD4_MASK
;
5729 if (strstr(key
, "M5-"))
5730 mask
|= GDK_MOD5_MASK
;
5733 for (i
= strlen(key
) - 1; i
> 0; i
--)
5737 /* validate keyname */
5738 keyval
= gdk_keyval_from_name(key
);
5739 if (keyval
== GDK_VoidSymbol
) {
5740 warnx("invalid keybinding name %s", key
);
5743 /* must run this test too, gtk+ doesn't handle 10 for example */
5744 if (gdk_keyval_name(keyval
) == NULL
) {
5745 warnx("invalid keybinding name %s", key
);
5749 /* Remove eventual dupes. */
5750 TAILQ_FOREACH(k
, &kbl
, entry
)
5751 if (k
->key
== keyval
&& k
->mask
== mask
) {
5752 TAILQ_REMOVE(&kbl
, k
, entry
);
5758 k
= g_malloc0(sizeof *k
);
5759 k
->cmd
= g_strdup(cmd
);
5761 k
->use_in_entry
= use_in_entry
;
5764 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5769 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5770 k
->cmd
, gdk_keyval_name(keyval
));
5772 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5778 add_kb(struct settings
*s
, char *entry
)
5782 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5784 /* clearall is special */
5785 if (!strcmp(entry
, "clearall")) {
5786 keybinding_clearall();
5790 kb
= strstr(entry
, ",");
5796 return (keybinding_add(entry
, key
, key
[0] == '!'));
5802 int (*func
)(struct tab
*, struct karg
*);
5806 { "command", 0, command
, ':', 0 },
5807 { "search", 0, command
, '/', 0 },
5808 { "searchb", 0, command
, '?', 0 },
5809 { "togglesrc", 0, toggle_src
, 0, 0 },
5811 /* yanking and pasting */
5812 { "yankuri", 0, yank_uri
, 0, 0 },
5813 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5814 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5815 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5818 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5819 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5822 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5823 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5826 { "hinting", 0, hint
, 0, 0 },
5828 /* custom stylesheet */
5829 { "userstyle", 0, userstyle
, 0, 0 },
5832 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5833 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5834 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5836 /* vertical movement */
5837 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5838 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5839 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5840 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5841 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5842 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5843 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5844 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5845 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5846 /* horizontal movement */
5847 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5848 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5849 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5850 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5852 { "favorites", 0, xtp_page_fl
, 0, 0 },
5853 { "fav", 0, xtp_page_fl
, 0, 0 },
5854 { "favadd", 0, add_favorite
, 0, 0 },
5856 { "qall", 0, quit
, 0, 0 },
5857 { "quitall", 0, quit
, 0, 0 },
5858 { "w", 0, save_tabs
, 0, 0 },
5859 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5860 { "help", 0, help
, 0, 0 },
5861 { "about", 0, about
, 0, 0 },
5862 { "stats", 0, stats
, 0, 0 },
5863 { "version", 0, about
, 0, 0 },
5866 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5867 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5868 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5869 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5870 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5871 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5872 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5873 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5874 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5875 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5876 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5878 /* cookie command */
5879 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5880 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5881 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5882 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5883 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5884 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5885 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5886 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5887 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5888 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5889 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5891 /* toplevel (domain) command */
5892 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5893 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5896 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5899 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5900 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5901 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5903 { "ca", 0, ca_cmd
, 0, 0 },
5904 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5905 { "dl", 0, xtp_page_dl
, 0, 0 },
5906 { "h", 0, xtp_page_hl
, 0, 0 },
5907 { "history", 0, xtp_page_hl
, 0, 0 },
5908 { "home", 0, go_home
, 0, 0 },
5909 { "restart", 0, restart
, 0, 0 },
5910 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5911 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5912 { "statustoggle", 0, statustoggle
, 0, 0 },
5913 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5915 { "print", 0, print_page
, 0, 0 },
5918 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5919 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5920 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5921 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5922 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5923 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5924 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5925 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5926 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5927 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5928 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5929 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5930 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5931 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5932 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5933 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5934 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5935 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5936 { "buffers", 0, buffers
, 0, 0 },
5937 { "ls", 0, buffers
, 0, 0 },
5938 { "tabs", 0, buffers
, 0, 0 },
5939 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
5941 /* command aliases (handy when -S flag is used) */
5942 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5943 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5944 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5945 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5948 { "set", 0, set
, 0, XT_SETARG
},
5950 { "fullscreen", 0, fullscreen
, 0, 0 },
5951 { "f", 0, fullscreen
, 0, 0 },
5954 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5955 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
5956 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
5957 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5958 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5965 } cmd_status
= {-1, 0};
5968 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5971 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5978 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5985 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5987 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5993 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5995 a
.i
= XT_NAV_FORWARD
;
6005 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6007 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6009 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6016 * cancel, remove, etc. downloads
6019 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6021 struct download find
, *d
= NULL
;
6023 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6025 /* some commands require a valid download id */
6026 if (cmd
!= XT_XTP_DL_LIST
) {
6027 /* lookup download in question */
6029 d
= RB_FIND(download_list
, &downloads
, &find
);
6032 show_oops(t
, "%s: no such download", __func__
);
6037 /* decide what to do */
6039 case XT_XTP_DL_CANCEL
:
6040 webkit_download_cancel(d
->download
);
6042 case XT_XTP_DL_REMOVE
:
6043 webkit_download_cancel(d
->download
); /* just incase */
6044 g_object_unref(d
->download
);
6045 RB_REMOVE(download_list
, &downloads
, d
);
6047 case XT_XTP_DL_LIST
:
6051 show_oops(t
, "%s: unknown command", __func__
);
6054 xtp_page_dl(t
, NULL
);
6058 * Actions on history, only does one thing for now, but
6059 * we provide the function for future actions
6062 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6064 struct history
*h
, *next
;
6068 case XT_XTP_HL_REMOVE
:
6069 /* walk backwards, as listed in reverse */
6070 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6071 next
= RB_PREV(history_list
, &hl
, h
);
6073 RB_REMOVE(history_list
, &hl
, h
);
6074 g_free((gpointer
) h
->title
);
6075 g_free((gpointer
) h
->uri
);
6082 case XT_XTP_HL_LIST
:
6083 /* Nothing - just xtp_page_hl() below */
6086 show_oops(t
, "%s: unknown command", __func__
);
6090 xtp_page_hl(t
, NULL
);
6093 /* remove a favorite */
6095 remove_favorite(struct tab
*t
, int index
)
6097 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6098 char *new_favs
, *tmp
;
6103 /* open favorites */
6104 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6106 if ((f
= fopen(file
, "r")) == NULL
) {
6107 show_oops(t
, "%s: can't open favorites: %s",
6108 __func__
, strerror(errno
));
6112 /* build a string which will become the new favroites file */
6113 new_favs
= g_strdup("");
6116 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6117 if (feof(f
) || ferror(f
))
6119 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6126 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6127 if (feof(f
) || ferror(f
)) {
6128 show_oops(t
, "%s: can't parse favorites %s",
6129 __func__
, strerror(errno
));
6134 /* as long as this isn't the one we are deleting add to file */
6137 new_favs
= g_strdup_printf("%s%s\n%s\n",
6138 new_favs
, title
, uri
);
6150 /* write back new favorites file */
6151 if ((f
= fopen(file
, "w")) == NULL
) {
6152 show_oops(t
, "%s: can't open favorites: %s",
6153 __func__
, strerror(errno
));
6157 fwrite(new_favs
, strlen(new_favs
), 1, f
);
6170 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6173 case XT_XTP_FL_LIST
:
6174 /* nothing, just the below call to xtp_page_fl() */
6176 case XT_XTP_FL_REMOVE
:
6177 remove_favorite(t
, arg
);
6180 show_oops(t
, "%s: invalid favorites command", __func__
);
6184 xtp_page_fl(t
, NULL
);
6188 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6191 case XT_XTP_CL_LIST
:
6192 /* nothing, just xtp_page_cl() */
6194 case XT_XTP_CL_REMOVE
:
6198 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6202 xtp_page_cl(t
, NULL
);
6205 /* link an XTP class to it's session key and handler function */
6206 struct xtp_despatch
{
6209 void (*handle_func
)(struct tab
*, uint8_t, int);
6212 struct xtp_despatch xtp_despatches
[] = {
6213 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6214 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6215 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6216 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6217 { XT_XTP_INVALID
, NULL
, NULL
}
6221 * is the url xtp protocol? (xxxt://)
6222 * if so, parse and despatch correct bahvior
6225 parse_xtp_url(struct tab
*t
, const char *url
)
6227 char *dup
= NULL
, *p
, *last
;
6228 uint8_t n_tokens
= 0;
6229 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6230 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6235 * tokens array meaning:
6237 * tokens[1] = session key
6238 * tokens[2] = action
6239 * tokens[3] = optional argument
6242 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6244 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6247 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6249 /* split out the url */
6250 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6251 (p
= strtok_r(NULL
, "/", &last
))) {
6253 tokens
[n_tokens
++] = p
;
6256 /* should be atleast three fields 'class/seskey/command/arg' */
6260 dsp
= xtp_despatches
;
6261 req_class
= atoi(tokens
[0]);
6262 while (dsp
->xtp_class
) {
6263 if (dsp
->xtp_class
== req_class
) {
6270 /* did we find one atall? */
6271 if (dsp_match
== NULL
) {
6272 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6276 /* check session key and call despatch function */
6277 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6278 ret
= TRUE
; /* all is well, this was a valid xtp request */
6279 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6292 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6294 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6296 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6299 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6304 show_oops(t
, "activate_uri_entry_cb no uri");
6308 uri
+= strspn(uri
, "\t ");
6310 /* if xxxt:// treat specially */
6311 if (parse_xtp_url(t
, uri
))
6314 /* otherwise continue to load page normally */
6315 load_uri(t
, (gchar
*)uri
);
6320 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6322 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6323 char *newuri
= NULL
;
6326 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6329 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6333 if (search_string
== NULL
) {
6334 show_oops(t
, "no search_string");
6338 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6340 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6341 newuri
= g_strdup_printf(search_string
, enc_search
);
6345 webkit_web_view_load_uri(t
->wv
, newuri
);
6353 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6355 struct domain
*d
= NULL
;
6358 if (uri
== NULL
|| t
== NULL
)
6361 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6366 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6367 es
? "enable" : "disable", uri
);
6369 g_object_set(G_OBJECT(t
->settings
),
6370 "enable-html5-local-storage", es
, (char *)NULL
);
6371 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6375 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6377 struct domain
*d
= NULL
;
6380 if (uri
== NULL
|| t
== NULL
)
6383 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6388 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6389 es
? "enable" : "disable", uri
);
6391 g_object_set(G_OBJECT(t
->settings
),
6392 "enable-scripts", es
, (char *)NULL
);
6393 g_object_set(G_OBJECT(t
->settings
),
6394 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6395 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6397 button_set_stockid(t
->js_toggle
,
6398 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6402 color_address_bar(gpointer p
)
6405 struct tab
*tt
, *t
= p
;
6406 gchar
*col_str
= XT_COLOR_YELLOW
;
6408 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6410 /* make sure t still exists */
6413 TAILQ_FOREACH(tt
, &tabs
, entry
)
6419 switch (load_compare_cert(t
, NULL
)) {
6421 col_str
= XT_COLOR_BLUE
;
6424 col_str
= XT_COLOR_GREEN
;
6426 case CERT_UNTRUSTED
:
6427 col_str
= XT_COLOR_YELLOW
;
6430 col_str
= XT_COLOR_RED
;
6434 gdk_color_parse(col_str
, &color
);
6435 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6437 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6438 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6440 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6448 show_ca_status(struct tab
*t
, const char *uri
)
6451 gchar
*col_str
= XT_COLOR_WHITE
;
6453 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6454 ssl_strict_certs
, ssl_ca_file
, uri
);
6461 if (ssl_ca_file
== NULL
) {
6462 if (g_str_has_prefix(uri
, "http://"))
6464 if (g_str_has_prefix(uri
, "https://")) {
6465 col_str
= XT_COLOR_RED
;
6470 if (g_str_has_prefix(uri
, "http://") ||
6471 !g_str_has_prefix(uri
, "https://"))
6474 color_address_bar(t
);
6480 gdk_color_parse(col_str
, &color
);
6481 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6483 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6484 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6486 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6491 free_favicon(struct tab
*t
)
6493 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6494 __func__
, t
->icon_download
, t
->icon_request
);
6496 if (t
->icon_request
)
6497 g_object_unref(t
->icon_request
);
6498 if (t
->icon_dest_uri
)
6499 g_free(t
->icon_dest_uri
);
6501 t
->icon_request
= NULL
;
6502 t
->icon_dest_uri
= NULL
;
6506 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6508 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6509 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6511 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6512 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6514 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6515 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6519 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6521 GdkPixbuf
*pb_scaled
;
6523 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6524 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6525 GDK_INTERP_BILINEAR
);
6529 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6530 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6532 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6533 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6535 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6536 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6538 if (pb_scaled
!= pb
)
6539 g_object_unref(pb_scaled
);
6543 xt_icon_from_file(struct tab
*t
, char *file
)
6547 if (g_str_has_prefix(file
, "file://"))
6548 file
+= strlen("file://");
6550 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6552 xt_icon_from_pixbuf(t
, pb
);
6555 xt_icon_from_name(t
, "text-html");
6559 is_valid_icon(char *file
)
6562 const char *mime_type
;
6566 gf
= g_file_new_for_path(file
);
6567 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6569 mime_type
= g_file_info_get_content_type(fi
);
6570 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6571 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6572 g_strcmp0(mime_type
, "image/png") == 0 ||
6573 g_strcmp0(mime_type
, "image/gif") == 0 ||
6574 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6582 set_favicon_from_file(struct tab
*t
, char *file
)
6586 if (t
== NULL
|| file
== NULL
)
6589 if (g_str_has_prefix(file
, "file://"))
6590 file
+= strlen("file://");
6591 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6593 if (!stat(file
, &sb
)) {
6594 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6595 /* corrupt icon so trash it */
6596 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6599 /* no need to set icon to default here */
6603 xt_icon_from_file(t
, file
);
6607 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6610 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6611 struct tab
*tt
= NULL
, *t
= NULL
;
6614 * find the webview instead of passing in the tab as it could have been
6615 * deleted from underneath us.
6617 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6626 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6627 __func__
, t
->tab_id
, status
);
6630 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6632 t
->icon_download
= NULL
;
6635 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6638 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6641 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6643 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6644 __func__
, t
->tab_id
);
6645 t
->icon_download
= NULL
;
6648 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6651 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6652 __func__
, t
->icon_dest_uri
);
6653 set_favicon_from_file(t
, t
->icon_dest_uri
);
6654 /* these will be freed post callback */
6655 t
->icon_request
= NULL
;
6656 t
->icon_download
= NULL
;
6664 abort_favicon_download(struct tab
*t
)
6666 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6668 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6669 if (t
->icon_download
) {
6670 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6671 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6672 webkit_download_cancel(t
->icon_download
);
6673 t
->icon_download
= NULL
;
6678 xt_icon_from_name(t
, "text-html");
6682 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6684 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6686 if (uri
== NULL
|| t
== NULL
)
6689 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6690 /* take icon from WebKitIconDatabase */
6693 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6695 xt_icon_from_pixbuf(t
, pb
);
6698 xt_icon_from_name(t
, "text-html");
6699 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6700 /* download icon to cache dir */
6701 gchar
*name_hash
, file
[PATH_MAX
];
6704 if (t
->icon_request
) {
6705 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6709 /* check to see if we got the icon in cache */
6710 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6711 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6714 if (!stat(file
, &sb
)) {
6715 if (sb
.st_size
> 0) {
6716 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6718 set_favicon_from_file(t
, file
);
6722 /* corrupt icon so trash it */
6723 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6728 /* create download for icon */
6729 t
->icon_request
= webkit_network_request_new(uri
);
6730 if (t
->icon_request
== NULL
) {
6731 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6736 t
->icon_download
= webkit_download_new(t
->icon_request
);
6737 if (t
->icon_download
== NULL
)
6740 /* we have to free icon_dest_uri later */
6741 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6742 webkit_download_set_destination_uri(t
->icon_download
,
6745 if (webkit_download_get_status(t
->icon_download
) ==
6746 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6747 g_object_unref(t
->icon_request
);
6748 g_free(t
->icon_dest_uri
);
6749 t
->icon_request
= NULL
;
6750 t
->icon_dest_uri
= NULL
;
6754 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6755 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6757 webkit_download_start(t
->icon_download
);
6762 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6764 const gchar
*uri
= NULL
, *title
= NULL
;
6765 struct history
*h
, find
;
6769 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6770 webkit_web_view_get_load_status(wview
),
6771 get_uri(t
) ? get_uri(t
) : "NOTHING");
6774 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6778 switch (webkit_web_view_get_load_status(wview
)) {
6779 case WEBKIT_LOAD_PROVISIONAL
:
6781 abort_favicon_download(t
);
6782 #if GTK_CHECK_VERSION(2, 20, 0)
6783 gtk_widget_show(t
->spinner
);
6784 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6786 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6788 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6790 /* assume we are a new address */
6791 gdk_color_parse("white", &color
);
6792 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6793 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6795 /* take focus if we are visible */
6801 case WEBKIT_LOAD_COMMITTED
:
6806 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6812 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6814 /* check if js white listing is enabled */
6815 if (enable_cookie_whitelist
)
6816 check_and_set_cookie(uri
, t
);
6817 if (enable_js_whitelist
)
6818 check_and_set_js(uri
, t
);
6824 /* we know enough to autosave the session */
6825 if (session_autosave
) {
6830 show_ca_status(t
, uri
);
6833 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6837 case WEBKIT_LOAD_FINISHED
:
6843 if (!strncmp(uri
, "http://", strlen("http://")) ||
6844 !strncmp(uri
, "https://", strlen("https://")) ||
6845 !strncmp(uri
, "file://", strlen("file://"))) {
6847 h
= RB_FIND(history_list
, &hl
, &find
);
6849 title
= get_title(t
, FALSE
);
6850 h
= g_malloc(sizeof *h
);
6851 h
->uri
= g_strdup(uri
);
6852 h
->title
= g_strdup(title
);
6853 RB_INSERT(history_list
, &hl
, h
);
6854 completion_add_uri(h
->uri
);
6855 update_history_tabs(NULL
);
6859 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6860 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6861 case WEBKIT_LOAD_FAILED
:
6864 #if GTK_CHECK_VERSION(2, 20, 0)
6865 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6866 gtk_widget_hide(t
->spinner
);
6869 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6874 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6876 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6877 can_go_back_for_real(t
));
6879 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6880 can_go_forward_for_real(t
));
6884 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6886 const gchar
*title
= NULL
, *win_title
= NULL
;
6888 title
= get_title(t
, FALSE
);
6889 win_title
= get_title(t
, TRUE
);
6890 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6891 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6892 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6893 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6897 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6899 run_script(t
, JS_HINTING
);
6903 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6905 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6906 progress
== 100 ? 0 : (double)progress
/ 100);
6907 if (show_url
== 0) {
6908 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6909 progress
== 100 ? 0 : (double)progress
/ 100);
6912 update_statusbar_position(NULL
, NULL
);
6916 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6917 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6918 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6921 WebKitWebNavigationReason reason
;
6922 struct domain
*d
= NULL
;
6925 show_oops(NULL
, "webview_npd_cb invalid parameters");
6929 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6931 webkit_network_request_get_uri(request
));
6933 uri
= (char *)webkit_network_request_get_uri(request
);
6935 /* if this is an xtp url, we don't load anything else */
6936 if (parse_xtp_url(t
, uri
))
6939 if (t
->ctrl_click
) {
6941 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6942 webkit_web_policy_decision_ignore(pd
);
6943 return (TRUE
); /* we made the decission */
6947 * This is a little hairy but it comes down to this:
6948 * when we run in whitelist mode we have to assist the browser in
6949 * opening the URL that it would have opened in a new tab.
6951 reason
= webkit_web_navigation_action_get_reason(na
);
6952 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6953 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6954 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6955 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6957 webkit_web_policy_decision_use(pd
);
6958 return (TRUE
); /* we made the decision */
6965 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6968 struct domain
*d
= NULL
;
6970 WebKitWebView
*webview
= NULL
;
6972 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6973 webkit_web_view_get_uri(wv
));
6976 /* open in current tab */
6978 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6979 uri
= webkit_web_view_get_uri(wv
);
6980 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6983 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6985 } else if (enable_scripts
== 1) {
6986 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6994 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6997 struct domain
*d
= NULL
;
6999 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7001 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7002 uri
= webkit_web_view_get_uri(wv
);
7003 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7007 } else if (enable_scripts
== 1)
7014 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7016 /* we can not eat the event without throwing gtk off so defer it */
7018 /* catch middle click */
7019 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7024 /* catch ctrl click */
7025 if (e
->type
== GDK_BUTTON_RELEASE
&&
7026 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7031 return (XT_CB_PASSTHROUGH
);
7035 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7037 struct mime_type
*m
;
7039 m
= find_mime_type(mime_type
);
7047 show_oops(t
, "can't fork mime handler");
7057 execlp(m
->mt_action
, m
->mt_action
,
7058 webkit_network_request_get_uri(request
), (void *)NULL
);
7067 get_mime_type(char *file
)
7069 const char *mime_type
;
7073 if (g_str_has_prefix(file
, "file://"))
7074 file
+= strlen("file://");
7076 gf
= g_file_new_for_path(file
);
7077 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7079 mime_type
= g_file_info_get_content_type(fi
);
7087 run_download_mimehandler(char *mime_type
, char *file
)
7089 struct mime_type
*m
;
7091 m
= find_mime_type(mime_type
);
7097 show_oops(NULL
, "can't fork download mime handler");
7107 if (g_str_has_prefix(file
, "file://"))
7108 file
+= strlen("file://");
7109 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7118 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7121 WebKitDownloadStatus status
;
7122 const gchar
*file
= NULL
, *mime
= NULL
;
7124 if (download
== NULL
)
7126 status
= webkit_download_get_status(download
);
7127 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7130 file
= webkit_download_get_destination_uri(download
);
7133 mime
= get_mime_type((char *)file
);
7137 run_download_mimehandler((char *)mime
, (char *)file
);
7141 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7142 WebKitNetworkRequest
*request
, char *mime_type
,
7143 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7146 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7150 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7151 t
->tab_id
, mime_type
);
7153 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7154 webkit_web_policy_decision_ignore(decision
);
7159 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7160 webkit_web_policy_decision_download(decision
);
7168 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7172 const gchar
*suggested_name
;
7173 gchar
*filename
= NULL
;
7175 struct download
*download_entry
;
7178 if (wk_download
== NULL
|| t
== NULL
) {
7179 show_oops(NULL
, "%s invalid parameters", __func__
);
7183 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7184 if (suggested_name
== NULL
)
7185 return (FALSE
); /* abort download */
7196 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7198 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7199 filename
: suggested_name
);
7201 } while (!stat(uri
+ strlen("file://"), &sb
));
7203 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7204 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7206 webkit_download_set_destination_uri(wk_download
, uri
);
7208 if (webkit_download_get_status(wk_download
) ==
7209 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7210 show_oops(t
, "%s: download failed to start", __func__
);
7212 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7214 /* connect "download first" mime handler */
7215 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7216 G_CALLBACK(download_status_changed_cb
), NULL
);
7218 download_entry
= g_malloc(sizeof(struct download
));
7219 download_entry
->download
= wk_download
;
7220 download_entry
->tab
= t
;
7221 download_entry
->id
= next_download_id
++;
7222 RB_INSERT(download_list
, &downloads
, download_entry
);
7223 /* get from history */
7224 g_object_ref(wk_download
);
7225 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7226 show_oops(t
, "Download of '%s' started...",
7227 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7236 /* sync other download manager tabs */
7237 update_download_tabs(NULL
);
7240 * NOTE: never redirect/render the current tab before this
7241 * function returns. This will cause the download to never start.
7243 return (ret
); /* start download */
7247 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7249 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7252 show_oops(NULL
, "webview_hover_cb");
7257 set_status(t
, uri
, XT_STATUS_LINK
);
7260 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7265 mark(struct tab
*t
, struct karg
*arg
)
7271 if ((index
= marktoindex(mark
)) == -1)
7274 if (arg
->i
== XT_MARK_SET
)
7275 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7276 else if (arg
->i
== XT_MARK_GOTO
) {
7277 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7278 show_oops(t
, "mark '%c' does not exist", mark
);
7281 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7282 something changes the document size */
7283 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7290 marks_clear(struct tab
*t
)
7294 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7295 t
->mark
[i
] = XT_INVALID_MARK
;
7301 char file
[PATH_MAX
];
7302 char *line
= NULL
, *p
;
7307 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7308 if ((f
= fopen(file
, "r+")) == NULL
) {
7309 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7313 for (i
= 1; ; i
++) {
7314 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7315 if (feof(f
) || ferror(f
))
7317 if (strlen(line
) == 0 || line
[0] == '#') {
7323 p
= strtok(line
, " \t");
7325 if (p
== NULL
|| strlen(p
) != 1 ||
7326 (index
= marktoindex(*p
)) == -1) {
7327 warnx("corrupt quickmarks file, line %d", i
);
7331 p
= strtok(NULL
, " \t");
7332 if (qmarks
[index
] != NULL
)
7333 g_free(qmarks
[index
]);
7334 qmarks
[index
] = g_strdup(p
);
7345 char file
[PATH_MAX
];
7349 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7350 if ((f
= fopen(file
, "r+")) == NULL
) {
7351 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7355 for (i
= 0; i
< XT_NOMARKS
; i
++)
7356 if (qmarks
[i
] != NULL
)
7357 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7365 qmark(struct tab
*t
, struct karg
*arg
)
7370 mark
= arg
->s
[strlen(arg
->s
)-1];
7371 index
= marktoindex(mark
);
7377 if (qmarks
[index
] != NULL
)
7378 g_free(qmarks
[index
]);
7380 qmarks_load(); /* sync if multiple instances */
7381 qmarks
[index
] = g_strdup(get_uri(t
));
7385 if (qmarks
[index
] != NULL
)
7386 load_uri(t
, qmarks
[index
]);
7388 show_oops(t
, "quickmark \"%c\" does not exist",
7394 if (qmarks
[index
] != NULL
)
7395 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7397 show_oops(t
, "quickmark \"%c\" does not exist",
7408 go_up(struct tab
*t
, struct karg
*args
)
7414 levels
= atoi(args
->s
);
7418 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7419 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7421 tmp
+= strlen(XT_PROTO_DELIM
);
7423 /* if an uri starts with a slash, leave it alone (for file:///) */
7430 p
= strrchr(tmp
, '/');
7444 gototab(struct tab
*t
, struct karg
*args
)
7447 struct karg arg
= {0, NULL
, -1};
7449 tab
= atoi(args
->s
);
7451 arg
.i
= XT_TAB_NEXT
;
7460 zoom_amount(struct tab
*t
, struct karg
*arg
)
7462 struct karg narg
= {0, NULL
, -1};
7464 narg
.i
= atoi(arg
->s
);
7465 resizetab(t
, &narg
);
7471 flip_colon(struct tab
*t
, struct karg
*arg
)
7473 struct karg narg
= {0, NULL
, -1};
7476 if (t
== NULL
|| arg
== NULL
)
7479 p
= strstr(arg
->s
, ":");
7491 /* buffer commands receive the regex that triggered them in arg.s */
7492 char bcmd
[XT_BUFCMD_SZ
];
7496 #define XT_PRE_NO (0)
7497 #define XT_PRE_YES (1)
7498 #define XT_PRE_MAYBE (2)
7500 int (*func
)(struct tab
*, struct karg
*);
7504 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7505 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7506 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7507 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7508 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7509 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7510 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7511 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7512 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7513 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7514 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7515 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7516 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7517 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7518 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7519 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7520 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7521 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7525 buffercmd_init(void)
7529 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7530 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7531 REG_EXTENDED
| REG_NOSUB
))
7532 startpage_add("invalid buffercmd regex %s",
7533 buffercmds
[i
].regex
);
7537 buffercmd_abort(struct tab
*t
)
7541 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7542 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7545 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7546 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7550 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7552 struct karg arg
= {0, NULL
, -1};
7555 arg
.s
= g_strdup(bcmd
);
7557 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7558 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7568 buffercmd_addkey(struct tab
*t
, guint keyval
)
7571 char s
[XT_BUFCMD_SZ
];
7573 if (keyval
== GDK_Escape
) {
7575 return (XT_CB_HANDLED
);
7578 /* key with modifier or non-ascii character */
7579 if (!isascii(keyval
))
7580 return (XT_CB_PASSTHROUGH
);
7582 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7583 "to buffer \"%s\"\n", keyval
, bcmd
);
7585 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7586 if (bcmd
[i
] == '\0') {
7591 /* buffer full, ignore input */
7592 if (i
>= LENGTH(bcmd
) -1) {
7593 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7595 return (XT_CB_HANDLED
);
7598 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7600 /* find exact match */
7601 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7602 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7603 (size_t) 0, NULL
, 0) == 0) {
7604 buffercmd_execute(t
, &buffercmds
[i
]);
7608 /* find non exact matches to see if we need to abort ot not */
7609 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7610 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7613 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7614 if (isdigit(bcmd
[0])) {
7615 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7619 if (sscanf(bcmd
, "%s", s
) == 0)
7622 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7623 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7626 if (sscanf(bcmd
, "%s", s
) == 0)
7629 if (c
== -1 && buffercmds
[i
].precount
)
7631 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7634 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7635 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7638 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7643 return (XT_CB_HANDLED
);
7647 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7649 struct key_binding
*k
;
7651 /* handle keybindings if buffercmd is empty.
7652 if not empty, allow commands like C-n */
7653 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7654 TAILQ_FOREACH(k
, &kbl
, entry
)
7655 if (e
->keyval
== k
->key
7656 && (entry
? k
->use_in_entry
: 1)) {
7658 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7659 return (cmd_execute(t
, k
->cmd
));
7660 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7661 return (cmd_execute(t
, k
->cmd
));
7665 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7666 return buffercmd_addkey(t
, e
->keyval
);
7668 return (XT_CB_PASSTHROUGH
);
7672 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7674 char s
[2], buf
[128];
7675 const char *errstr
= NULL
;
7677 /* don't use w directly; use t->whatever instead */
7680 show_oops(NULL
, "wv_keypress_after_cb");
7681 return (XT_CB_PASSTHROUGH
);
7684 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7685 e
->keyval
, e
->state
, t
);
7689 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7691 return (XT_CB_HANDLED
);
7695 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7697 /* we have a string */
7699 /* we have a number */
7700 snprintf(buf
, sizeof buf
,
7701 "vimprobable_fire(%s)", t
->hint_num
);
7708 /* XXX unfuck this */
7709 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7710 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7711 /* last input was numerical */
7713 l
= strlen(t
->hint_num
);
7720 t
->hint_num
[l
] = '\0';
7724 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7725 /* last input was alphanumerical */
7727 l
= strlen(t
->hint_buf
);
7734 t
->hint_buf
[l
] = '\0';
7744 /* numerical input */
7745 if (CLEAN(e
->state
) == 0 &&
7746 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7747 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7748 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7749 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7750 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7754 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7755 "invalid link number\n");
7758 snprintf(buf
, sizeof buf
,
7759 "vimprobable_update_hints(%s)",
7761 t
->hint_mode
= XT_HINT_NUMERICAL
;
7765 /* empty the counter buffer */
7766 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7767 return (XT_CB_HANDLED
);
7770 /* alphanumerical input */
7771 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7772 e
->keyval
<= GDK_z
) ||
7773 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7774 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7775 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7776 e
->keyval
<= GDK_9
) ||
7777 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7778 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7779 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7780 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7781 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7782 " %s\n", t
->hint_buf
);
7784 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7787 snprintf(buf
, sizeof buf
,
7788 "vimprobable_show_hints('%s')", t
->hint_buf
);
7789 t
->hint_mode
= XT_HINT_ALPHANUM
;
7792 /* empty the counter buffer */
7793 bzero(t
->hint_num
, sizeof t
->hint_num
);
7794 return (XT_CB_HANDLED
);
7797 return (XT_CB_HANDLED
);
7800 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7801 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7802 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7805 return (handle_keypress(t
, e
, 0));
7809 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7813 /* Hide buffers, if they are visible, with escape. */
7814 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7815 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7816 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7818 return (XT_CB_HANDLED
);
7821 return (XT_CB_PASSTHROUGH
);
7825 search_continue(struct tab
*t
)
7827 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7828 gboolean rv
= FALSE
;
7832 if (strlen(c
) == 1) {
7833 webkit_web_view_unmark_text_matches(t
->wv
);
7838 t
->search_forward
= TRUE
;
7839 else if (c
[0] == '?')
7840 t
->search_forward
= FALSE
;
7850 search_cb(struct tab
*t
)
7852 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7855 if (search_continue(t
) == FALSE
)
7859 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7861 /* not found, mark red */
7862 gdk_color_parse(XT_COLOR_RED
, &color
);
7863 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7864 /* unmark and remove selection */
7865 webkit_web_view_unmark_text_matches(t
->wv
);
7866 /* my kingdom for a way to unselect text in webview */
7868 /* found, highlight all */
7869 webkit_web_view_unmark_text_matches(t
->wv
);
7870 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7871 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7872 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7873 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7881 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7883 const gchar
*c
= gtk_entry_get_text(w
);
7886 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7887 return (XT_CB_PASSTHROUGH
);
7890 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7891 e
->keyval
, e
->state
, t
);
7893 if (search_continue(t
) == FALSE
)
7896 /* if search length is > 4 then no longer play timeout games */
7897 if (strlen(c
) > 4) {
7899 g_source_remove(t
->search_id
);
7906 /* reestablish a new timer if the user types fast */
7908 g_source_remove(t
->search_id
);
7909 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7912 return (XT_CB_PASSTHROUGH
);
7916 match_uri(const gchar
*uri
, const gchar
*key
) {
7919 gboolean match
= FALSE
;
7923 if (!strncmp(key
, uri
, len
))
7926 voffset
= strstr(uri
, "/") + 2;
7927 if (!strncmp(key
, voffset
, len
))
7929 else if (g_str_has_prefix(voffset
, "www.")) {
7930 voffset
= voffset
+ strlen("www.");
7931 if (!strncmp(key
, voffset
, len
))
7940 match_session(const gchar
*name
, const gchar
*key
) {
7943 sub
= strcasestr(name
, key
);
7949 cmd_getlist(int id
, char *key
)
7956 if (cmds
[id
].type
& XT_URLARG
) {
7957 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7958 if (match_uri(h
->uri
, key
)) {
7959 cmd_status
.list
[c
] = (char *)h
->uri
;
7965 } else if (cmds
[id
].type
& XT_SESSARG
) {
7966 TAILQ_FOREACH(s
, &sessions
, entry
)
7967 if (match_session(s
->name
, key
)) {
7968 cmd_status
.list
[c
] = (char *)s
->name
;
7974 } else if (cmds
[id
].type
& XT_SETARG
) {
7975 for (i
= 0; i
< LENGTH(rs
); i
++)
7976 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
7977 cmd_status
.list
[c
++] = rs
[i
].name
;
7983 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7985 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7986 if (cmds
[i
].level
< dep
)
7988 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7989 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
7990 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7998 cmd_getnext(int dir
)
8000 cmd_status
.index
+= dir
;
8002 if (cmd_status
.index
< 0)
8003 cmd_status
.index
= cmd_status
.len
- 1;
8004 else if (cmd_status
.index
>= cmd_status
.len
)
8005 cmd_status
.index
= 0;
8007 return cmd_status
.list
[cmd_status
.index
];
8011 cmd_tokenize(char *s
, char *tokens
[])
8015 size_t len
= strlen(s
);
8018 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8019 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8020 tok
= strtok_r(NULL
, " ", &last
), i
++)
8030 cmd_complete(struct tab
*t
, char *str
, int dir
)
8032 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8033 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8035 char *tok
, *match
, *s
= g_strdup(str
);
8037 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8040 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8043 for (i
= 0; isdigit(s
[i
]); i
++)
8046 for (; isspace(s
[i
]); i
++)
8051 levels
= cmd_tokenize(s
, tokens
);
8053 for (i
= 0; i
< levels
- 1; i
++) {
8056 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8057 if (cmds
[j
].level
< dep
)
8059 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8063 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8070 if (matchcount
== 1) {
8071 strlcat(res
, tok
, sizeof res
);
8072 strlcat(res
, " ", sizeof res
);
8082 if (cmd_status
.index
== -1)
8083 cmd_getlist(parent
, tokens
[i
]);
8085 if (cmd_status
.len
> 0) {
8086 match
= cmd_getnext(dir
);
8087 strlcat(res
, match
, sizeof res
);
8088 gtk_entry_set_text(w
, res
);
8089 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8096 cmd_execute(struct tab
*t
, char *str
)
8098 struct cmd
*cmd
= NULL
;
8099 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
8101 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8102 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8103 struct karg arg
= {0, NULL
, -1};
8108 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8114 while (isspace(s
[0]))
8117 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8118 prefix
= atoi(prefixstr
);
8122 for (tok
= strtok_r(s
, " ", &last
); tok
;
8123 tok
= strtok_r(NULL
, " ", &last
)) {
8125 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8126 if (cmds
[j
].level
< dep
)
8128 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8130 if (cmds
[j
].level
== dep
&&
8131 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8135 if (len
== strlen(cmds
[j
].cmd
)) {
8141 if (matchcount
== 1) {
8146 show_oops(t
, "Invalid command: %s", str
);
8154 arg
.precount
= prefix
;
8155 else if (cmd_prefix
> 0)
8156 arg
.precount
= cmd_prefix
;
8158 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8159 show_oops(t
, "No prefix allowed: %s", str
);
8163 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8164 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8165 arg
.precount
= atoi(arg
.s
);
8166 if (arg
.precount
<= 0) {
8167 if (arg
.s
[0] == '0')
8168 show_oops(t
, "Zero count");
8170 show_oops(t
, "Trailing characters");
8175 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8176 __func__
, arg
.precount
, arg
.s
);
8192 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8195 show_oops(NULL
, "entry_key_cb invalid parameters");
8196 return (XT_CB_PASSTHROUGH
);
8199 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8200 e
->keyval
, e
->state
, t
);
8204 if (e
->keyval
== GDK_Escape
) {
8205 /* don't use focus_webview(t) because we want to type :cmds */
8206 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8209 return (handle_keypress(t
, e
, 1));
8212 struct command_entry
*
8213 history_prev(struct command_list
*l
, struct command_entry
*at
)
8216 at
= TAILQ_LAST(l
, command_list
);
8218 at
= TAILQ_PREV(at
, command_list
, entry
);
8220 at
= TAILQ_LAST(l
, command_list
);
8226 struct command_entry
*
8227 history_next(struct command_list
*l
, struct command_entry
*at
)
8230 at
= TAILQ_FIRST(l
);
8232 at
= TAILQ_NEXT(at
, entry
);
8234 at
= TAILQ_FIRST(l
);
8241 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8243 int rv
= XT_CB_HANDLED
;
8244 const gchar
*c
= gtk_entry_get_text(w
);
8247 show_oops(NULL
, "cmd_keypress_cb parameters");
8248 return (XT_CB_PASSTHROUGH
);
8251 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8252 e
->keyval
, e
->state
, t
);
8256 e
->keyval
= GDK_Escape
;
8257 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8258 e
->keyval
= GDK_Escape
;
8260 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8261 e
->keyval
!= GDK_ISO_Left_Tab
)
8262 cmd_status
.index
= -1;
8264 switch (e
->keyval
) {
8267 cmd_complete(t
, (char *)&c
[1], 1);
8269 case GDK_ISO_Left_Tab
:
8271 cmd_complete(t
, (char *)&c
[1], -1);
8276 if ((search_at
= history_next(&shl
, search_at
))) {
8277 search_at
->line
[0] = c
[0];
8278 gtk_entry_set_text(w
, search_at
->line
);
8279 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8282 if ((history_at
= history_prev(&chl
, history_at
))) {
8283 history_at
->line
[0] = c
[0];
8284 gtk_entry_set_text(w
, history_at
->line
);
8285 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8292 if ((search_at
= history_next(&shl
, search_at
))) {
8293 search_at
->line
[0] = c
[0];
8294 gtk_entry_set_text(w
, search_at
->line
);
8295 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8298 if ((history_at
= history_next(&chl
, history_at
))) {
8299 history_at
->line
[0] = c
[0];
8300 gtk_entry_set_text(w
, history_at
->line
);
8301 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8307 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8315 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8316 webkit_web_view_unmark_text_matches(t
->wv
);
8320 rv
= XT_CB_PASSTHROUGH
;
8326 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8328 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8332 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8334 /* popup menu enabled */
8339 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8342 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8343 return (XT_CB_PASSTHROUGH
);
8346 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8347 t
->tab_id
, t
->popup
);
8349 /* if popup is enabled don't lose focus */
8352 return (XT_CB_PASSTHROUGH
);
8358 if (show_url
== 0 || t
->focus_wv
)
8361 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8363 return (XT_CB_PASSTHROUGH
);
8367 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8370 const gchar
*c
= gtk_entry_get_text(entry
);
8373 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8377 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8384 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8390 if (c
[0] == '/' || c
[0] == '?') {
8391 /* see if there is a timer pending */
8393 g_source_remove(t
->search_id
);
8398 if (t
->search_text
) {
8399 g_free(t
->search_text
);
8400 t
->search_text
= NULL
;
8403 t
->search_text
= g_strdup(s
);
8405 g_free(global_search
);
8406 global_search
= g_strdup(s
);
8407 t
->search_forward
= c
[0] == '/';
8409 history_add(&shl
, search_file
, s
, &search_history_count
);
8413 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8420 backward_cb(GtkWidget
*w
, struct tab
*t
)
8425 show_oops(NULL
, "backward_cb invalid parameters");
8429 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8436 forward_cb(GtkWidget
*w
, struct tab
*t
)
8441 show_oops(NULL
, "forward_cb invalid parameters");
8445 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8447 a
.i
= XT_NAV_FORWARD
;
8452 home_cb(GtkWidget
*w
, struct tab
*t
)
8455 show_oops(NULL
, "home_cb invalid parameters");
8459 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8465 stop_cb(GtkWidget
*w
, struct tab
*t
)
8467 WebKitWebFrame
*frame
;
8470 show_oops(NULL
, "stop_cb invalid parameters");
8474 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8476 frame
= webkit_web_view_get_main_frame(t
->wv
);
8477 if (frame
== NULL
) {
8478 show_oops(t
, "stop_cb: no frame");
8482 webkit_web_frame_stop_loading(frame
);
8483 abort_favicon_download(t
);
8487 setup_webkit(struct tab
*t
)
8489 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8490 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8491 FALSE
, (char *)NULL
);
8493 warnx("webkit does not have \"enable-dns-prefetching\" property");
8494 g_object_set(G_OBJECT(t
->settings
),
8495 "user-agent", t
->user_agent
, (char *)NULL
);
8496 g_object_set(G_OBJECT(t
->settings
),
8497 "enable-scripts", enable_scripts
, (char *)NULL
);
8498 g_object_set(G_OBJECT(t
->settings
),
8499 "enable-plugins", enable_plugins
, (char *)NULL
);
8500 g_object_set(G_OBJECT(t
->settings
),
8501 "javascript-can-open-windows-automatically", enable_scripts
,
8503 g_object_set(G_OBJECT(t
->settings
),
8504 "enable-html5-database", FALSE
, (char *)NULL
);
8505 g_object_set(G_OBJECT(t
->settings
),
8506 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8507 g_object_set(G_OBJECT(t
->settings
),
8508 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8509 g_object_set(G_OBJECT(t
->settings
),
8510 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8511 g_object_set(G_OBJECT(t
->wv
),
8512 "full-content-zoom", TRUE
, (char *)NULL
);
8514 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8518 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8520 struct tab
*ti
, *t
= NULL
;
8521 gdouble view_size
, value
, max
;
8524 TAILQ_FOREACH(ti
, &tabs
, entry
)
8525 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8533 if (adjustment
== NULL
)
8534 adjustment
= gtk_scrolled_window_get_vadjustment(
8535 GTK_SCROLLED_WINDOW(t
->browser_win
));
8537 view_size
= gtk_adjustment_get_page_size(adjustment
);
8538 value
= gtk_adjustment_get_value(adjustment
);
8539 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8542 position
= g_strdup("All");
8543 else if (value
== max
)
8544 position
= g_strdup("Bot");
8545 else if (value
== 0)
8546 position
= g_strdup("Top");
8548 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8550 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8557 create_browser(struct tab
*t
)
8561 GtkAdjustment
*adjustment
;
8564 show_oops(NULL
, "create_browser invalid parameters");
8568 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8569 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8570 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8571 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8573 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8574 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8575 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8577 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8578 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8581 t
->settings
= webkit_web_settings_new();
8583 if (user_agent
== NULL
) {
8584 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8586 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8589 t
->user_agent
= g_strdup(user_agent
);
8591 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8594 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8595 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8596 G_CALLBACK(update_statusbar_position
), NULL
);
8608 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8609 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8610 gtk_widget_set_name(w
, "xxxterm");
8611 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8612 g_signal_connect(G_OBJECT(w
), "delete_event",
8613 G_CALLBACK (gtk_main_quit
), NULL
);
8619 create_kiosk_toolbar(struct tab
*t
)
8621 GtkWidget
*toolbar
= NULL
, *b
;
8623 b
= gtk_hbox_new(FALSE
, 0);
8625 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8627 /* backward button */
8628 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8629 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8630 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8631 G_CALLBACK(backward_cb
), t
);
8632 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8634 /* forward button */
8635 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8636 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8637 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8638 G_CALLBACK(forward_cb
), t
);
8639 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8642 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8643 gtk_widget_set_sensitive(t
->gohome
, true);
8644 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8645 G_CALLBACK(home_cb
), t
);
8646 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8648 /* create widgets but don't use them */
8649 t
->uri_entry
= gtk_entry_new();
8650 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8651 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8652 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8658 create_toolbar(struct tab
*t
)
8660 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8662 b
= gtk_hbox_new(FALSE
, 0);
8664 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8666 /* backward button */
8667 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8668 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8669 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8670 G_CALLBACK(backward_cb
), t
);
8671 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8673 /* forward button */
8674 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8675 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8676 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8677 G_CALLBACK(forward_cb
), t
);
8678 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8682 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8683 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8684 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8685 G_CALLBACK(stop_cb
), t
);
8686 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8690 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8691 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8692 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8693 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8694 G_CALLBACK(js_toggle_cb
), t
);
8695 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8697 t
->uri_entry
= gtk_entry_new();
8698 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8699 G_CALLBACK(activate_uri_entry_cb
), t
);
8700 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8701 G_CALLBACK(entry_key_cb
), t
);
8703 eb1
= gtk_hbox_new(FALSE
, 0);
8704 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8705 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8706 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8709 if (search_string
) {
8711 t
->search_entry
= gtk_entry_new();
8712 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8713 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8714 G_CALLBACK(activate_search_entry_cb
), t
);
8715 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8716 G_CALLBACK(entry_key_cb
), t
);
8717 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8718 eb2
= gtk_hbox_new(FALSE
, 0);
8719 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8720 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8722 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8729 create_buffers(struct tab
*t
)
8731 GtkCellRenderer
*renderer
;
8734 view
= gtk_tree_view_new();
8736 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8738 renderer
= gtk_cell_renderer_text_new();
8739 gtk_tree_view_insert_column_with_attributes
8740 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8742 renderer
= gtk_cell_renderer_text_new();
8743 gtk_tree_view_insert_column_with_attributes
8744 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8747 gtk_tree_view_set_model
8748 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8754 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8755 GtkTreeViewColumn
*col
, struct tab
*t
)
8760 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8762 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8765 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8766 set_current_tab(id
- 1);
8772 /* after tab reordering/creation/removal */
8779 TAILQ_FOREACH(t
, &tabs
, entry
) {
8780 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8781 if (t
->tab_id
> maxid
)
8784 gtk_widget_show(t
->tab_elems
.sep
);
8787 TAILQ_FOREACH(t
, &tabs
, entry
) {
8788 if (t
->tab_id
== maxid
) {
8789 gtk_widget_hide(t
->tab_elems
.sep
);
8795 /* after active tab change */
8797 recolor_compact_tabs(void)
8803 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8804 TAILQ_FOREACH(t
, &tabs
, entry
)
8805 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8808 curid
= gtk_notebook_get_current_page(notebook
);
8809 TAILQ_FOREACH(t
, &tabs
, entry
)
8810 if (t
->tab_id
== curid
) {
8811 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8812 gtk_widget_modify_fg(t
->tab_elems
.label
,
8813 GTK_STATE_NORMAL
, &color
);
8819 set_current_tab(int page_num
)
8821 buffercmd_abort(get_current_tab());
8822 gtk_notebook_set_current_page(notebook
, page_num
);
8823 recolor_compact_tabs();
8827 undo_close_tab_save(struct tab
*t
)
8831 struct undo
*u1
, *u2
;
8833 WebKitWebHistoryItem
*item
;
8835 if ((uri
= get_uri(t
)) == NULL
)
8838 u1
= g_malloc0(sizeof(struct undo
));
8839 u1
->uri
= g_strdup(uri
);
8841 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8843 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8844 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8847 /* forward history */
8848 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8852 u1
->history
= g_list_prepend(u1
->history
,
8853 webkit_web_history_item_copy(item
));
8854 items
= g_list_next(items
);
8859 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8860 u1
->history
= g_list_prepend(u1
->history
,
8861 webkit_web_history_item_copy(item
));
8865 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8869 u1
->history
= g_list_prepend(u1
->history
,
8870 webkit_web_history_item_copy(item
));
8871 items
= g_list_next(items
);
8874 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8876 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8877 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8878 TAILQ_REMOVE(&undos
, u2
, entry
);
8880 g_list_free(u2
->history
);
8889 delete_tab(struct tab
*t
)
8893 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8899 TAILQ_REMOVE(&tabs
, t
, entry
);
8901 /* Halt all webkit activity. */
8902 abort_favicon_download(t
);
8903 webkit_web_view_stop_loading(t
->wv
);
8905 /* Save the tab, so we can undo the close. */
8906 undo_close_tab_save(t
);
8908 if (browser_mode
== XT_BM_KIOSK
) {
8909 gtk_widget_destroy(t
->uri_entry
);
8910 gtk_widget_destroy(t
->stop
);
8911 gtk_widget_destroy(t
->js_toggle
);
8914 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8915 gtk_widget_destroy(t
->vbox
);
8919 g_source_remove(t
->search_id
);
8921 g_free(t
->user_agent
);
8922 g_free(t
->stylesheet
);
8926 if (TAILQ_EMPTY(&tabs
)) {
8927 if (browser_mode
== XT_BM_KIOSK
)
8928 create_new_tab(home
, NULL
, 1, -1);
8930 create_new_tab(NULL
, NULL
, 1, -1);
8933 /* recreate session */
8934 if (session_autosave
) {
8940 recolor_compact_tabs();
8944 update_statusbar_zoom(struct tab
*t
)
8947 char s
[16] = { '\0' };
8949 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8950 if ((zoom
<= 0.99 || zoom
>= 1.01))
8951 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8952 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8956 setzoom_webkit(struct tab
*t
, int adjust
)
8958 #define XT_ZOOMPERCENT 0.04
8963 show_oops(NULL
, "setzoom_webkit invalid parameters");
8967 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8968 if (adjust
== XT_ZOOM_IN
)
8969 zoom
+= XT_ZOOMPERCENT
;
8970 else if (adjust
== XT_ZOOM_OUT
)
8971 zoom
-= XT_ZOOMPERCENT
;
8972 else if (adjust
> 0)
8973 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8975 show_oops(t
, "setzoom_webkit invalid zoom value");
8979 if (zoom
< XT_ZOOMPERCENT
)
8980 zoom
= XT_ZOOMPERCENT
;
8981 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8982 update_statusbar_zoom(t
);
8986 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8988 struct tab
*t
= (struct tab
*) data
;
8990 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8992 switch (event
->button
) {
8994 set_current_tab(t
->tab_id
);
9005 append_tab(struct tab
*t
)
9010 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9011 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9015 create_sbe(int width
)
9019 sbe
= gtk_entry_new();
9020 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9021 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9022 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9023 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9024 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9025 gtk_widget_set_size_request(sbe
, width
, -1);
9031 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9036 WebKitWebHistoryItem
*item
;
9040 int sbe_p
= 0, sbe_b
= 0,
9043 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9045 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9046 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9050 t
= g_malloc0(sizeof *t
);
9052 if (title
== NULL
) {
9053 title
= "(untitled)";
9057 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9059 /* label + button for tab */
9060 b
= gtk_hbox_new(FALSE
, 0);
9063 #if GTK_CHECK_VERSION(2, 20, 0)
9064 t
->spinner
= gtk_spinner_new();
9066 t
->label
= gtk_label_new(title
);
9067 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9068 gtk_widget_set_size_request(t
->label
, 100, 0);
9069 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9070 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9071 gtk_widget_set_size_request(b
, 130, 0);
9073 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9074 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9075 #if GTK_CHECK_VERSION(2, 20, 0)
9076 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9080 if (browser_mode
== XT_BM_KIOSK
) {
9081 t
->toolbar
= create_kiosk_toolbar(t
);
9082 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9085 t
->toolbar
= create_toolbar(t
);
9087 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9095 t
->browser_win
= create_browser(t
);
9096 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9098 /* oops message for user feedback */
9099 t
->oops
= gtk_entry_new();
9100 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9101 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9102 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9103 gdk_color_parse(XT_COLOR_RED
, &color
);
9104 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9105 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9106 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9109 t
->cmd
= gtk_entry_new();
9110 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9111 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9112 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9113 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9116 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9118 t
->sbe
.statusbar
= gtk_entry_new();
9119 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9120 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9121 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9122 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9124 /* create these widgets only if specified in statusbar_elems */
9126 t
->sbe
.position
= create_sbe(40);
9127 t
->sbe
.zoom
= create_sbe(40);
9128 t
->sbe
.buffercmd
= create_sbe(60);
9130 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9132 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9135 /* gtk widgets cannot be added to a box twice. sbe_* variables
9136 make sure of this */
9137 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9141 GtkWidget
*sep
= gtk_vseparator_new();
9143 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9144 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9145 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9146 FALSE
, FALSE
, FALSE
);
9151 warnx("flag \"%c\" specified more than "
9152 "once in statusbar_elems\n", *p
);
9156 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9157 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9161 warnx("flag \"%c\" specified more than "
9162 "once in statusbar_elems\n", *p
);
9166 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9167 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9171 warnx("flag \"%c\" specified more than "
9172 "once in statusbar_elems\n", *p
);
9176 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9177 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9180 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9185 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9188 t
->buffers
= create_buffers(t
);
9189 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9191 /* xtp meaning is normal by default */
9192 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9194 /* set empty favicon */
9195 xt_icon_from_name(t
, "text-html");
9197 /* and show it all */
9198 gtk_widget_show_all(b
);
9199 gtk_widget_show_all(t
->vbox
);
9201 /* compact tab bar */
9202 t
->tab_elems
.label
= gtk_label_new(title
);
9203 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9204 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9205 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9206 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9208 t
->tab_elems
.eventbox
= gtk_event_box_new();
9209 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9210 t
->tab_elems
.sep
= gtk_vseparator_new();
9212 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9213 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9214 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9215 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9216 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9217 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9219 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9221 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9223 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9226 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9228 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9230 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9233 id
= position
>= 0 ? position
:
9234 gtk_notebook_get_current_page(notebook
) + 1;
9235 if (id
> gtk_notebook_get_n_pages(notebook
))
9238 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9239 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9240 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9241 t
->tab_elems
.eventbox
, id
);
9246 #if GTK_CHECK_VERSION(2, 20, 0)
9247 /* turn spinner off if we are a new tab without uri */
9249 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9250 gtk_widget_hide(t
->spinner
);
9253 /* make notebook tabs reorderable */
9254 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9256 /* compact tabs clickable */
9257 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9258 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9260 g_object_connect(G_OBJECT(t
->cmd
),
9261 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9262 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9263 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9264 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9265 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9268 /* reuse wv_button_cb to hide oops */
9269 g_object_connect(G_OBJECT(t
->oops
),
9270 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9273 g_signal_connect(t
->buffers
,
9274 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9275 g_object_connect(G_OBJECT(t
->buffers
),
9276 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9278 g_object_connect(G_OBJECT(t
->wv
),
9279 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9280 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9281 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9282 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9283 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9284 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9285 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9286 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9287 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9288 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9289 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9290 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9291 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9292 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9293 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9294 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9296 g_signal_connect(t
->wv
,
9297 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9298 g_signal_connect(t
->wv
,
9299 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9301 /* hijack the unused keys as if we were the browser */
9302 g_object_connect(G_OBJECT(t
->toolbar
),
9303 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9306 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9307 G_CALLBACK(tab_close_cb
), t
);
9310 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9311 /* restore the tab's history */
9312 if (u
&& u
->history
) {
9316 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9317 items
= g_list_next(items
);
9320 item
= g_list_nth_data(u
->history
, u
->back
);
9322 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9325 g_list_free(u
->history
);
9327 webkit_web_back_forward_list_clear(t
->bfl
);
9333 url_set_visibility();
9334 statusbar_set_visibility();
9337 set_current_tab(t
->tab_id
);
9338 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9342 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9346 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9353 recolor_compact_tabs();
9354 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9359 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9365 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9367 if (gtk_notebook_get_current_page(notebook
) == -1)
9370 TAILQ_FOREACH(t
, &tabs
, entry
) {
9371 if (t
->tab_id
== pn
) {
9372 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9375 uri
= get_title(t
, TRUE
);
9376 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9382 /* can't use focus_webview here */
9383 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9390 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9393 struct tab
*t
= NULL
, *tt
;
9397 TAILQ_FOREACH(tt
, &tabs
, entry
)
9398 if (tt
->tab_id
== pn
) {
9403 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9405 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9410 menuitem_response(struct tab
*t
)
9412 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9416 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9418 GtkWidget
*menu
, *menu_items
;
9419 GdkEventButton
*bevent
;
9423 if (event
->type
== GDK_BUTTON_PRESS
) {
9424 bevent
= (GdkEventButton
*) event
;
9425 menu
= gtk_menu_new();
9427 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9428 if ((uri
= get_uri(ti
)) == NULL
)
9429 /* XXX make sure there is something to print */
9430 /* XXX add gui pages in here to look purdy */
9432 menu_items
= gtk_menu_item_new_with_label(uri
);
9433 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9434 gtk_widget_show(menu_items
);
9436 g_signal_connect_swapped((menu_items
),
9437 "activate", G_CALLBACK(menuitem_response
),
9441 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9442 bevent
->button
, bevent
->time
);
9444 /* unref object so it'll free itself when popped down */
9445 #if !GTK_CHECK_VERSION(3, 0, 0)
9446 /* XXX does not need unref with gtk+3? */
9447 g_object_ref_sink(menu
);
9448 g_object_unref(menu
);
9451 return (TRUE
/* eat event */);
9454 return (FALSE
/* propagate */);
9458 icon_size_map(int icon_size
)
9460 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9461 icon_size
> GTK_ICON_SIZE_DIALOG
)
9462 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9468 create_button(char *name
, char *stockid
, int size
)
9470 GtkWidget
*button
, *image
;
9474 rcstring
= g_strdup_printf(
9475 "style \"%s-style\"\n"
9477 " GtkWidget::focus-padding = 0\n"
9478 " GtkWidget::focus-line-width = 0\n"
9482 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9483 gtk_rc_parse_string(rcstring
);
9485 button
= gtk_button_new();
9486 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9487 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9489 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9490 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9491 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9492 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9493 gtk_widget_set_name(button
, name
);
9494 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9500 button_set_stockid(GtkWidget
*button
, char *stockid
)
9504 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9505 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9506 gtk_button_set_image(GTK_BUTTON(button
), image
);
9510 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9513 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9516 if (xterm_workaround
== 0)
9520 * xterm doesn't play nice with clipboards because it clears the
9521 * primary when clicked. We rely on primary being set to properly
9522 * handle middle mouse button clicks (paste). So when someone clears
9523 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9524 * other application behavior (as in DON'T clear primary).
9527 p
= gtk_clipboard_wait_for_text(primary
);
9529 if (gdk_property_get(gdk_get_default_root_window(),
9531 gdk_atom_intern("STRING", FALSE
),
9533 1024 * 1024 /* picked out of my butt */,
9539 /* yes sir, we need to NUL the string */
9541 gtk_clipboard_set_text(primary
, p
, -1);
9555 char file
[PATH_MAX
];
9558 vbox
= gtk_vbox_new(FALSE
, 0);
9559 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9560 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9561 #if !GTK_CHECK_VERSION(3, 0, 0)
9562 /* XXX seems to be needed with gtk+2 */
9563 gtk_notebook_set_tab_hborder(notebook
, 0);
9564 gtk_notebook_set_tab_vborder(notebook
, 0);
9566 gtk_notebook_set_scrollable(notebook
, TRUE
);
9567 gtk_notebook_set_show_border(notebook
, FALSE
);
9568 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9570 abtn
= gtk_button_new();
9571 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9572 gtk_widget_set_size_request(arrow
, -1, -1);
9573 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9574 gtk_widget_set_size_request(abtn
, -1, 20);
9576 #if GTK_CHECK_VERSION(2, 20, 0)
9577 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9579 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9581 /* compact tab bar */
9582 tab_bar
= gtk_hbox_new(TRUE
, 0);
9584 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9585 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9586 gtk_widget_set_size_request(vbox
, -1, -1);
9588 g_object_connect(G_OBJECT(notebook
),
9589 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9591 g_object_connect(G_OBJECT(notebook
),
9592 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9593 NULL
, (char *)NULL
);
9594 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9595 G_CALLBACK(arrow_cb
), NULL
);
9597 main_window
= create_window();
9598 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9601 for (i
= 0; i
< LENGTH(icons
); i
++) {
9602 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9603 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9604 l
= g_list_append(l
, pb
);
9606 gtk_window_set_default_icon_list(l
);
9608 /* clipboard work around */
9609 if (xterm_workaround
)
9611 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9612 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9614 gtk_widget_show_all(abtn
);
9615 gtk_widget_show_all(main_window
);
9616 notebook_tab_set_visibility();
9620 set_hook(void **hook
, char *name
)
9623 errx(1, "set_hook");
9625 if (*hook
== NULL
) {
9626 *hook
= dlsym(RTLD_NEXT
, name
);
9628 errx(1, "can't hook %s", name
);
9632 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9634 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9636 g_return_val_if_fail(cookie1
, FALSE
);
9637 g_return_val_if_fail(cookie2
, FALSE
);
9639 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9640 !strcmp (cookie1
->value
, cookie2
->value
) &&
9641 !strcmp (cookie1
->path
, cookie2
->path
) &&
9642 !strcmp (cookie1
->domain
, cookie2
->domain
));
9646 transfer_cookies(void)
9649 SoupCookie
*sc
, *pc
;
9651 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9653 for (;cf
; cf
= cf
->next
) {
9655 sc
= soup_cookie_copy(pc
);
9656 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9659 soup_cookies_free(cf
);
9663 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9668 print_cookie("soup_cookie_jar_delete_cookie", c
);
9670 if (cookies_enabled
== 0)
9673 if (jar
== NULL
|| c
== NULL
)
9676 /* find and remove from persistent jar */
9677 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9679 for (;cf
; cf
= cf
->next
) {
9681 if (soup_cookie_equal(ci
, c
)) {
9682 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9687 soup_cookies_free(cf
);
9689 /* delete from session jar */
9690 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9694 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9696 struct domain
*d
= NULL
;
9700 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9701 jar
, p_cookiejar
, s_cookiejar
);
9703 if (cookies_enabled
== 0)
9706 /* see if we are up and running */
9707 if (p_cookiejar
== NULL
) {
9708 _soup_cookie_jar_add_cookie(jar
, cookie
);
9711 /* disallow p_cookiejar adds, shouldn't happen */
9712 if (jar
== p_cookiejar
)
9716 if (jar
== NULL
|| cookie
== NULL
)
9719 if (enable_cookie_whitelist
&&
9720 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9722 DNPRINTF(XT_D_COOKIE
,
9723 "soup_cookie_jar_add_cookie: reject %s\n",
9725 if (save_rejected_cookies
) {
9726 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9727 show_oops(NULL
, "can't open reject cookie file");
9730 fseek(r_cookie_f
, 0, SEEK_END
);
9731 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9732 cookie
->http_only
? "#HttpOnly_" : "",
9734 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9736 cookie
->secure
? "TRUE" : "FALSE",
9738 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9745 if (!allow_volatile_cookies
)
9749 if (cookie
->expires
== NULL
&& session_timeout
) {
9750 soup_cookie_set_expires(cookie
,
9751 soup_date_new_from_now(session_timeout
));
9752 print_cookie("modified add cookie", cookie
);
9755 /* see if we are white listed for persistence */
9756 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9757 /* add to persistent jar */
9758 c
= soup_cookie_copy(cookie
);
9759 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9760 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9763 /* add to session jar */
9764 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9765 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9771 char file
[PATH_MAX
];
9773 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9774 "soup_cookie_jar_add_cookie");
9775 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9776 "soup_cookie_jar_delete_cookie");
9778 if (cookies_enabled
== 0)
9782 * the following code is intricate due to overriding several libsoup
9784 * do not alter order of these operations.
9787 /* rejected cookies */
9788 if (save_rejected_cookies
)
9789 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9792 /* persistent cookies */
9793 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9794 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9796 /* session cookies */
9797 s_cookiejar
= soup_cookie_jar_new();
9798 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9799 cookie_policy
, (void *)NULL
);
9802 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9806 setup_proxy(char *uri
)
9809 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9810 soup_uri_free(proxy_uri
);
9814 if (http_proxy
!= uri
) {
9821 http_proxy
= g_strdup(uri
);
9822 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9823 proxy_uri
= soup_uri_new(http_proxy
);
9824 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
9825 g_object_set(session
, "proxy-uri", proxy_uri
,
9831 set_http_proxy(char *proxy
)
9838 /* see if we need to clear it instead */
9839 if (strlen(proxy
) == 0) {
9844 uri
= soup_uri_new(proxy
);
9845 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9856 send_cmd_to_socket(char *cmd
)
9859 struct sockaddr_un sa
;
9861 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9862 warnx("%s: socket", __func__
);
9866 sa
.sun_family
= AF_UNIX
;
9867 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9868 work_dir
, XT_SOCKET_FILE
);
9871 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9872 warnx("%s: connect", __func__
);
9876 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9877 warnx("%s: send", __func__
);
9888 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9891 char str
[XT_MAX_URL_LENGTH
];
9892 socklen_t t
= sizeof(struct sockaddr_un
);
9893 struct sockaddr_un sa
;
9898 gint fd
= g_io_channel_unix_get_fd(source
);
9900 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9905 if (getpeereid(s
, &uid
, &gid
) == -1) {
9909 if (uid
!= getuid() || gid
!= getgid()) {
9910 warnx("unauthorized user");
9916 warnx("not a valid user");
9920 n
= recv(s
, str
, sizeof(str
), 0);
9924 tt
= TAILQ_LAST(&tabs
, tab_list
);
9925 cmd_execute(tt
, str
);
9933 struct sockaddr_un sa
;
9935 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9936 warn("is_running: socket");
9940 sa
.sun_family
= AF_UNIX
;
9941 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9942 work_dir
, XT_SOCKET_FILE
);
9945 /* connect to see if there is a listener */
9946 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9947 rv
= 0; /* not running */
9949 rv
= 1; /* already running */
9960 struct sockaddr_un sa
;
9962 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9963 warn("build_socket: socket");
9967 sa
.sun_family
= AF_UNIX
;
9968 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9969 work_dir
, XT_SOCKET_FILE
);
9972 /* connect to see if there is a listener */
9973 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9974 /* no listener so we will */
9975 unlink(sa
.sun_path
);
9977 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9978 warn("build_socket: bind");
9982 if (listen(s
, 1) == -1) {
9983 warn("build_socket: listen");
9996 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9997 GtkTreeIter
*iter
, struct tab
*t
)
10001 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10002 load_uri(t
, value
);
10009 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10010 GtkTreeIter
*iter
, struct tab
*t
)
10014 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10015 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10016 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10023 completion_add_uri(const gchar
*uri
)
10027 /* add uri to list_store */
10028 gtk_list_store_append(completion_model
, &iter
);
10029 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10033 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10034 GtkTreeIter
*iter
, gpointer user_data
)
10037 gboolean match
= FALSE
;
10039 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10045 match
= match_uri(value
, key
);
10052 completion_add(struct tab
*t
)
10054 /* enable completion for tab */
10055 t
->completion
= gtk_entry_completion_new();
10056 gtk_entry_completion_set_text_column(t
->completion
, 0);
10057 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10058 gtk_entry_completion_set_model(t
->completion
,
10059 GTK_TREE_MODEL(completion_model
));
10060 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10062 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10063 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10064 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10065 G_CALLBACK(completion_select_cb
), t
);
10066 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10067 G_CALLBACK(completion_hover_cb
), t
);
10075 if (stat(dir
, &sb
)) {
10076 if (mkdir(dir
, S_IRWXU
) == -1)
10077 err(1, "mkdir %s", dir
);
10078 if (stat(dir
, &sb
))
10079 err(1, "stat %s", dir
);
10081 if (S_ISDIR(sb
.st_mode
) == 0)
10082 errx(1, "%s not a dir", dir
);
10083 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10084 warnx("fixing invalid permissions on %s", dir
);
10085 if (chmod(dir
, S_IRWXU
) == -1)
10086 err(1, "chmod %s", dir
);
10094 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10100 main(int argc
, char *argv
[])
10103 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10104 char conf
[PATH_MAX
] = { '\0' };
10105 char file
[PATH_MAX
];
10106 char *env_proxy
= NULL
;
10110 struct sigaction sact
;
10111 GIOChannel
*channel
;
10117 gtk_init(&argc
, &argv
);
10119 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10123 RB_INIT(&downloads
);
10125 TAILQ_INIT(&sessions
);
10128 TAILQ_INIT(&aliases
);
10129 TAILQ_INIT(&undos
);
10135 /* fiddle with ulimits */
10136 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10139 /* just use them all */
10140 rlp
.rlim_cur
= rlp
.rlim_max
;
10141 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10143 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10145 else if (rlp
.rlim_cur
<= 256)
10146 startpage_add("%s requires at least 256 file "
10147 "descriptors, currently it has up to %d available",
10148 __progname
, rlp
.rlim_cur
);
10151 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10160 errx(0 , "Version: %s", version
);
10163 strlcpy(conf
, optarg
, sizeof(conf
));
10166 strlcpy(named_session
, optarg
, sizeof(named_session
));
10185 init_keybindings();
10187 gnutls_global_init();
10189 /* generate session keys for xtp pages */
10190 generate_xtp_session_key(&dl_session_key
);
10191 generate_xtp_session_key(&hl_session_key
);
10192 generate_xtp_session_key(&cl_session_key
);
10193 generate_xtp_session_key(&fl_session_key
);
10196 bzero(&sact
, sizeof(sact
));
10197 sigemptyset(&sact
.sa_mask
);
10198 sact
.sa_handler
= sigchild
;
10199 sact
.sa_flags
= SA_NOCLDSTOP
;
10200 sigaction(SIGCHLD
, &sact
, NULL
);
10202 /* set download dir */
10203 pwd
= getpwuid(getuid());
10205 errx(1, "invalid user %d", getuid());
10206 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10208 /* compile buffer command regexes */
10211 /* set default string settings */
10212 home
= g_strdup("https://www.cyphertite.com");
10213 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10214 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10215 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10216 cmd_font_name
= g_strdup("monospace normal 9");
10217 oops_font_name
= g_strdup("monospace normal 9");
10218 statusbar_font_name
= g_strdup("monospace normal 9");
10219 tabbar_font_name
= g_strdup("monospace normal 9");
10220 statusbar_elems
= g_strdup("BP");
10221 encoding
= g_strdup("ISO-8859-1");
10223 /* read config file */
10224 if (strlen(conf
) == 0)
10225 snprintf(conf
, sizeof conf
, "%s/.%s",
10226 pwd
->pw_dir
, XT_CONF_FILE
);
10227 config_parse(conf
, 0);
10230 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10231 oops_font
= pango_font_description_from_string(oops_font_name
);
10232 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10233 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10235 /* working directory */
10236 if (strlen(work_dir
) == 0)
10237 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10238 pwd
->pw_dir
, XT_DIR
);
10241 /* icon cache dir */
10242 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10243 xxx_dir(cache_dir
);
10246 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10247 xxx_dir(certs_dir
);
10250 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10251 work_dir
, XT_SESSIONS_DIR
);
10252 xxx_dir(sessions_dir
);
10254 /* runtime settings that can override config file */
10255 if (runtime_settings
[0] != '\0')
10256 config_parse(runtime_settings
, 1);
10259 if (!strcmp(download_dir
, pwd
->pw_dir
))
10260 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10261 xxx_dir(download_dir
);
10263 /* favorites file */
10264 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10265 if (stat(file
, &sb
)) {
10266 warnx("favorites file doesn't exist, creating it");
10267 if ((f
= fopen(file
, "w")) == NULL
)
10268 err(1, "favorites");
10272 /* quickmarks file */
10273 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10274 if (stat(file
, &sb
)) {
10275 warnx("quickmarks file doesn't exist, creating it");
10276 if ((f
= fopen(file
, "w")) == NULL
)
10277 err(1, "quickmarks");
10281 /* search history */
10282 if (history_autosave
) {
10283 snprintf(search_file
, sizeof search_file
, "%s/%s",
10284 work_dir
, XT_SEARCH_FILE
);
10285 if (stat(search_file
, &sb
)) {
10286 warnx("search history file doesn't exist, creating it");
10287 if ((f
= fopen(search_file
, "w")) == NULL
)
10288 err(1, "search_history");
10291 history_read(&shl
, search_file
, &search_history_count
);
10294 /* command history */
10295 if (history_autosave
) {
10296 snprintf(command_file
, sizeof command_file
, "%s/%s",
10297 work_dir
, XT_COMMAND_FILE
);
10298 if (stat(command_file
, &sb
)) {
10299 warnx("command history file doesn't exist, creating it");
10300 if ((f
= fopen(command_file
, "w")) == NULL
)
10301 err(1, "command_history");
10304 history_read(&chl
, command_file
, &cmd_history_count
);
10308 session
= webkit_get_default_session();
10313 if (stat(ssl_ca_file
, &sb
)) {
10314 warnx("no CA file: %s", ssl_ca_file
);
10315 g_free(ssl_ca_file
);
10316 ssl_ca_file
= NULL
;
10318 g_object_set(session
,
10319 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10320 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10324 /* set default encoding */
10325 g_object_set(session
, "default-encoding", encoding
, (char *)NULL
);
10327 /* guess_search regex */
10328 if (url_regex
== NULL
)
10329 url_regex
= g_strdup(XT_URL_REGEX
);
10331 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10332 startpage_add("invalid url regex %s", url_regex
);
10335 env_proxy
= getenv("http_proxy");
10337 setup_proxy(env_proxy
);
10339 setup_proxy(http_proxy
);
10342 send_cmd_to_socket(argv
[0]);
10346 /* set some connection parameters */
10347 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10348 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10351 /* see if there is already an xxxterm running */
10352 if (single_instance
&& is_running()) {
10354 warnx("already running");
10359 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10360 send_cmd_to_socket(cmd
);
10370 /* uri completion */
10371 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10374 buffers_store
= gtk_list_store_new
10375 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10381 notebook_tab_set_visibility();
10383 if (save_global_history
)
10384 restore_global_history();
10386 /* restore session list */
10387 restore_sessions_list();
10389 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10390 restore_saved_tabs();
10392 a
.s
= named_session
;
10393 a
.i
= XT_SES_DONOTHING
;
10394 open_tabs(NULL
, &a
);
10397 /* see if we have an exception */
10398 if (!TAILQ_EMPTY(&spl
)) {
10399 create_new_tab("about:startpage", NULL
, focus
, -1);
10404 create_new_tab(argv
[0], NULL
, focus
, -1);
10411 if (TAILQ_EMPTY(&tabs
))
10412 create_new_tab(home
, NULL
, 1, -1);
10415 if ((s
= build_socket()) != -1) {
10416 channel
= g_io_channel_unix_new(s
);
10417 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10422 gnutls_global_deinit();