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 <JavaScriptCore/JavaScript.h>
78 #include <gnutls/gnutls.h>
79 #include <gnutls/x509.h>
82 #include "javascript.h"
84 /* comment if you don't want to use threads */
90 GCRY_THREAD_OPTION_PTHREAD_IMPL
;
95 javascript.h borrowed from vimprobable2 under the following license:
97 Copyright (c) 2009 Leon Winter
98 Copyright (c) 2009 Hannes Schueller
99 Copyright (c) 2009 Matto Fransen
101 Permission is hereby granted, free of charge, to any person obtaining a copy
102 of this software and associated documentation files (the "Software"), to deal
103 in the Software without restriction, including without limitation the rights
104 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
105 copies of the Software, and to permit persons to whom the Software is
106 furnished to do so, subject to the following conditions:
108 The above copyright notice and this permission notice shall be included in
109 all copies or substantial portions of the Software.
111 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
112 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
113 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
114 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
115 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
116 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
120 static char *version
= XXXTERM_VERSION
;
122 /* hooked functions */
123 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
124 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
129 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
130 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
131 #define XT_D_MOVE 0x0001
132 #define XT_D_KEY 0x0002
133 #define XT_D_TAB 0x0004
134 #define XT_D_URL 0x0008
135 #define XT_D_CMD 0x0010
136 #define XT_D_NAV 0x0020
137 #define XT_D_DOWNLOAD 0x0040
138 #define XT_D_CONFIG 0x0080
139 #define XT_D_JS 0x0100
140 #define XT_D_FAVORITE 0x0200
141 #define XT_D_PRINTING 0x0400
142 #define XT_D_COOKIE 0x0800
143 #define XT_D_KEYBINDING 0x1000
144 #define XT_D_CLIP 0x2000
145 #define XT_D_BUFFERCMD 0x4000
146 u_int32_t swm_debug
= 0
164 #define DPRINTF(x...)
165 #define DNPRINTF(n,x...)
168 #define LENGTH(x) (sizeof x / sizeof x[0])
169 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
170 ~(GDK_BUTTON1_MASK) & \
171 ~(GDK_BUTTON2_MASK) & \
172 ~(GDK_BUTTON3_MASK) & \
173 ~(GDK_BUTTON4_MASK) & \
176 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
187 TAILQ_ENTRY(tab
) entry
;
189 GtkWidget
*tab_content
;
198 GtkWidget
*uri_entry
;
199 GtkWidget
*search_entry
;
201 GtkWidget
*browser_win
;
202 GtkWidget
*statusbar_box
;
204 GtkWidget
*statusbar
;
205 GtkWidget
*buffercmd
;
216 GtkWidget
*js_toggle
;
217 GtkEntryCompletion
*completion
;
221 WebKitWebHistoryItem
*item
;
222 WebKitWebBackForwardList
*bfl
;
225 WebKitNetworkRequest
*icon_request
;
226 WebKitDownload
*icon_download
;
227 gchar
*icon_dest_uri
;
229 /* adjustments for browser */
232 GtkAdjustment
*adjust_h
;
233 GtkAdjustment
*adjust_v
;
239 int xtp_meaning
; /* identifies dls/favorites */
241 int popup
; /* 1 if cmd_entry has popup visible */
243 /* https thread stuff */
249 #define XT_HINT_NONE (0)
250 #define XT_HINT_NUMERICAL (1)
251 #define XT_HINT_ALPHANUM (2)
255 /* custom stylesheet */
265 WebKitWebSettings
*settings
;
269 double mark
[XT_NOMARKS
];
271 TAILQ_HEAD(tab_list
, tab
);
274 RB_ENTRY(history
) entry
;
278 RB_HEAD(history_list
, history
);
281 TAILQ_ENTRY(session
) entry
;
284 TAILQ_HEAD(session_list
, session
);
287 RB_ENTRY(download
) entry
;
289 WebKitDownload
*download
;
292 RB_HEAD(download_list
, download
);
295 RB_ENTRY(domain
) entry
;
297 int handy
; /* app use */
299 RB_HEAD(domain_list
, domain
);
302 TAILQ_ENTRY(undo
) entry
;
305 int back
; /* Keeps track of how many back
306 * history items there are. */
308 TAILQ_HEAD(undo_tailq
, undo
);
312 TAILQ_ENTRY(sp
) entry
;
314 TAILQ_HEAD(sp_list
, sp
);
316 struct command_entry
{
318 TAILQ_ENTRY(command_entry
) entry
;
320 TAILQ_HEAD(command_list
, command_entry
);
322 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
323 int next_download_id
= 1;
332 #define XT_NAME ("XXXTerm")
333 #define XT_DIR (".xxxterm")
334 #define XT_CACHE_DIR ("cache")
335 #define XT_CERT_DIR ("certs/")
336 #define XT_SESSIONS_DIR ("sessions/")
337 #define XT_CONF_FILE ("xxxterm.conf")
338 #define XT_FAVS_FILE ("favorites")
339 #define XT_QMARKS_FILE ("quickmarks")
340 #define XT_SAVED_TABS_FILE ("main_session")
341 #define XT_RESTART_TABS_FILE ("restart_tabs")
342 #define XT_SOCKET_FILE ("socket")
343 #define XT_HISTORY_FILE ("history")
344 #define XT_REJECT_FILE ("rejected.txt")
345 #define XT_COOKIE_FILE ("cookies.txt")
346 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
347 #define XT_SEARCH_FILE ("search_history")
348 #define XT_COMMAND_FILE ("command_history")
349 #define XT_CB_HANDLED (TRUE)
350 #define XT_CB_PASSTHROUGH (FALSE)
351 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
352 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
353 #define XT_DLMAN_REFRESH "10"
354 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
355 "td{overflow: hidden;" \
356 " padding: 2px 2px 2px 2px;" \
357 " border: 1px solid black;" \
358 " vertical-align:top;" \
359 " word-wrap: break-word}\n" \
360 "tr:hover{background: #ffff99}\n" \
361 "th{background-color: #cccccc;" \
362 " border: 1px solid black}\n" \
363 "table{width: 100%%;" \
364 " border: 1px black solid;" \
365 " border-collapse:collapse}\n" \
367 "border: 1px solid black;" \
370 ".progress-inner{float: left;" \
372 " background: green}\n" \
373 ".dlstatus{font-size: small;" \
374 " text-align: center}\n" \
376 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
377 #define XT_MAX_UNDO_CLOSE_TAB (32)
378 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
379 #define XT_PRINT_EXTRA_MARGIN 10
380 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
381 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
384 #define XT_COLOR_RED "#cc0000"
385 #define XT_COLOR_YELLOW "#ffff66"
386 #define XT_COLOR_BLUE "lightblue"
387 #define XT_COLOR_GREEN "#99ff66"
388 #define XT_COLOR_WHITE "white"
389 #define XT_COLOR_BLACK "black"
391 #define XT_COLOR_CT_BACKGROUND "#000000"
392 #define XT_COLOR_CT_INACTIVE "#dddddd"
393 #define XT_COLOR_CT_ACTIVE "#bbbb00"
394 #define XT_COLOR_CT_SEPARATOR "#555555"
396 #define XT_COLOR_SB_SEPARATOR "#555555"
398 #define XT_PROTO_DELIM "://"
401 * xxxterm "protocol" (xtp)
402 * We use this for managing stuff like downloads and favorites. They
403 * make magical HTML pages in memory which have xxxt:// links in order
404 * to communicate with xxxterm's internals. These links take the format:
405 * xxxt://class/session_key/action/arg
407 * Don't begin xtp class/actions as 0. atoi returns that on error.
409 * Typically we have not put addition of items in this framework, as
410 * adding items is either done via an ex-command or via a keybinding instead.
413 #define XT_XTP_STR "xxxt://"
415 /* XTP classes (xxxt://<class>) */
416 #define XT_XTP_INVALID 0 /* invalid */
417 #define XT_XTP_DL 1 /* downloads */
418 #define XT_XTP_HL 2 /* history */
419 #define XT_XTP_CL 3 /* cookies */
420 #define XT_XTP_FL 4 /* favorites */
422 /* XTP download actions */
423 #define XT_XTP_DL_LIST 1
424 #define XT_XTP_DL_CANCEL 2
425 #define XT_XTP_DL_REMOVE 3
427 /* XTP history actions */
428 #define XT_XTP_HL_LIST 1
429 #define XT_XTP_HL_REMOVE 2
431 /* XTP cookie actions */
432 #define XT_XTP_CL_LIST 1
433 #define XT_XTP_CL_REMOVE 2
435 /* XTP cookie actions */
436 #define XT_XTP_FL_LIST 1
437 #define XT_XTP_FL_REMOVE 2
440 #define XT_MOVE_INVALID (0)
441 #define XT_MOVE_DOWN (1)
442 #define XT_MOVE_UP (2)
443 #define XT_MOVE_BOTTOM (3)
444 #define XT_MOVE_TOP (4)
445 #define XT_MOVE_PAGEDOWN (5)
446 #define XT_MOVE_PAGEUP (6)
447 #define XT_MOVE_HALFDOWN (7)
448 #define XT_MOVE_HALFUP (8)
449 #define XT_MOVE_LEFT (9)
450 #define XT_MOVE_FARLEFT (10)
451 #define XT_MOVE_RIGHT (11)
452 #define XT_MOVE_FARRIGHT (12)
453 #define XT_MOVE_PERCENT (13)
455 #define XT_QMARK_SET (0)
456 #define XT_QMARK_OPEN (1)
457 #define XT_QMARK_TAB (2)
459 #define XT_MARK_SET (0)
460 #define XT_MARK_GOTO (1)
462 #define XT_TAB_LAST (-4)
463 #define XT_TAB_FIRST (-3)
464 #define XT_TAB_PREV (-2)
465 #define XT_TAB_NEXT (-1)
466 #define XT_TAB_INVALID (0)
467 #define XT_TAB_NEW (1)
468 #define XT_TAB_DELETE (2)
469 #define XT_TAB_DELQUIT (3)
470 #define XT_TAB_OPEN (4)
471 #define XT_TAB_UNDO_CLOSE (5)
472 #define XT_TAB_SHOW (6)
473 #define XT_TAB_HIDE (7)
474 #define XT_TAB_NEXTSTYLE (8)
476 #define XT_NAV_INVALID (0)
477 #define XT_NAV_BACK (1)
478 #define XT_NAV_FORWARD (2)
479 #define XT_NAV_RELOAD (3)
481 #define XT_FOCUS_INVALID (0)
482 #define XT_FOCUS_URI (1)
483 #define XT_FOCUS_SEARCH (2)
485 #define XT_SEARCH_INVALID (0)
486 #define XT_SEARCH_NEXT (1)
487 #define XT_SEARCH_PREV (2)
489 #define XT_PASTE_CURRENT_TAB (0)
490 #define XT_PASTE_NEW_TAB (1)
492 #define XT_ZOOM_IN (-1)
493 #define XT_ZOOM_OUT (-2)
494 #define XT_ZOOM_NORMAL (100)
496 #define XT_URL_SHOW (1)
497 #define XT_URL_HIDE (2)
499 #define XT_WL_TOGGLE (1<<0)
500 #define XT_WL_ENABLE (1<<1)
501 #define XT_WL_DISABLE (1<<2)
502 #define XT_WL_FQDN (1<<3) /* default */
503 #define XT_WL_TOPLEVEL (1<<4)
504 #define XT_WL_PERSISTENT (1<<5)
505 #define XT_WL_SESSION (1<<6)
506 #define XT_WL_RELOAD (1<<7)
508 #define XT_SHOW (1<<7)
509 #define XT_DELETE (1<<8)
510 #define XT_SAVE (1<<9)
511 #define XT_OPEN (1<<10)
513 #define XT_CMD_OPEN (0)
514 #define XT_CMD_OPEN_CURRENT (1)
515 #define XT_CMD_TABNEW (2)
516 #define XT_CMD_TABNEW_CURRENT (3)
518 #define XT_STATUS_NOTHING (0)
519 #define XT_STATUS_LINK (1)
520 #define XT_STATUS_URI (2)
521 #define XT_STATUS_LOADING (3)
523 #define XT_SES_DONOTHING (0)
524 #define XT_SES_CLOSETABS (1)
526 #define XT_BM_NORMAL (0)
527 #define XT_BM_WHITELIST (1)
528 #define XT_BM_KIOSK (2)
530 #define XT_PREFIX (1<<0)
531 #define XT_USERARG (1<<1)
532 #define XT_URLARG (1<<2)
533 #define XT_INTARG (1<<3)
534 #define XT_SESSARG (1<<4)
535 #define XT_SETARG (1<<5)
537 #define XT_TABS_NORMAL 0
538 #define XT_TABS_COMPACT 1
540 #define XT_BUFCMD_SZ (8)
548 TAILQ_ENTRY(mime_type
) entry
;
550 TAILQ_HEAD(mime_type_list
, mime_type
);
556 TAILQ_ENTRY(alias
) entry
;
558 TAILQ_HEAD(alias_list
, alias
);
560 /* settings that require restart */
561 int tabless
= 0; /* allow only 1 tab */
562 int enable_socket
= 0;
563 int single_instance
= 0; /* only allow one xxxterm to run */
564 int fancy_bar
= 1; /* fancy toolbar */
565 int browser_mode
= XT_BM_NORMAL
;
566 int enable_localstorage
= 1;
567 char *statusbar_elems
= NULL
;
569 /* runtime settings */
570 int show_tabs
= 1; /* show tabs on notebook */
571 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
572 int show_url
= 1; /* show url toolbar on notebook */
573 int show_statusbar
= 0; /* vimperator style status bar */
574 int ctrl_click_focus
= 0; /* ctrl click gets focus */
575 int cookies_enabled
= 1; /* enable cookies */
576 int read_only_cookies
= 0; /* enable to not write cookies */
577 int enable_scripts
= 1;
578 int enable_plugins
= 0;
579 gfloat default_zoom_level
= 1.0;
580 char default_script
[PATH_MAX
];
581 int window_height
= 768;
582 int window_width
= 1024;
583 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
584 int refresh_interval
= 10; /* download refresh interval */
585 int enable_cookie_whitelist
= 0;
586 int enable_js_whitelist
= 0;
587 int session_timeout
= 3600; /* cookie session timeout */
588 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
589 char *ssl_ca_file
= NULL
;
590 char *resource_dir
= NULL
;
591 gboolean ssl_strict_certs
= FALSE
;
592 int append_next
= 1; /* append tab after current tab */
594 char *search_string
= NULL
;
595 char *http_proxy
= NULL
;
596 char download_dir
[PATH_MAX
];
597 char runtime_settings
[PATH_MAX
]; /* override of settings */
598 int allow_volatile_cookies
= 0;
599 int save_global_history
= 0; /* save global history to disk */
600 char *user_agent
= NULL
;
601 int save_rejected_cookies
= 0;
602 int session_autosave
= 0;
603 int guess_search
= 0;
604 int dns_prefetch
= FALSE
;
605 gint max_connections
= 25;
606 gint max_host_connections
= 5;
607 gint enable_spell_checking
= 0;
608 char *spell_check_languages
= NULL
;
609 int xterm_workaround
= 0;
610 char *url_regex
= NULL
;
611 int history_autosave
= 0;
612 char search_file
[PATH_MAX
];
613 char command_file
[PATH_MAX
];
614 char *encoding
= NULL
;
616 char *cmd_font_name
= NULL
;
617 char *oops_font_name
= NULL
;
618 char *statusbar_font_name
= NULL
;
619 char *tabbar_font_name
= NULL
;
620 PangoFontDescription
*cmd_font
;
621 PangoFontDescription
*oops_font
;
622 PangoFontDescription
*statusbar_font
;
623 PangoFontDescription
*tabbar_font
;
624 char *qmarks
[XT_NOMARKS
];
626 int btn_down
; /* M1 down in any wv */
627 regex_t url_re
; /* guess_search regex */
631 int set_browser_mode(struct settings
*, char *);
632 int set_cookie_policy(struct settings
*, char *);
633 int set_download_dir(struct settings
*, char *);
634 int set_default_script(struct settings
*, char *);
635 int set_runtime_dir(struct settings
*, char *);
636 int set_tab_style(struct settings
*, char *);
637 int set_work_dir(struct settings
*, char *);
638 int add_alias(struct settings
*, char *);
639 int add_mime_type(struct settings
*, char *);
640 int add_cookie_wl(struct settings
*, char *);
641 int add_js_wl(struct settings
*, char *);
642 int add_kb(struct settings
*, char *);
643 void button_set_stockid(GtkWidget
*, char *);
644 GtkWidget
* create_button(char *, char *, int);
646 char *get_browser_mode(struct settings
*);
647 char *get_cookie_policy(struct settings
*);
648 char *get_download_dir(struct settings
*);
649 char *get_default_script(struct settings
*);
650 char *get_runtime_dir(struct settings
*);
651 char *get_tab_style(struct settings
*);
652 char *get_work_dir(struct settings
*);
653 void startpage_add(const char *, ...);
655 void walk_alias(struct settings
*, void (*)(struct settings
*,
656 char *, void *), void *);
657 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
658 char *, void *), void *);
659 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
660 char *, void *), void *);
661 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
663 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
664 char *, void *), void *);
666 void recalc_tabs(void);
667 void recolor_compact_tabs(void);
668 void set_current_tab(int page_num
);
669 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
670 void marks_clear(struct tab
*t
);
672 int set_http_proxy(char *);
675 int (*set
)(struct settings
*, char *);
676 char *(*get
)(struct settings
*);
677 void (*walk
)(struct settings
*,
678 void (*cb
)(struct settings
*, char *, void *),
682 struct special s_browser_mode
= {
688 struct special s_cookie
= {
694 struct special s_alias
= {
700 struct special s_mime
= {
706 struct special s_js
= {
712 struct special s_kb
= {
718 struct special s_cookie_wl
= {
724 struct special s_default_script
= {
730 struct special s_download_dir
= {
736 struct special s_work_dir
= {
742 struct special s_tab_style
= {
751 #define XT_S_INVALID (0)
754 #define XT_S_FLOAT (3)
756 #define XT_SF_RESTART (1<<0)
757 #define XT_SF_RUNTIME (1<<1)
762 int (*activate
)(char *);
764 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
765 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
766 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
767 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
768 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
769 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
770 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
771 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
772 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
773 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
774 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
775 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
776 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
777 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
778 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
779 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
780 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
781 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
782 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
783 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
784 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
785 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
786 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
787 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
788 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
789 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
790 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
791 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
792 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
793 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
794 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
795 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
796 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
797 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
798 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
799 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
800 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
801 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
802 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
803 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
804 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
805 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
806 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
807 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
808 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
809 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
810 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
811 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
814 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
815 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
816 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
817 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
819 /* runtime settings */
820 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
821 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
822 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
823 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
824 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
827 int about(struct tab
*, struct karg
*);
828 int blank(struct tab
*, struct karg
*);
829 int ca_cmd(struct tab
*, struct karg
*);
830 int cookie_show_wl(struct tab
*, struct karg
*);
831 int js_show_wl(struct tab
*, struct karg
*);
832 int help(struct tab
*, struct karg
*);
833 int set(struct tab
*, struct karg
*);
834 int stats(struct tab
*, struct karg
*);
835 int marco(struct tab
*, struct karg
*);
836 int startpage(struct tab
*, struct karg
*);
837 const char * marco_message(int *);
838 int xtp_page_cl(struct tab
*, struct karg
*);
839 int xtp_page_dl(struct tab
*, struct karg
*);
840 int xtp_page_fl(struct tab
*, struct karg
*);
841 int xtp_page_hl(struct tab
*, struct karg
*);
842 void xt_icon_from_file(struct tab
*, char *);
843 const gchar
*get_uri(struct tab
*);
844 const gchar
*get_title(struct tab
*, bool);
846 #define XT_URI_ABOUT ("about:")
847 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
848 #define XT_URI_ABOUT_ABOUT ("about")
849 #define XT_URI_ABOUT_BLANK ("blank")
850 #define XT_URI_ABOUT_CERTS ("certs")
851 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
852 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
853 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
854 #define XT_URI_ABOUT_FAVORITES ("favorites")
855 #define XT_URI_ABOUT_HELP ("help")
856 #define XT_URI_ABOUT_HISTORY ("history")
857 #define XT_URI_ABOUT_JSWL ("jswl")
858 #define XT_URI_ABOUT_SET ("set")
859 #define XT_URI_ABOUT_STATS ("stats")
860 #define XT_URI_ABOUT_MARCO ("marco")
861 #define XT_URI_ABOUT_STARTPAGE ("startpage")
865 int (*func
)(struct tab
*, struct karg
*);
867 { XT_URI_ABOUT_ABOUT
, about
},
868 { XT_URI_ABOUT_BLANK
, blank
},
869 { XT_URI_ABOUT_CERTS
, ca_cmd
},
870 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
871 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
872 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
873 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
874 { XT_URI_ABOUT_HELP
, help
},
875 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
876 { XT_URI_ABOUT_JSWL
, js_show_wl
},
877 { XT_URI_ABOUT_SET
, set
},
878 { XT_URI_ABOUT_STATS
, stats
},
879 { XT_URI_ABOUT_MARCO
, marco
},
880 { XT_URI_ABOUT_STARTPAGE
, startpage
},
883 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
884 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
885 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
886 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
887 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
888 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
889 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
892 extern char *__progname
;
895 GtkWidget
*main_window
;
896 GtkNotebook
*notebook
;
898 GtkWidget
*arrow
, *abtn
;
899 struct tab_list tabs
;
900 struct history_list hl
;
901 struct session_list sessions
;
902 struct download_list downloads
;
903 struct domain_list c_wl
;
904 struct domain_list js_wl
;
905 struct undo_tailq undos
;
906 struct keybinding_list kbl
;
908 struct command_list chl
;
909 struct command_list shl
;
910 struct command_entry
*history_at
;
911 struct command_entry
*search_at
;
913 int updating_dl_tabs
= 0;
914 int updating_hl_tabs
= 0;
915 int updating_cl_tabs
= 0;
916 int updating_fl_tabs
= 0;
917 int cmd_history_count
= 0;
918 int search_history_count
= 0;
920 uint64_t blocked_cookies
= 0;
921 char named_session
[PATH_MAX
];
922 GtkListStore
*completion_model
;
923 GtkListStore
*buffers_store
;
925 void xxx_dir(char *);
926 int icon_size_map(int);
927 void completion_add(struct tab
*);
928 void completion_add_uri(const gchar
*);
929 void show_oops(struct tab
*, const char *, ...);
932 history_delete(struct command_list
*l
, int *counter
)
934 struct command_entry
*c
;
936 if (l
== NULL
|| counter
== NULL
)
939 c
= TAILQ_LAST(l
, command_list
);
943 TAILQ_REMOVE(l
, c
, entry
);
950 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
952 struct command_entry
*c
;
955 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
958 /* don't add the same line */
959 c
= TAILQ_FIRST(list
);
961 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
964 c
= g_malloc0(sizeof *c
);
965 c
->line
= g_strdup_printf(" %s", l
);
968 TAILQ_INSERT_HEAD(list
, c
, entry
);
971 history_delete(list
, counter
);
973 if (history_autosave
&& file
) {
974 f
= fopen(file
, "w");
976 show_oops(NULL
, "couldn't write history %s", file
);
980 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
982 fprintf(f
, "%s\n", c
->line
);
990 history_read(struct command_list
*list
, char *file
, int *counter
)
993 char *s
, line
[65536];
995 if (list
== NULL
|| file
== NULL
)
998 f
= fopen(file
, "r");
1000 startpage_add("couldn't open history file %s", file
);
1005 s
= fgets(line
, sizeof line
, f
);
1006 if (s
== NULL
|| feof(f
) || ferror(f
))
1008 if ((s
= strchr(line
, '\n')) == NULL
) {
1009 startpage_add("invalid history file %s", file
);
1015 history_add(list
, NULL
, line
+ 1, counter
);
1023 /* marks and quickmarks array storage.
1024 * first a-z, then A-Z, then 0-9 */
1031 if (i
>= 0 && i
<= 'z' - 'a')
1035 if (i
>= 0 && i
<= 'Z' - 'A')
1050 if (m
>= 'a' && m
<= 'z')
1051 return ret
+ m
- 'a';
1053 ret
+= 'z' - 'a' + 1;
1054 if (m
>= 'A' && m
<= 'Z')
1055 return ret
+ m
- 'A';
1057 ret
+= 'Z' - 'A' + 1;
1058 if (m
>= '0' && m
<= '9')
1059 return ret
+ m
- '0';
1068 int saved_errno
, status
;
1071 saved_errno
= errno
;
1073 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1077 if (errno
!= ECHILD
) {
1079 clog_warn("sigchild: waitpid:");
1085 if (WIFEXITED(status
)) {
1086 if (WEXITSTATUS(status
) != 0) {
1088 clog_warnx("sigchild: child exit status: %d",
1089 WEXITSTATUS(status));
1094 clog_warnx("sigchild: child is terminated abnormally");
1099 errno
= saved_errno
;
1103 is_g_object_setting(GObject
*o
, char *str
)
1105 guint n_props
= 0, i
;
1106 GParamSpec
**proplist
;
1108 if (! G_IS_OBJECT(o
))
1111 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1114 for (i
=0; i
< n_props
; i
++) {
1115 if (! strcmp(proplist
[i
]->name
, str
))
1122 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1126 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1128 "<title>%s</title>\n"
1137 addstyles
? XT_PAGE_STYLE
: "",
1146 * Display a web page from a HTML string in memory, rather than from a URL
1149 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1151 char file
[PATH_MAX
];
1154 /* we set this to indicate we want to manually do navaction */
1156 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1158 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1160 /* set t->xtp_meaning */
1161 for (i
= 0; i
< LENGTH(about_list
); i
++)
1162 if (!strcmp(title
, about_list
[i
].name
)) {
1167 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
1169 #if GTK_CHECK_VERSION(2, 20, 0)
1170 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1171 gtk_widget_hide(t
->spinner
);
1173 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1174 xt_icon_from_file(t
, file
);
1179 get_current_tab(void)
1183 TAILQ_FOREACH(t
, &tabs
, entry
) {
1184 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1188 warnx("%s: no current tab", __func__
);
1194 set_status(struct tab
*t
, gchar
*s
, int status
)
1202 case XT_STATUS_LOADING
:
1203 type
= g_strdup_printf("Loading: %s", s
);
1206 case XT_STATUS_LINK
:
1207 type
= g_strdup_printf("Link: %s", s
);
1209 t
->status
= g_strdup(gtk_entry_get_text(
1210 GTK_ENTRY(t
->sbe
.statusbar
)));
1214 type
= g_strdup_printf("%s", s
);
1216 t
->status
= g_strdup(type
);
1220 t
->status
= g_strdup(s
);
1222 case XT_STATUS_NOTHING
:
1227 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1233 hide_cmd(struct tab
*t
)
1235 history_at
= NULL
; /* just in case */
1236 search_at
= NULL
; /* just in case */
1237 gtk_widget_hide(t
->cmd
);
1241 show_cmd(struct tab
*t
)
1245 gtk_widget_hide(t
->oops
);
1246 gtk_widget_show(t
->cmd
);
1250 hide_buffers(struct tab
*t
)
1252 gtk_widget_hide(t
->buffers
);
1253 gtk_list_store_clear(buffers_store
);
1263 sort_tabs_by_page_num(struct tab
***stabs
)
1268 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1270 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1272 TAILQ_FOREACH(t
, &tabs
, entry
)
1273 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1279 buffers_make_list(void)
1282 const gchar
*title
= NULL
;
1284 struct tab
**stabs
= NULL
;
1286 num_tabs
= sort_tabs_by_page_num(&stabs
);
1288 for (i
= 0; i
< num_tabs
; i
++)
1290 gtk_list_store_append(buffers_store
, &iter
);
1291 title
= get_title(stabs
[i
], FALSE
);
1292 gtk_list_store_set(buffers_store
, &iter
,
1293 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1303 show_buffers(struct tab
*t
)
1305 buffers_make_list();
1306 gtk_widget_show(t
->buffers
);
1307 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1311 toggle_buffers(struct tab
*t
)
1313 if (gtk_widget_get_visible(t
->buffers
))
1320 buffers(struct tab
*t
, struct karg
*args
)
1328 hide_oops(struct tab
*t
)
1330 gtk_widget_hide(t
->oops
);
1334 show_oops(struct tab
*at
, const char *fmt
, ...)
1338 struct tab
*t
= NULL
;
1344 if ((t
= get_current_tab()) == NULL
)
1350 if (vasprintf(&msg
, fmt
, ap
) == -1)
1351 errx(1, "show_oops failed");
1354 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1355 gtk_widget_hide(t
->cmd
);
1356 gtk_widget_show(t
->oops
);
1363 get_as_string(struct settings
*s
)
1374 warnx("get_as_string skip %s\n", s
->name
);
1375 } else if (s
->type
== XT_S_INT
)
1376 r
= g_strdup_printf("%d", *s
->ival
);
1377 else if (s
->type
== XT_S_STR
)
1378 r
= g_strdup(*s
->sval
);
1379 else if (s
->type
== XT_S_FLOAT
)
1380 r
= g_strdup_printf("%f", *s
->fval
);
1382 r
= g_strdup_printf("INVALID TYPE");
1388 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1393 for (i
= 0; i
< LENGTH(rs
); i
++) {
1394 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1395 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1397 s
= get_as_string(&rs
[i
]);
1398 cb(&rs
[i
], s
, cb_args
);
1405 set_browser_mode(struct settings
*s
, char *val
)
1407 if (!strcmp(val
, "whitelist")) {
1408 browser_mode
= XT_BM_WHITELIST
;
1409 allow_volatile_cookies
= 0;
1410 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1411 cookies_enabled
= 1;
1412 enable_cookie_whitelist
= 1;
1413 read_only_cookies
= 0;
1414 save_rejected_cookies
= 0;
1415 session_timeout
= 3600;
1417 enable_js_whitelist
= 1;
1418 enable_localstorage
= 0;
1419 } else if (!strcmp(val
, "normal")) {
1420 browser_mode
= XT_BM_NORMAL
;
1421 allow_volatile_cookies
= 0;
1422 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1423 cookies_enabled
= 1;
1424 enable_cookie_whitelist
= 0;
1425 read_only_cookies
= 0;
1426 save_rejected_cookies
= 0;
1427 session_timeout
= 3600;
1429 enable_js_whitelist
= 0;
1430 enable_localstorage
= 1;
1431 } else if (!strcmp(val
, "kiosk")) {
1432 browser_mode
= XT_BM_KIOSK
;
1433 allow_volatile_cookies
= 0;
1434 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1435 cookies_enabled
= 1;
1436 enable_cookie_whitelist
= 0;
1437 read_only_cookies
= 0;
1438 save_rejected_cookies
= 0;
1439 session_timeout
= 3600;
1441 enable_js_whitelist
= 0;
1442 enable_localstorage
= 1;
1452 get_browser_mode(struct settings
*s
)
1456 if (browser_mode
== XT_BM_WHITELIST
)
1457 r
= g_strdup("whitelist");
1458 else if (browser_mode
== XT_BM_NORMAL
)
1459 r
= g_strdup("normal");
1460 else if (browser_mode
== XT_BM_KIOSK
)
1461 r
= g_strdup("kiosk");
1469 set_cookie_policy(struct settings
*s
, char *val
)
1471 if (!strcmp(val
, "no3rdparty"))
1472 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1473 else if (!strcmp(val
, "accept"))
1474 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1475 else if (!strcmp(val
, "reject"))
1476 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1484 get_cookie_policy(struct settings
*s
)
1488 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1489 r
= g_strdup("no3rdparty");
1490 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1491 r
= g_strdup("accept");
1492 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1493 r
= g_strdup("reject");
1501 get_default_script(struct settings
*s
)
1503 if (default_script
[0] == '\0')
1505 return (g_strdup(default_script
));
1509 set_default_script(struct settings
*s
, char *val
)
1512 snprintf(default_script
, sizeof default_script
, "%s/%s",
1513 pwd
->pw_dir
, &val
[1]);
1515 strlcpy(default_script
, val
, sizeof default_script
);
1521 get_download_dir(struct settings
*s
)
1523 if (download_dir
[0] == '\0')
1525 return (g_strdup(download_dir
));
1529 set_download_dir(struct settings
*s
, char *val
)
1532 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1533 pwd
->pw_dir
, &val
[1]);
1535 strlcpy(download_dir
, val
, sizeof download_dir
);
1542 * We use these to prevent people putting xxxt:// URLs on
1543 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1545 #define XT_XTP_SES_KEY_SZ 8
1546 #define XT_XTP_SES_KEY_HEX_FMT \
1547 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1548 char *dl_session_key
; /* downloads */
1549 char *hl_session_key
; /* history list */
1550 char *cl_session_key
; /* cookie list */
1551 char *fl_session_key
; /* favorites list */
1553 char work_dir
[PATH_MAX
];
1554 char certs_dir
[PATH_MAX
];
1555 char cache_dir
[PATH_MAX
];
1556 char sessions_dir
[PATH_MAX
];
1557 char cookie_file
[PATH_MAX
];
1558 SoupURI
*proxy_uri
= NULL
;
1559 SoupSession
*session
;
1560 SoupCookieJar
*s_cookiejar
;
1561 SoupCookieJar
*p_cookiejar
;
1562 char rc_fname
[PATH_MAX
];
1564 struct mime_type_list mtl
;
1565 struct alias_list aliases
;
1568 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1569 void delete_tab(struct tab
*);
1570 void setzoom_webkit(struct tab
*, int);
1571 int run_script(struct tab
*, char *);
1572 int download_rb_cmp(struct download
*, struct download
*);
1573 gboolean
cmd_execute(struct tab
*t
, char *str
);
1576 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1578 return (strcmp(h1
->uri
, h2
->uri
));
1580 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1583 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1585 return (strcmp(d1
->d
, d2
->d
));
1587 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1590 get_work_dir(struct settings
*s
)
1592 if (work_dir
[0] == '\0')
1594 return (g_strdup(work_dir
));
1598 set_work_dir(struct settings
*s
, char *val
)
1601 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1602 pwd
->pw_dir
, &val
[1]);
1604 strlcpy(work_dir
, val
, sizeof work_dir
);
1610 get_tab_style(struct settings
*s
)
1612 if (tab_style
== XT_TABS_NORMAL
)
1613 return (g_strdup("normal"));
1615 return (g_strdup("compact"));
1619 set_tab_style(struct settings
*s
, char *val
)
1621 if (!strcmp(val
, "normal"))
1622 tab_style
= XT_TABS_NORMAL
;
1623 else if (!strcmp(val
, "compact"))
1624 tab_style
= XT_TABS_COMPACT
;
1632 * generate a session key to secure xtp commands.
1633 * pass in a ptr to the key in question and it will
1634 * be modified in place.
1637 generate_xtp_session_key(char **key
)
1639 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1645 /* make a new one */
1646 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1647 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1648 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1649 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1651 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1655 * validate a xtp session key.
1659 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1661 if (strcmp(trusted
, untrusted
) != 0) {
1662 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1671 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1673 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1675 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1677 struct valid_url_types
{
1688 valid_url_type(char *url
)
1692 for (i
= 0; i
< LENGTH(vut
); i
++)
1693 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1700 print_cookie(char *msg
, SoupCookie
*c
)
1706 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1707 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1708 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1709 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1710 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1711 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1712 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1713 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1714 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1715 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1719 walk_alias(struct settings
*s
,
1720 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1725 if (s
== NULL
|| cb
== NULL
) {
1726 show_oops(NULL
, "walk_alias invalid parameters");
1730 TAILQ_FOREACH(a
, &aliases
, entry
) {
1731 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1732 cb(s
, str
, cb_args
);
1738 match_alias(char *url_in
)
1742 char *url_out
= NULL
, *search
, *enc_arg
;
1744 search
= g_strdup(url_in
);
1746 if (strsep(&arg
, " \t") == NULL
) {
1747 show_oops(NULL
, "match_alias: NULL URL");
1751 TAILQ_FOREACH(a
, &aliases
, entry
) {
1752 if (!strcmp(search
, a
->a_name
))
1757 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1760 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1761 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1764 url_out
= g_strdup_printf(a
->a_uri
, "");
1772 guess_url_type(char *url_in
)
1775 char *url_out
= NULL
, *enc_search
= NULL
;
1778 /* substitute aliases */
1779 url_out
= match_alias(url_in
);
1780 if (url_out
!= NULL
)
1783 /* see if we are an about page */
1784 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1785 for (i
= 0; i
< LENGTH(about_list
); i
++)
1786 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1787 about_list
[i
].name
)) {
1788 url_out
= g_strdup(url_in
);
1792 if (guess_search
&& url_regex
&&
1793 !(g_str_has_prefix(url_in
, "http://") ||
1794 g_str_has_prefix(url_in
, "https://"))) {
1795 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1796 /* invalid URI so search instead */
1797 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1798 url_out
= g_strdup_printf(search_string
, enc_search
);
1804 /* XXX not sure about this heuristic */
1805 if (stat(url_in
, &sb
) == 0)
1806 url_out
= g_strdup_printf("file://%s", url_in
);
1808 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1810 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1816 load_uri(struct tab
*t
, gchar
*uri
)
1819 gchar
*newuri
= NULL
;
1825 /* Strip leading spaces. */
1826 while (*uri
&& isspace(*uri
))
1829 if (strlen(uri
) == 0) {
1834 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1836 if (valid_url_type(uri
)) {
1837 newuri
= guess_url_type(uri
);
1841 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1842 for (i
= 0; i
< LENGTH(about_list
); i
++)
1843 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1844 bzero(&args
, sizeof args
);
1845 about_list
[i
].func(t
, &args
);
1846 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1850 show_oops(t
, "invalid about page");
1854 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1856 webkit_web_view_load_uri(t
->wv
, uri
);
1863 get_uri(struct tab
*t
)
1865 const gchar
*uri
= NULL
;
1867 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1869 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1870 uri
= webkit_web_view_get_uri(t
->wv
);
1872 /* use tmp_uri to make sure it is g_freed */
1875 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1876 about_list
[t
->xtp_meaning
].name
);
1883 get_title(struct tab
*t
, bool window
)
1885 const gchar
*set
= NULL
, *title
= NULL
;
1886 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1888 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1889 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1892 title
= webkit_web_view_get_title(t
->wv
);
1893 if ((set
= title
? title
: get_uri(t
)))
1897 set
= window
? XT_NAME
: "(untitled)";
1903 add_alias(struct settings
*s
, char *line
)
1906 struct alias
*a
= NULL
;
1908 if (s
== NULL
|| line
== NULL
) {
1909 show_oops(NULL
, "add_alias invalid parameters");
1914 a
= g_malloc(sizeof(*a
));
1916 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1917 show_oops(NULL
, "add_alias: incomplete alias definition");
1920 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1921 show_oops(NULL
, "add_alias: invalid alias definition");
1925 a
->a_name
= g_strdup(alias
);
1926 a
->a_uri
= g_strdup(l
);
1928 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1930 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1940 add_mime_type(struct settings
*s
, char *line
)
1944 struct mime_type
*m
= NULL
;
1945 int downloadfirst
= 0;
1947 /* XXX this could be smarter */
1949 if (line
== NULL
|| strlen(line
) == 0) {
1950 show_oops(NULL
, "add_mime_type invalid parameters");
1959 m
= g_malloc(sizeof(*m
));
1961 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1962 show_oops(NULL
, "add_mime_type: invalid mime_type");
1965 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1966 mime_type
[strlen(mime_type
) - 1] = '\0';
1971 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1972 show_oops(NULL
, "add_mime_type: invalid mime_type");
1976 m
->mt_type
= g_strdup(mime_type
);
1977 m
->mt_action
= g_strdup(l
);
1978 m
->mt_download
= downloadfirst
;
1980 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1981 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1983 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1993 find_mime_type(char *mime_type
)
1995 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1997 TAILQ_FOREACH(m
, &mtl
, entry
) {
1998 if (m
->mt_default
&&
1999 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
2002 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
2015 walk_mime_type(struct settings
*s
,
2016 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2018 struct mime_type
*m
;
2021 if (s
== NULL
|| cb
== NULL
) {
2022 show_oops(NULL
, "walk_mime_type invalid parameters");
2026 TAILQ_FOREACH(m
, &mtl
, entry
) {
2027 str
= g_strdup_printf("%s%s --> %s",
2029 m
->mt_default
? "*" : "",
2031 cb(s
, str
, cb_args
);
2037 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2043 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2046 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2048 /* treat *.moo.com the same as .moo.com */
2049 if (str
[0] == '*' && str
[1] == '.')
2051 else if (str
[0] == '.')
2056 /* slice off port number */
2057 p
= g_strrstr(str
, ":");
2061 d
= g_malloc(sizeof *d
);
2063 d
->d
= g_strdup_printf(".%s", str
);
2065 d
->d
= g_strdup(str
);
2068 if (RB_INSERT(domain_list
, wl
, d
))
2071 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2082 add_cookie_wl(struct settings
*s
, char *entry
)
2084 wl_add(entry
, &c_wl
, 1);
2089 walk_cookie_wl(struct settings
*s
,
2090 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2094 if (s
== NULL
|| cb
== NULL
) {
2095 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2099 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2100 cb(s
, d
->d
, cb_args
);
2104 walk_js_wl(struct settings
*s
,
2105 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2109 if (s
== NULL
|| cb
== NULL
) {
2110 show_oops(NULL
, "walk_js_wl invalid parameters");
2114 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2115 cb(s
, d
->d
, cb_args
);
2119 add_js_wl(struct settings
*s
, char *entry
)
2121 wl_add(entry
, &js_wl
, 1 /* persistent */);
2126 wl_find(const gchar
*search
, struct domain_list
*wl
)
2129 struct domain
*d
= NULL
, dfind
;
2132 if (search
== NULL
|| wl
== NULL
)
2134 if (strlen(search
) < 2)
2137 if (search
[0] != '.')
2138 s
= g_strdup_printf(".%s", search
);
2140 s
= g_strdup(search
);
2142 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2145 d
= RB_FIND(domain_list
, wl
, &dfind
);
2159 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2165 if (s
== NULL
|| wl
== NULL
)
2168 if (!strncmp(s
, "http://", strlen("http://")))
2169 s
= &s
[strlen("http://")];
2170 else if (!strncmp(s
, "https://", strlen("https://")))
2171 s
= &s
[strlen("https://")];
2176 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2177 /* chop string at first slash */
2178 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2181 r
= wl_find(ss
, wl
);
2190 settings_add(char *var
, char *val
)
2197 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2198 if (strcmp(var
, rs
[i
].name
))
2202 if (rs
[i
].s
->set(&rs
[i
], val
))
2203 errx(1, "invalid value for %s: %s", var
, val
);
2207 switch (rs
[i
].type
) {
2216 errx(1, "invalid sval for %s",
2230 errx(1, "invalid type for %s", var
);
2239 config_parse(char *filename
, int runtime
)
2242 char *line
, *cp
, *var
, *val
;
2243 size_t len
, lineno
= 0;
2245 char file
[PATH_MAX
];
2248 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2250 if (filename
== NULL
)
2253 if (runtime
&& runtime_settings
[0] != '\0') {
2254 snprintf(file
, sizeof file
, "%s/%s",
2255 work_dir
, runtime_settings
);
2256 if (stat(file
, &sb
)) {
2257 warnx("runtime file doesn't exist, creating it");
2258 if ((f
= fopen(file
, "w")) == NULL
)
2260 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2264 strlcpy(file
, filename
, sizeof file
);
2266 if ((config
= fopen(file
, "r")) == NULL
) {
2267 warn("config_parse: cannot open %s", filename
);
2272 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2273 if (feof(config
) || ferror(config
))
2277 cp
+= (long)strspn(cp
, WS
);
2278 if (cp
[0] == '\0') {
2284 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2285 startpage_add("invalid configuration file entry: %s",
2288 cp
+= (long)strspn(cp
, WS
);
2290 if ((val
= strsep(&cp
, "\0")) == NULL
)
2293 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2294 handled
= settings_add(var
, val
);
2296 startpage_add("invalid configuration file entry: %s=%s",
2306 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2312 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2316 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2319 JSStringGetUTF8CString(jsref
, s
, l
);
2320 JSStringRelease(jsref
);
2326 disable_hints(struct tab
*t
)
2328 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2329 bzero(t
->hint_num
, sizeof t
->hint_num
);
2330 run_script(t
, "vimprobable_clear()");
2332 t
->hint_mode
= XT_HINT_NONE
;
2336 enable_hints(struct tab
*t
)
2338 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2339 run_script(t
, "vimprobable_show_hints()");
2341 t
->hint_mode
= XT_HINT_NONE
;
2344 #define XT_JS_OPEN ("open;")
2345 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2346 #define XT_JS_FIRE ("fire;")
2347 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2348 #define XT_JS_FOUND ("found;")
2349 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2352 run_script(struct tab
*t
, char *s
)
2354 JSGlobalContextRef ctx
;
2355 WebKitWebFrame
*frame
;
2357 JSValueRef val
, exception
;
2360 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2361 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2363 frame
= webkit_web_view_get_main_frame(t
->wv
);
2364 ctx
= webkit_web_frame_get_global_context(frame
);
2366 str
= JSStringCreateWithUTF8CString(s
);
2367 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2368 NULL
, 0, &exception
);
2369 JSStringRelease(str
);
2371 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2373 es
= js_ref_to_string(ctx
, exception
);
2374 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2378 es
= js_ref_to_string(ctx
, val
);
2379 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2381 /* handle return value right here */
2382 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2385 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2388 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2389 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2390 &es
[XT_JS_FIRE_LEN
]);
2395 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2396 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2407 hint(struct tab
*t
, struct karg
*args
)
2410 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2412 if (t
->hints_on
== 0)
2421 apply_style(struct tab
*t
)
2423 g_object_set(G_OBJECT(t
->settings
),
2424 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2428 userstyle(struct tab
*t
, struct karg
*args
)
2430 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2434 g_object_set(G_OBJECT(t
->settings
),
2435 "user-stylesheet-uri", NULL
, (char *)NULL
);
2444 * Doesn't work fully, due to the following bug:
2445 * https://bugs.webkit.org/show_bug.cgi?id=51747
2448 restore_global_history(void)
2450 char file
[PATH_MAX
];
2455 const char delim
[3] = {'\\', '\\', '\0'};
2457 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2459 if ((f
= fopen(file
, "r")) == NULL
) {
2460 warnx("%s: fopen", __func__
);
2465 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2466 if (feof(f
) || ferror(f
))
2469 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2470 if (feof(f
) || ferror(f
)) {
2472 warnx("%s: broken history file\n", __func__
);
2476 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2477 webkit_web_history_item_new_with_data(uri
, title
);
2478 h
= g_malloc(sizeof(struct history
));
2479 h
->uri
= g_strdup(uri
);
2480 h
->title
= g_strdup(title
);
2481 RB_INSERT(history_list
, &hl
, h
);
2482 completion_add_uri(h
->uri
);
2484 warnx("%s: failed to restore history\n", __func__
);
2500 save_global_history_to_disk(struct tab
*t
)
2502 char file
[PATH_MAX
];
2506 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2508 if ((f
= fopen(file
, "w")) == NULL
) {
2509 show_oops(t
, "%s: global history file: %s",
2510 __func__
, strerror(errno
));
2514 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2515 if (h
->uri
&& h
->title
)
2516 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2525 quit(struct tab
*t
, struct karg
*args
)
2527 if (save_global_history
)
2528 save_global_history_to_disk(t
);
2536 restore_sessions_list(void)
2539 struct dirent
*dp
= NULL
;
2542 sdir
= opendir(sessions_dir
);
2544 while ((dp
= readdir(sdir
)) != NULL
)
2545 if (dp
->d_type
== DT_REG
) {
2546 s
= g_malloc(sizeof(struct session
));
2547 s
->name
= g_strdup(dp
->d_name
);
2548 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2555 open_tabs(struct tab
*t
, struct karg
*a
)
2557 char file
[PATH_MAX
];
2561 struct tab
*ti
, *tt
;
2566 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2567 if ((f
= fopen(file
, "r")) == NULL
)
2570 ti
= TAILQ_LAST(&tabs
, tab_list
);
2573 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2574 if (feof(f
) || ferror(f
))
2577 /* retrieve session name */
2578 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2579 strlcpy(named_session
,
2580 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2581 sizeof named_session
);
2585 if (uri
&& strlen(uri
))
2586 create_new_tab(uri
, NULL
, 1, -1);
2592 /* close open tabs */
2593 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2595 tt
= TAILQ_FIRST(&tabs
);
2615 restore_saved_tabs(void)
2617 char file
[PATH_MAX
];
2618 int unlink_file
= 0;
2623 snprintf(file
, sizeof file
, "%s/%s",
2624 sessions_dir
, XT_RESTART_TABS_FILE
);
2625 if (stat(file
, &sb
) == -1)
2626 a
.s
= XT_SAVED_TABS_FILE
;
2629 a
.s
= XT_RESTART_TABS_FILE
;
2632 a
.i
= XT_SES_DONOTHING
;
2633 rv
= open_tabs(NULL
, &a
);
2642 save_tabs(struct tab
*t
, struct karg
*a
)
2644 char file
[PATH_MAX
];
2646 int num_tabs
= 0, i
;
2647 struct tab
**stabs
= NULL
;
2652 snprintf(file
, sizeof file
, "%s/%s",
2653 sessions_dir
, named_session
);
2655 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2657 if ((f
= fopen(file
, "w")) == NULL
) {
2658 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2662 /* save session name */
2663 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2665 /* Save tabs, in the order they are arranged in the notebook. */
2666 num_tabs
= sort_tabs_by_page_num(&stabs
);
2668 for (i
= 0; i
< num_tabs
; i
++)
2670 if (get_uri(stabs
[i
]) != NULL
)
2671 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2672 else if (gtk_entry_get_text(GTK_ENTRY(
2673 stabs
[i
]->uri_entry
)))
2674 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2675 stabs
[i
]->uri_entry
)));
2680 /* try and make sure this gets to disk NOW. XXX Backup first? */
2681 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2682 show_oops(t
, "May not have managed to save session: %s",
2692 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2704 run_page_script(struct tab
*t
, struct karg
*args
)
2707 char *tmp
, script
[PATH_MAX
];
2709 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2710 if (tmp
[0] == '\0') {
2711 show_oops(t
, "no script specified");
2715 if ((uri
= get_uri(t
)) == NULL
) {
2716 show_oops(t
, "tab is empty, not running script");
2721 snprintf(script
, sizeof script
, "%s/%s",
2722 pwd
->pw_dir
, &tmp
[1]);
2724 strlcpy(script
, tmp
, sizeof script
);
2728 show_oops(t
, "can't fork to run script");
2738 execlp(script
, script
, uri
, (void *)NULL
);
2748 yank_uri(struct tab
*t
, struct karg
*args
)
2751 GtkClipboard
*clipboard
;
2753 if ((uri
= get_uri(t
)) == NULL
)
2756 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2757 gtk_clipboard_set_text(clipboard
, uri
, -1);
2763 paste_uri(struct tab
*t
, struct karg
*args
)
2765 GtkClipboard
*clipboard
;
2766 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2768 gchar
*p
= NULL
, *uri
;
2770 /* try primary clipboard first */
2771 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2772 p
= gtk_clipboard_wait_for_text(clipboard
);
2774 /* if it failed get whatever text is in cut_buffer0 */
2775 if (p
== NULL
&& xterm_workaround
)
2776 if (gdk_property_get(gdk_get_default_root_window(),
2778 gdk_atom_intern("STRING", FALSE
),
2780 1024 * 1024 /* picked out of my butt */,
2786 /* yes sir, we need to NUL the string */
2792 while (*uri
&& isspace(*uri
))
2794 if (strlen(uri
) == 0) {
2795 show_oops(t
, "empty paste buffer");
2798 if (guess_search
== 0 && valid_url_type(uri
)) {
2799 /* we can be clever and paste this in search box */
2800 show_oops(t
, "not a valid URL");
2804 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2806 else if (args
->i
== XT_PASTE_NEW_TAB
)
2807 create_new_tab(uri
, NULL
, 1, -1);
2818 find_domain(const gchar
*s
, int toplevel
)
2826 uri
= soup_uri_new(s
);
2828 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2832 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2833 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2834 while(--p
>= uri
->host
&& *p
!= '.');
2841 ret
= g_strdup_printf(".%s", p
);
2849 toggle_cwl(struct tab
*t
, struct karg
*args
)
2860 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2862 if (uri
== NULL
|| dom
== NULL
||
2863 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2864 show_oops(t
, "Can't toggle domain in cookie white list");
2867 d
= wl_find(dom
, &c_wl
);
2874 if (args
->i
& XT_WL_TOGGLE
)
2876 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2878 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2882 /* enable cookies for domain */
2883 wl_add(dom
, &c_wl
, 0);
2885 /* disable cookies for domain */
2886 RB_REMOVE(domain_list
, &c_wl
, d
);
2888 if (args
->i
& XT_WL_RELOAD
)
2889 webkit_web_view_reload(t
->wv
);
2897 toggle_js(struct tab
*t
, struct karg
*args
)
2907 g_object_get(G_OBJECT(t
->settings
),
2908 "enable-scripts", &es
, (char *)NULL
);
2909 if (args
->i
& XT_WL_TOGGLE
)
2911 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2913 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2919 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2921 if (uri
== NULL
|| dom
== NULL
||
2922 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2923 show_oops(t
, "Can't toggle domain in JavaScript white list");
2928 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2929 wl_add(dom
, &js_wl
, 0 /* session */);
2931 d
= wl_find(dom
, &js_wl
);
2933 RB_REMOVE(domain_list
, &js_wl
, d
);
2934 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2936 g_object_set(G_OBJECT(t
->settings
),
2937 "enable-scripts", es
, (char *)NULL
);
2938 g_object_set(G_OBJECT(t
->settings
),
2939 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2940 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2942 if (args
->i
& XT_WL_RELOAD
)
2943 webkit_web_view_reload(t
->wv
);
2951 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2955 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2958 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2963 toggle_src(struct tab
*t
, struct karg
*args
)
2970 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2971 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2972 webkit_web_view_reload(t
->wv
);
2978 focus_webview(struct tab
*t
)
2983 /* only grab focus if we are visible */
2984 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2985 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2989 focus(struct tab
*t
, struct karg
*args
)
2991 if (t
== NULL
|| args
== NULL
)
2997 if (args
->i
== XT_FOCUS_URI
)
2998 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2999 else if (args
->i
== XT_FOCUS_SEARCH
)
3000 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
3006 stats(struct tab
*t
, struct karg
*args
)
3008 char *page
, *body
, *s
, line
[64 * 1024];
3009 uint64_t line_count
= 0;
3013 show_oops(NULL
, "stats invalid parameters");
3016 if (save_rejected_cookies
) {
3017 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
3019 s
= fgets(line
, sizeof line
, r_cookie_f
);
3020 if (s
== NULL
|| feof(r_cookie_f
) ||
3026 snprintf(line
, sizeof line
,
3027 "<br/>Cookies blocked(*) total: %llu", line_count
);
3029 show_oops(t
, "Can't open blocked cookies file: %s",
3033 body
= g_strdup_printf(
3034 "Cookies blocked(*) this session: %llu"
3036 "<p><small><b>*</b> results vary based on settings</small></p>",
3040 page
= get_html_page("Statistics", body
, "", 0);
3043 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3050 marco(struct tab
*t
, struct karg
*args
)
3052 char *page
, line
[64 * 1024];
3056 show_oops(NULL
, "marco invalid parameters");
3059 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3061 page
= get_html_page("Marco Sez...", line
, "", 0);
3063 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3070 blank(struct tab
*t
, struct karg
*args
)
3073 show_oops(NULL
, "blank invalid parameters");
3075 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3081 about(struct tab
*t
, struct karg
*args
)
3086 show_oops(NULL
, "about invalid parameters");
3088 body
= g_strdup_printf("<b>Version: %s</b>"
3089 #ifdef XXXTERM_BUILDSTR
3090 "<br><b>Build: %s</b>"
3095 "<li>Marco Peereboom <marco@peereboom.us></li>"
3096 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3097 "<li>Edd Barrett <vext01@gmail.com> </li>"
3098 "<li>Todd T. Fries <todd@fries.net> </li>"
3099 "<li>Raphael Graf <r@undefined.ch> </li>"
3101 "Copyrights and licenses can be found on the XXXTerm "
3102 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3104 #ifdef XXXTERM_BUILDSTR
3105 version
, XXXTERM_BUILDSTR
3111 page
= get_html_page("About", body
, "", 0);
3114 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3121 help(struct tab
*t
, struct karg
*args
)
3123 char *page
, *head
, *body
;
3126 show_oops(NULL
, "help invalid parameters");
3128 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3129 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3131 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3132 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3133 "cgi-bin/man-cgi?xxxterm</a>";
3135 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3137 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3144 startpage(struct tab
*t
, struct karg
*args
)
3146 char *page
, *body
, *b
;
3150 show_oops(NULL
, "startpage invalid parameters");
3152 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3154 TAILQ_FOREACH(s
, &spl
, entry
) {
3156 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3160 page
= get_html_page("Startup Exception", body
, "", 0);
3163 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3170 startpage_add(const char *fmt
, ...)
3180 if (vasprintf(&msg
, fmt
, ap
) == -1)
3181 errx(1, "startpage_add failed");
3184 s
= g_malloc0(sizeof *s
);
3187 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3191 * update all favorite tabs apart from one. Pass NULL if
3192 * you want to update all.
3195 update_favorite_tabs(struct tab
*apart_from
)
3198 if (!updating_fl_tabs
) {
3199 updating_fl_tabs
= 1; /* stop infinite recursion */
3200 TAILQ_FOREACH(t
, &tabs
, entry
)
3201 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3202 && (t
!= apart_from
))
3203 xtp_page_fl(t
, NULL
);
3204 updating_fl_tabs
= 0;
3208 /* show a list of favorites (bookmarks) */
3210 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3212 char file
[PATH_MAX
];
3214 char *uri
= NULL
, *title
= NULL
;
3215 size_t len
, lineno
= 0;
3217 char *body
, *tmp
, *page
= NULL
;
3218 const char delim
[3] = {'\\', '\\', '\0'};
3220 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3223 warn("%s: bad param", __func__
);
3225 /* new session key */
3226 if (!updating_fl_tabs
)
3227 generate_xtp_session_key(&fl_session_key
);
3229 /* open favorites */
3230 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3231 if ((f
= fopen(file
, "r")) == NULL
) {
3232 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3237 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3238 "<th style='width: 40px'>#</th><th>Link</th>"
3239 "<th style='width: 40px'>Rm</th></tr>\n");
3242 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3244 if (strlen(title
) == 0 || title
[0] == '#') {
3250 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3251 if (feof(f
) || ferror(f
)) {
3252 show_oops(t
, "favorites file corrupt");
3258 body
= g_strdup_printf("%s<tr>"
3260 "<td><a href='%s'>%s</a></td>"
3261 "<td style='text-align: center'>"
3262 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3264 body
, i
, uri
, title
,
3265 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3277 /* if none, say so */
3280 body
= g_strdup_printf("%s<tr>"
3281 "<td colspan='3' style='text-align: center'>"
3282 "No favorites - To add one use the 'favadd' command."
3283 "</td></tr>", body
);
3288 body
= g_strdup_printf("%s</table>", body
);
3298 page
= get_html_page("Favorites", body
, "", 1);
3299 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3303 update_favorite_tabs(t
);
3312 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3313 size_t cert_count
, char *title
)
3315 gnutls_datum_t cinfo
;
3319 body
= g_strdup("");
3321 for (i
= 0; i
< cert_count
; i
++) {
3322 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3327 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3328 body
, i
, cinfo
.data
);
3329 gnutls_free(cinfo
.data
);
3333 tmp
= get_html_page(title
, body
, "", 0);
3336 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3341 ca_cmd(struct tab
*t
, struct karg
*args
)
3344 int rv
= 1, certs
= 0, certs_read
;
3347 gnutls_x509_crt_t
*c
= NULL
;
3348 char *certs_buf
= NULL
, *s
;
3350 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3351 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3355 if (fstat(fileno(f
), &sb
) == -1) {
3356 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3360 certs_buf
= g_malloc(sb
.st_size
+ 1);
3361 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3362 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3365 certs_buf
[sb
.st_size
] = '\0';
3368 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3370 s
+= strlen("BEGIN CERTIFICATE");
3373 bzero(&dt
, sizeof dt
);
3374 dt
.data
= (unsigned char *)certs_buf
;
3375 dt
.size
= sb
.st_size
;
3376 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3377 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3378 GNUTLS_X509_FMT_PEM
, 0);
3379 if (certs_read
<= 0) {
3380 show_oops(t
, "No cert(s) available");
3383 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3396 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
3400 struct addrinfo hints
, *res
= NULL
, *ai
;
3401 int rv
= -1, s
= -1, on
, error
;
3403 static gchar myerror
[256]; /* this is not thread safe */
3406 *error_str
= myerror
;
3407 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3408 *error_str
= "invalid URI";
3412 su
= soup_uri_new(uri
);
3414 *error_str
= "invalid soup URI";
3417 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3418 *error_str
= "invalid HTTPS URI";
3422 snprintf(port
, sizeof port
, "%d", su
->port
);
3423 bzero(&hints
, sizeof(struct addrinfo
));
3424 hints
.ai_flags
= AI_CANONNAME
;
3425 hints
.ai_family
= AF_UNSPEC
;
3426 hints
.ai_socktype
= SOCK_STREAM
;
3428 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3429 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
3430 gai_strerror(errno
));
3434 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3440 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3442 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3445 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3448 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3452 snprintf(myerror
, sizeof myerror
,
3453 "could not obtain certificates from: %s",
3459 strlcpy(domain
, su
->host
, domain_sz
);
3466 if (rv
== -1 && s
!= -1)
3473 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3476 gnutls_deinit(gsession
);
3478 gnutls_certificate_free_credentials(xcred
);
3484 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
3485 gnutls_certificate_credentials_t
*xc
)
3487 gnutls_certificate_credentials_t xcred
;
3488 gnutls_session_t gsession
;
3490 static gchar myerror
[1024]; /* this is not thread safe */
3492 if (gs
== NULL
|| xc
== NULL
)
3499 gnutls_certificate_allocate_credentials(&xcred
);
3500 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3501 GNUTLS_X509_FMT_PEM
);
3503 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3504 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3505 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3506 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3507 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3508 snprintf(myerror
, sizeof myerror
,
3509 "gnutls_handshake failed %d fatal %d %s",
3511 gnutls_error_is_fatal(rv
),
3512 gnutls_strerror_name(rv
));
3513 stop_tls(gsession
, xcred
);
3517 gnutls_credentials_type_t cred
;
3518 cred
= gnutls_auth_get_type(gsession
);
3519 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3520 snprintf(myerror
, sizeof myerror
,
3521 "gnutls_auth_get_type failed %d",
3523 stop_tls(gsession
, xcred
);
3531 *error_str
= myerror
;
3536 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3540 const gnutls_datum_t
*cl
;
3541 gnutls_x509_crt_t
*all_certs
;
3544 if (certs
== NULL
|| cert_count
== NULL
)
3546 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3548 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3552 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3553 for (i
= 0; i
< len
; i
++) {
3554 gnutls_x509_crt_init(&all_certs
[i
]);
3555 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3556 GNUTLS_X509_FMT_PEM
< 0)) {
3570 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3574 for (i
= 0; i
< cert_count
; i
++)
3575 gnutls_x509_crt_deinit(certs
[i
]);
3580 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3582 GdkColor c_text
, c_base
;
3584 gdk_color_parse(text
, &c_text
);
3585 gdk_color_parse(base
, &c_base
);
3587 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3588 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3589 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3590 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3592 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3593 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3594 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3595 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3599 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3600 size_t cert_count
, char *domain
)
3603 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3608 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3611 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3612 if ((f
= fopen(file
, "w")) == NULL
) {
3613 show_oops(t
, "Can't create cert file %s %s",
3614 file
, strerror(errno
));
3618 for (i
= 0; i
< cert_count
; i
++) {
3619 cert_buf_sz
= sizeof cert_buf
;
3620 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3621 cert_buf
, &cert_buf_sz
)) {
3622 show_oops(t
, "gnutls_x509_crt_export failed");
3625 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3626 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3631 /* not the best spot but oh well */
3632 gdk_color_parse("lightblue", &color
);
3633 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3634 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3647 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
3649 char domain
[8182], file
[PATH_MAX
];
3650 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3652 unsigned int error
= 0;
3654 size_t cert_buf_sz
, cert_count
;
3655 enum cert_trust rv
= CERT_UNTRUSTED
;
3656 static gchar serr
[80]; /* this isn't thread safe */
3657 gnutls_session_t gsession
;
3658 gnutls_x509_crt_t
*certs
;
3659 gnutls_certificate_credentials_t xcred
;
3661 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3665 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
3666 sizeof domain
)) == -1)
3669 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3672 if (start_tls(error_str
, s
, &gsession
, &xcred
))
3674 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3676 /* verify certs in case cert file doesn't exist */
3677 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3679 *error_str
= "Invalid certificates";
3684 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3685 *error_str
= "Can't get connection certificates";
3689 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3690 if ((f
= fopen(file
, "r")) == NULL
) {
3696 for (i
= 0; i
< cert_count
; i
++) {
3697 cert_buf_sz
= sizeof cert_buf
;
3698 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3699 cert_buf
, &cert_buf_sz
)) {
3702 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3703 rv
= CERT_BAD
; /* critical */
3706 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3707 rv
= CERT_BAD
; /* critical */
3716 free_connection_certs(certs
, cert_count
);
3718 /* we close the socket first for speed */
3722 /* only complain if we didn't save it locally */
3723 if (error
&& rv
!= CERT_LOCAL
) {
3724 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3725 if (error
& GNUTLS_CERT_INVALID
)
3726 strlcat(serr
, "invalid, ", sizeof serr
);
3727 if (error
& GNUTLS_CERT_REVOKED
)
3728 strlcat(serr
, "revoked, ", sizeof serr
);
3729 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3730 strlcat(serr
, "signer not found, ", sizeof serr
);
3731 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3732 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3733 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3734 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3735 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3736 strlcat(serr
, "not activated, ", sizeof serr
);
3737 if (error
& GNUTLS_CERT_EXPIRED
)
3738 strlcat(serr
, "expired, ", sizeof serr
);
3739 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3740 if (serr
[i
] == ',') {
3747 stop_tls(gsession
, xcred
);
3753 cert_cmd(struct tab
*t
, struct karg
*args
)
3755 const gchar
*uri
, *error_str
= NULL
;
3759 gnutls_session_t gsession
;
3760 gnutls_x509_crt_t
*certs
;
3761 gnutls_certificate_credentials_t xcred
;
3766 if (ssl_ca_file
== NULL
) {
3767 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3771 if ((uri
= get_uri(t
)) == NULL
) {
3772 show_oops(t
, "Invalid URI");
3776 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
3777 sizeof domain
)) == -1) {
3778 show_oops(t
, "%s", error_str
);
3783 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
3787 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3788 show_oops(t
, "get_connection_certs failed");
3792 if (args
->i
& XT_SHOW
)
3793 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3794 else if (args
->i
& XT_SAVE
)
3795 save_certs(t
, certs
, cert_count
, domain
);
3797 free_connection_certs(certs
, cert_count
);
3799 /* we close the socket first for speed */
3802 stop_tls(gsession
, xcred
);
3804 show_oops(t
, "%s", error_str
);
3809 remove_cookie(int index
)
3815 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3817 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3819 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3823 print_cookie("remove cookie", c
);
3824 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3829 soup_cookies_free(cf
);
3835 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3840 body
= g_strdup("");
3843 if (args
->i
& XT_WL_PERSISTENT
) {
3845 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3847 RB_FOREACH(d
, domain_list
, wl
) {
3851 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3857 if (args
->i
& XT_WL_SESSION
) {
3859 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3861 RB_FOREACH(d
, domain_list
, wl
) {
3865 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3870 tmp
= get_html_page(title
, body
, "", 0);
3873 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3875 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3881 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3883 char file
[PATH_MAX
];
3885 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3893 if (t
== NULL
|| args
== NULL
)
3896 if (runtime_settings
[0] == '\0')
3899 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3900 if ((f
= fopen(file
, "r+")) == NULL
)
3904 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3905 if (uri
== NULL
|| dom
== NULL
||
3906 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3907 show_oops(t
, "Can't add domain to %s white list",
3908 js
? "JavaScript" : "cookie");
3912 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3915 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3918 if (!strcmp(line
, lt
))
3924 fprintf(f
, "%s\n", lt
);
3929 d
= wl_find(dom
, &js_wl
);
3931 settings_add("js_wl", dom
);
3932 d
= wl_find(dom
, &js_wl
);
3936 d
= wl_find(dom
, &c_wl
);
3938 settings_add("cookie_wl", dom
);
3939 d
= wl_find(dom
, &c_wl
);
3943 /* find and add to persistent jar */
3944 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3945 for (;cf
; cf
= cf
->next
) {
3947 if (!strcmp(dom
, ci
->domain
) ||
3948 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3949 c
= soup_cookie_copy(ci
);
3950 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3953 soup_cookies_free(cf
);
3971 js_show_wl(struct tab
*t
, struct karg
*args
)
3973 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3974 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3980 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3982 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3983 wl_show(t
, args
, "Cookie White List", &c_wl
);
3989 cookie_cmd(struct tab
*t
, struct karg
*args
)
3991 if (args
->i
& XT_SHOW
)
3992 wl_show(t
, args
, "Cookie White List", &c_wl
);
3993 else if (args
->i
& XT_WL_TOGGLE
) {
3994 args
->i
|= XT_WL_RELOAD
;
3995 toggle_cwl(t
, args
);
3996 } else if (args
->i
& XT_SAVE
) {
3997 args
->i
|= XT_WL_RELOAD
;
3998 wl_save(t
, args
, 0);
3999 } else if (args
->i
& XT_DELETE
)
4000 show_oops(t
, "'cookie delete' currently unimplemented");
4006 js_cmd(struct tab
*t
, struct karg
*args
)
4008 if (args
->i
& XT_SHOW
)
4009 wl_show(t
, args
, "JavaScript White List", &js_wl
);
4010 else if (args
->i
& XT_SAVE
) {
4011 args
->i
|= XT_WL_RELOAD
;
4012 wl_save(t
, args
, 1);
4013 } else if (args
->i
& XT_WL_TOGGLE
) {
4014 args
->i
|= XT_WL_RELOAD
;
4016 } else if (args
->i
& XT_DELETE
)
4017 show_oops(t
, "'js delete' currently unimplemented");
4023 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4025 js_toggle_cb(t
->js_toggle
, t
);
4031 add_favorite(struct tab
*t
, struct karg
*args
)
4033 char file
[PATH_MAX
];
4036 size_t urilen
, linelen
;
4037 const gchar
*uri
, *title
;
4042 /* don't allow adding of xtp pages to favorites */
4043 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4044 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4048 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4049 if ((f
= fopen(file
, "r+")) == NULL
) {
4050 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4054 title
= get_title(t
, FALSE
);
4057 if (title
== NULL
|| uri
== NULL
) {
4058 show_oops(t
, "can't add page to favorites");
4062 urilen
= strlen(uri
);
4065 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4066 if (feof(f
) || ferror(f
))
4069 if (linelen
== urilen
&& !strcmp(line
, uri
))
4076 fprintf(f
, "\n%s\n%s", title
, uri
);
4082 update_favorite_tabs(NULL
);
4088 can_go_back_for_real(struct tab
*t
)
4091 WebKitWebHistoryItem
*item
;
4097 /* rely on webkit to make sure we can go backward when on an about page */
4099 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4100 return (webkit_web_view_can_go_back(t
->wv
));
4102 /* the back/forwars list is stupid so help determine if we can go back */
4103 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4105 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4106 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4114 can_go_forward_for_real(struct tab
*t
)
4117 WebKitWebHistoryItem
*item
;
4123 /* rely on webkit to make sure we can go forward when on an about page */
4125 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4126 return (webkit_web_view_can_go_forward(t
->wv
));
4128 /* the back/forwars list is stupid so help selecting a different item */
4129 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4131 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4132 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4140 go_back_for_real(struct tab
*t
)
4143 WebKitWebHistoryItem
*item
;
4151 webkit_web_view_go_back(t
->wv
);
4154 /* the back/forwars list is stupid so help selecting a different item */
4155 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4157 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4158 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4159 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4166 go_forward_for_real(struct tab
*t
)
4169 WebKitWebHistoryItem
*item
;
4177 webkit_web_view_go_forward(t
->wv
);
4180 /* the back/forwars list is stupid so help selecting a different item */
4181 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4183 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4184 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4185 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4192 navaction(struct tab
*t
, struct karg
*args
)
4194 WebKitWebHistoryItem
*item
;
4195 WebKitWebFrame
*frame
;
4197 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4198 t
->tab_id
, args
->i
);
4200 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4202 if (args
->i
== XT_NAV_BACK
)
4203 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4205 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4207 return (XT_CB_PASSTHROUGH
);
4208 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4210 return (XT_CB_PASSTHROUGH
);
4216 go_back_for_real(t
);
4218 case XT_NAV_FORWARD
:
4220 go_forward_for_real(t
);
4223 frame
= webkit_web_view_get_main_frame(t
->wv
);
4224 webkit_web_frame_reload(frame
);
4227 return (XT_CB_PASSTHROUGH
);
4231 move(struct tab
*t
, struct karg
*args
)
4233 GtkAdjustment
*adjust
;
4234 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4240 case XT_MOVE_BOTTOM
:
4242 case XT_MOVE_PAGEDOWN
:
4243 case XT_MOVE_PAGEUP
:
4244 case XT_MOVE_HALFDOWN
:
4245 case XT_MOVE_HALFUP
:
4246 case XT_MOVE_PERCENT
:
4247 adjust
= t
->adjust_v
;
4250 adjust
= t
->adjust_h
;
4254 pos
= gtk_adjustment_get_value(adjust
);
4255 ps
= gtk_adjustment_get_page_size(adjust
);
4256 upper
= gtk_adjustment_get_upper(adjust
);
4257 lower
= gtk_adjustment_get_lower(adjust
);
4258 si
= gtk_adjustment_get_step_increment(adjust
);
4259 pi
= gtk_adjustment_get_page_increment(adjust
);
4262 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4263 "max %f si %f pi %f\n",
4264 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4265 pos
, ps
, upper
, lower
, max
, si
, pi
);
4271 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4276 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4278 case XT_MOVE_BOTTOM
:
4279 case XT_MOVE_FARRIGHT
:
4280 gtk_adjustment_set_value(adjust
, max
);
4283 case XT_MOVE_FARLEFT
:
4284 gtk_adjustment_set_value(adjust
, lower
);
4286 case XT_MOVE_PAGEDOWN
:
4288 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4290 case XT_MOVE_PAGEUP
:
4292 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4294 case XT_MOVE_HALFDOWN
:
4296 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4298 case XT_MOVE_HALFUP
:
4300 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4302 case XT_MOVE_PERCENT
:
4303 percent
= atoi(args
->s
) / 100.0;
4304 pos
= max
* percent
;
4305 if (pos
< 0.0 || pos
> max
)
4307 gtk_adjustment_set_value(adjust
, pos
);
4310 return (XT_CB_PASSTHROUGH
);
4313 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4315 return (XT_CB_HANDLED
);
4319 url_set_visibility(void)
4323 TAILQ_FOREACH(t
, &tabs
, entry
)
4324 if (show_url
== 0) {
4325 gtk_widget_hide(t
->toolbar
);
4328 gtk_widget_show(t
->toolbar
);
4332 notebook_tab_set_visibility(void)
4334 if (show_tabs
== 0) {
4335 gtk_widget_hide(tab_bar
);
4336 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4338 if (tab_style
== XT_TABS_NORMAL
) {
4339 gtk_widget_hide(tab_bar
);
4340 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4341 } else if (tab_style
== XT_TABS_COMPACT
) {
4342 gtk_widget_show(tab_bar
);
4343 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4349 statusbar_set_visibility(void)
4353 TAILQ_FOREACH(t
, &tabs
, entry
)
4354 if (show_statusbar
== 0) {
4355 gtk_widget_hide(t
->statusbar_box
);
4358 gtk_widget_show(t
->statusbar_box
);
4362 url_set(struct tab
*t
, int enable_url_entry
)
4367 show_url
= enable_url_entry
;
4369 if (enable_url_entry
) {
4370 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4371 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4372 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4374 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4375 GTK_ENTRY_ICON_PRIMARY
);
4377 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4378 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4379 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4380 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4386 fullscreen(struct tab
*t
, struct karg
*args
)
4388 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4391 return (XT_CB_PASSTHROUGH
);
4393 if (show_url
== 0) {
4401 url_set_visibility();
4402 notebook_tab_set_visibility();
4404 return (XT_CB_HANDLED
);
4408 statustoggle(struct tab
*t
, struct karg
*args
)
4410 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4412 if (show_statusbar
== 1) {
4414 statusbar_set_visibility();
4415 } else if (show_statusbar
== 0) {
4417 statusbar_set_visibility();
4419 return (XT_CB_HANDLED
);
4423 urlaction(struct tab
*t
, struct karg
*args
)
4425 int rv
= XT_CB_HANDLED
;
4427 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4430 return (XT_CB_PASSTHROUGH
);
4434 if (show_url
== 0) {
4436 url_set_visibility();
4440 if (show_url
== 1) {
4442 url_set_visibility();
4450 tabaction(struct tab
*t
, struct karg
*args
)
4452 int rv
= XT_CB_HANDLED
;
4453 char *url
= args
->s
;
4457 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4460 return (XT_CB_PASSTHROUGH
);
4464 if (strlen(url
) > 0)
4465 create_new_tab(url
, NULL
, 1, args
->precount
);
4467 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4470 if (args
->precount
< 0)
4473 TAILQ_FOREACH(tt
, &tabs
, entry
)
4474 if (tt
->tab_id
== args
->precount
- 1) {
4479 case XT_TAB_DELQUIT
:
4480 if (gtk_notebook_get_n_pages(notebook
) > 1)
4486 if (strlen(url
) > 0)
4489 rv
= XT_CB_PASSTHROUGH
;
4495 if (show_tabs
== 0) {
4497 notebook_tab_set_visibility();
4501 if (show_tabs
== 1) {
4503 notebook_tab_set_visibility();
4506 case XT_TAB_NEXTSTYLE
:
4507 if (tab_style
== XT_TABS_NORMAL
) {
4508 tab_style
= XT_TABS_COMPACT
;
4509 recolor_compact_tabs();
4512 tab_style
= XT_TABS_NORMAL
;
4513 notebook_tab_set_visibility();
4515 case XT_TAB_UNDO_CLOSE
:
4516 if (undo_count
== 0) {
4517 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4522 u
= TAILQ_FIRST(&undos
);
4523 create_new_tab(u
->uri
, u
, 1, -1);
4525 TAILQ_REMOVE(&undos
, u
, entry
);
4527 /* u->history is freed in create_new_tab() */
4532 rv
= XT_CB_PASSTHROUGH
;
4546 resizetab(struct tab
*t
, struct karg
*args
)
4548 if (t
== NULL
|| args
== NULL
) {
4549 show_oops(NULL
, "resizetab invalid parameters");
4550 return (XT_CB_PASSTHROUGH
);
4553 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4554 t
->tab_id
, args
->i
);
4556 setzoom_webkit(t
, args
->i
);
4558 return (XT_CB_HANDLED
);
4562 movetab(struct tab
*t
, struct karg
*args
)
4566 if (t
== NULL
|| args
== NULL
) {
4567 show_oops(NULL
, "movetab invalid parameters");
4568 return (XT_CB_PASSTHROUGH
);
4571 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4572 t
->tab_id
, args
->i
);
4574 if (args
->i
>= XT_TAB_INVALID
)
4575 return (XT_CB_PASSTHROUGH
);
4577 if (TAILQ_EMPTY(&tabs
))
4578 return (XT_CB_PASSTHROUGH
);
4580 n
= gtk_notebook_get_n_pages(notebook
);
4581 dest
= gtk_notebook_get_current_page(notebook
);
4585 if (args
->precount
< 0)
4586 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4588 dest
= args
->precount
- 1;
4592 if (args
->precount
< 0)
4595 dest
-= args
->precount
% n
;
4608 return (XT_CB_PASSTHROUGH
);
4611 if (dest
< 0 || dest
>= n
)
4612 return (XT_CB_PASSTHROUGH
);
4613 if (t
->tab_id
== dest
) {
4614 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4615 return (XT_CB_HANDLED
);
4618 set_current_tab(dest
);
4620 return (XT_CB_HANDLED
);
4626 command(struct tab
*t
, struct karg
*args
)
4628 char *s
= NULL
, *ss
= NULL
;
4632 if (t
== NULL
|| args
== NULL
) {
4633 show_oops(NULL
, "command invalid parameters");
4634 return (XT_CB_PASSTHROUGH
);
4645 if (cmd_prefix
== 0)
4648 ss
= g_strdup_printf(":%d", cmd_prefix
);
4659 case XT_CMD_OPEN_CURRENT
:
4662 case XT_CMD_TABNEW_CURRENT
:
4663 if (!s
) /* FALL THROUGH? */
4665 if ((uri
= get_uri(t
)) != NULL
) {
4666 ss
= g_strdup_printf("%s%s", s
, uri
);
4671 show_oops(t
, "command: invalid opcode %d", args
->i
);
4672 return (XT_CB_PASSTHROUGH
);
4675 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4677 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4678 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4679 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4681 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4682 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4687 return (XT_CB_HANDLED
);
4691 * Return a new string with a download row (in html)
4692 * appended. Old string is freed.
4695 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4698 WebKitDownloadStatus stat
;
4699 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4701 char cur_sz
[FMT_SCALED_STRSIZE
];
4702 char tot_sz
[FMT_SCALED_STRSIZE
];
4705 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4707 /* All actions wil take this form:
4708 * xxxt://class/seskey
4710 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4711 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4713 stat
= webkit_download_get_status(dl
->download
);
4716 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4717 status_html
= g_strdup_printf("Finished");
4718 cmd_html
= g_strdup_printf(
4719 "<a href='%s%d/%d'>Remove</a>",
4720 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4722 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4723 /* gather size info */
4724 progress
= 100 * webkit_download_get_progress(dl
->download
);
4727 webkit_download_get_current_size(dl
->download
), cur_sz
);
4729 webkit_download_get_total_size(dl
->download
), tot_sz
);
4731 status_html
= g_strdup_printf(
4732 "<div style='width: 100%%' align='center'>"
4733 "<div class='progress-outer'>"
4734 "<div class='progress-inner' style='width: %.2f%%'>"
4735 "</div></div></div>"
4736 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4737 progress
, cur_sz
, tot_sz
, progress
);
4739 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4740 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4744 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4745 status_html
= g_strdup_printf("Cancelled");
4746 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4747 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4749 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4750 status_html
= g_strdup_printf("Error!");
4751 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4752 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4754 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4755 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4756 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4757 status_html
= g_strdup_printf("Starting");
4760 show_oops(t
, "%s: unknown download status", __func__
);
4763 new_html
= g_strdup_printf(
4764 "%s\n<tr><td>%s</td><td>%s</td>"
4765 "<td style='text-align:center'>%s</td></tr>\n",
4766 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4767 status_html
, cmd_html
);
4771 g_free(status_html
);
4782 * update all download tabs apart from one. Pass NULL if
4783 * you want to update all.
4786 update_download_tabs(struct tab
*apart_from
)
4789 if (!updating_dl_tabs
) {
4790 updating_dl_tabs
= 1; /* stop infinite recursion */
4791 TAILQ_FOREACH(t
, &tabs
, entry
)
4792 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4793 && (t
!= apart_from
))
4794 xtp_page_dl(t
, NULL
);
4795 updating_dl_tabs
= 0;
4800 * update all cookie tabs apart from one. Pass NULL if
4801 * you want to update all.
4804 update_cookie_tabs(struct tab
*apart_from
)
4807 if (!updating_cl_tabs
) {
4808 updating_cl_tabs
= 1; /* stop infinite recursion */
4809 TAILQ_FOREACH(t
, &tabs
, entry
)
4810 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4811 && (t
!= apart_from
))
4812 xtp_page_cl(t
, NULL
);
4813 updating_cl_tabs
= 0;
4818 * update all history tabs apart from one. Pass NULL if
4819 * you want to update all.
4822 update_history_tabs(struct tab
*apart_from
)
4826 if (!updating_hl_tabs
) {
4827 updating_hl_tabs
= 1; /* stop infinite recursion */
4828 TAILQ_FOREACH(t
, &tabs
, entry
)
4829 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4830 && (t
!= apart_from
))
4831 xtp_page_hl(t
, NULL
);
4832 updating_hl_tabs
= 0;
4836 /* cookie management XTP page */
4838 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4840 char *body
, *page
, *tmp
;
4841 int i
= 1; /* all ids start 1 */
4842 GSList
*sc
, *pc
, *pc_start
;
4844 char *type
, *table_headers
, *last_domain
;
4846 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4849 show_oops(NULL
, "%s invalid parameters", __func__
);
4853 /* Generate a new session key */
4854 if (!updating_cl_tabs
)
4855 generate_xtp_session_key(&cl_session_key
);
4858 table_headers
= g_strdup_printf("<table><tr>"
4861 "<th style='width:200px'>Value</th>"
4865 "<th>HTTP<br />only</th>"
4866 "<th style='width:40px'>Rm</th></tr>\n");
4868 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4869 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4873 last_domain
= strdup("");
4874 for (; sc
; sc
= sc
->next
) {
4877 if (strcmp(last_domain
, c
->domain
) != 0) {
4880 last_domain
= strdup(c
->domain
);
4884 body
= g_strdup_printf("%s</table>"
4886 body
, c
->domain
, table_headers
);
4890 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4891 c
->domain
, table_headers
);
4896 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4897 if (soup_cookie_equal(pc
->data
, c
)) {
4898 type
= "Session + Persistent";
4903 body
= g_strdup_printf(
4906 "<td style='word-wrap:normal'>%s</td>"
4908 " <textarea rows='4'>%s</textarea>"
4914 "<td style='text-align:center'>"
4915 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4922 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4937 soup_cookies_free(sc
);
4938 soup_cookies_free(pc
);
4940 /* small message if there are none */
4942 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4943 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4946 body
= g_strdup_printf("%s</table>", body
);
4949 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4951 g_free(table_headers
);
4952 g_free(last_domain
);
4954 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4955 update_cookie_tabs(t
);
4963 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4965 char *body
, *page
, *tmp
;
4967 int i
= 1; /* all ids start 1 */
4969 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4972 show_oops(NULL
, "%s invalid parameters", __func__
);
4976 /* Generate a new session key */
4977 if (!updating_hl_tabs
)
4978 generate_xtp_session_key(&hl_session_key
);
4981 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4982 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4984 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4986 body
= g_strdup_printf(
4988 "<td><a href='%s'>%s</a></td>"
4990 "<td style='text-align: center'>"
4991 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4992 body
, h
->uri
, h
->uri
, h
->title
,
4993 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4994 XT_XTP_HL_REMOVE
, i
);
5000 /* small message if there are none */
5003 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5004 "colspan='3'>No History</td></tr>\n", body
);
5009 body
= g_strdup_printf("%s</table>", body
);
5012 page
= get_html_page("History", body
, "", TRUE
);
5016 * update all history manager tabs as the xtp session
5017 * key has now changed. No need to update the current tab.
5018 * Already did that above.
5020 update_history_tabs(t
);
5022 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5029 * Generate a web page detailing the status of any downloads
5032 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5034 struct download
*dl
;
5035 char *body
, *page
, *tmp
;
5039 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5042 show_oops(NULL
, "%s invalid parameters", __func__
);
5047 * Generate a new session key for next page instance.
5048 * This only happens for the top level call to xtp_page_dl()
5049 * in which case updating_dl_tabs is 0.
5051 if (!updating_dl_tabs
)
5052 generate_xtp_session_key(&dl_session_key
);
5054 /* header - with refresh so as to update */
5055 if (refresh_interval
>= 1)
5056 ref
= g_strdup_printf(
5057 "<meta http-equiv='refresh' content='%u"
5058 ";url=%s%d/%s/%d' />\n",
5067 body
= g_strdup_printf("<div align='center'>"
5068 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5069 "</p><table><tr><th style='width: 60%%'>"
5070 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5071 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5073 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5074 body
= xtp_page_dl_row(t
, body
, dl
);
5078 /* message if no downloads in list */
5081 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5082 " style='text-align: center'>"
5083 "No downloads</td></tr>\n", body
);
5088 body
= g_strdup_printf("%s</table></div>", body
);
5091 page
= get_html_page("Downloads", body
, ref
, 1);
5096 * update all download manager tabs as the xtp session
5097 * key has now changed. No need to update the current tab.
5098 * Already did that above.
5100 update_download_tabs(t
);
5102 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5109 search(struct tab
*t
, struct karg
*args
)
5113 if (t
== NULL
|| args
== NULL
) {
5114 show_oops(NULL
, "search invalid parameters");
5119 case XT_SEARCH_NEXT
:
5120 d
= t
->search_forward
;
5122 case XT_SEARCH_PREV
:
5123 d
= !t
->search_forward
;
5126 return (XT_CB_PASSTHROUGH
);
5129 if (t
->search_text
== NULL
) {
5130 if (global_search
== NULL
)
5131 return (XT_CB_PASSTHROUGH
);
5133 d
= t
->search_forward
= TRUE
;
5134 t
->search_text
= g_strdup(global_search
);
5135 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5136 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5140 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5141 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5143 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5145 return (XT_CB_HANDLED
);
5148 struct settings_args
{
5154 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5157 struct settings_args
*sa
= cb_args
;
5162 if (s
->flags
& XT_SF_RUNTIME
)
5168 *sa
->body
= g_strdup_printf(
5170 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5171 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5183 set_show(struct tab
*t
, struct karg
*args
)
5185 char *body
, *page
, *tmp
;
5187 struct settings_args sa
;
5189 bzero(&sa
, sizeof sa
);
5193 body
= g_strdup_printf("<div align='center'><table><tr>"
5194 "<th align='left'>Setting</th>"
5195 "<th align='left'>Value</th></tr>\n");
5197 settings_walk(print_setting
, &sa
);
5200 /* small message if there are none */
5203 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5204 "colspan='2'>No settings</td></tr>\n", body
);
5209 body
= g_strdup_printf("%s</table></div>", body
);
5212 page
= get_html_page("Settings", body
, "", 0);
5216 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5220 return (XT_CB_PASSTHROUGH
);
5224 set(struct tab
*t
, struct karg
*args
)
5229 if (args
== NULL
|| args
->s
== NULL
)
5230 return (set_show(t
, args
));
5233 p
= g_strstrip(args
->s
);
5236 return (set_show(t
, args
));
5238 /* we got some sort of string */
5239 val
= g_strrstr(p
, "=");
5242 val
= g_strchomp(val
);
5245 for (i
= 0; i
< LENGTH(rs
); i
++) {
5246 if (strcmp(rs
[i
].name
, p
))
5249 if (rs
[i
].activate
) {
5250 if (rs
[i
].activate(val
))
5251 show_oops(t
, "%s invalid value %s",
5254 show_oops(t
, ":set %s = %s", p
, val
);
5257 show_oops(t
, "not a runtime option: %s", p
);
5261 show_oops(t
, "unknown option: %s", p
);
5265 for (i
= 0; i
< LENGTH(rs
); i
++) {
5266 if (strcmp(rs
[i
].name
, p
))
5269 /* XXX this could use some cleanup */
5270 switch (rs
[i
].type
) {
5273 show_oops(t
, "%s = %d",
5274 rs
[i
].name
, *rs
[i
].ival
);
5275 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5276 show_oops(t
, "%s = %s",
5278 rs
[i
].s
->get(&rs
[i
]));
5279 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5280 show_oops(t
, "%s = ...", rs
[i
].name
);
5282 show_oops(t
, "%s = ", rs
[i
].name
);
5286 show_oops(t
, "%s = %f",
5287 rs
[i
].name
, *rs
[i
].fval
);
5288 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5289 show_oops(t
, "%s = %s",
5291 rs
[i
].s
->get(&rs
[i
]));
5292 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5293 show_oops(t
, "%s = ...", rs
[i
].name
);
5295 show_oops(t
, "%s = ", rs
[i
].name
);
5298 if (rs
[i
].sval
&& *rs
[i
].sval
)
5299 show_oops(t
, "%s = %s",
5300 rs
[i
].name
, *rs
[i
].sval
);
5301 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5302 show_oops(t
, "%s = %s",
5304 rs
[i
].s
->get(&rs
[i
]));
5305 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5306 show_oops(t
, "%s = ...", rs
[i
].name
);
5308 show_oops(t
, "%s = ", rs
[i
].name
);
5311 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5317 show_oops(t
, "unknown option: %s", p
);
5320 return (XT_CB_PASSTHROUGH
);
5324 session_save(struct tab
*t
, char *filename
)
5330 if (strlen(filename
) == 0)
5333 if (filename
[0] == '.' || filename
[0] == '/')
5337 if (save_tabs(t
, &a
))
5339 strlcpy(named_session
, filename
, sizeof named_session
);
5341 /* add the new session to the list of sessions */
5342 s
= g_malloc(sizeof(struct session
));
5343 s
->name
= g_strdup(filename
);
5344 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5352 session_open(struct tab
*t
, char *filename
)
5357 if (strlen(filename
) == 0)
5360 if (filename
[0] == '.' || filename
[0] == '/')
5364 a
.i
= XT_SES_CLOSETABS
;
5365 if (open_tabs(t
, &a
))
5368 strlcpy(named_session
, filename
, sizeof named_session
);
5376 session_delete(struct tab
*t
, char *filename
)
5378 char file
[PATH_MAX
];
5382 if (strlen(filename
) == 0)
5385 if (filename
[0] == '.' || filename
[0] == '/')
5388 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5392 if (!strcmp(filename
, named_session
))
5393 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5394 sizeof named_session
);
5396 /* remove session from sessions list */
5397 TAILQ_FOREACH(s
, &sessions
, entry
) {
5398 if (!strcmp(s
->name
, filename
))
5403 TAILQ_REMOVE(&sessions
, s
, entry
);
5404 g_free((gpointer
) s
->name
);
5413 session_cmd(struct tab
*t
, struct karg
*args
)
5415 char *filename
= args
->s
;
5420 if (args
->i
& XT_SHOW
)
5421 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5422 XT_SAVED_TABS_FILE
: named_session
);
5423 else if (args
->i
& XT_SAVE
) {
5424 if (session_save(t
, filename
)) {
5425 show_oops(t
, "Can't save session: %s",
5426 filename
? filename
: "INVALID");
5429 } else if (args
->i
& XT_OPEN
) {
5430 if (session_open(t
, filename
)) {
5431 show_oops(t
, "Can't open session: %s",
5432 filename
? filename
: "INVALID");
5435 } else if (args
->i
& XT_DELETE
) {
5436 if (session_delete(t
, filename
)) {
5437 show_oops(t
, "Can't delete session: %s",
5438 filename
? filename
: "INVALID");
5443 return (XT_CB_PASSTHROUGH
);
5447 * Make a hardcopy of the page
5450 print_page(struct tab
*t
, struct karg
*args
)
5452 WebKitWebFrame
*frame
;
5454 GtkPrintOperation
*op
;
5455 GtkPrintOperationAction action
;
5456 GtkPrintOperationResult print_res
;
5457 GError
*g_err
= NULL
;
5458 int marg_l
, marg_r
, marg_t
, marg_b
;
5460 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5462 ps
= gtk_page_setup_new();
5463 op
= gtk_print_operation_new();
5464 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5465 frame
= webkit_web_view_get_main_frame(t
->wv
);
5467 /* the default margins are too small, so we will bump them */
5468 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5469 XT_PRINT_EXTRA_MARGIN
;
5470 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5471 XT_PRINT_EXTRA_MARGIN
;
5472 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5473 XT_PRINT_EXTRA_MARGIN
;
5474 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5475 XT_PRINT_EXTRA_MARGIN
;
5478 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5479 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5480 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5481 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5483 gtk_print_operation_set_default_page_setup(op
, ps
);
5485 /* this appears to free 'op' and 'ps' */
5486 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5488 /* check it worked */
5489 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5490 show_oops(NULL
, "can't print: %s", g_err
->message
);
5491 g_error_free (g_err
);
5499 go_home(struct tab
*t
, struct karg
*args
)
5506 set_encoding(struct tab
*t
, struct karg
*args
)
5510 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5511 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5513 e
= webkit_web_view_get_encoding(t
->wv
);
5514 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5516 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5522 restart(struct tab
*t
, struct karg
*args
)
5526 a
.s
= XT_RESTART_TABS_FILE
;
5528 execvp(start_argv
[0], start_argv
);
5534 #define CTRL GDK_CONTROL_MASK
5535 #define MOD1 GDK_MOD1_MASK
5536 #define SHFT GDK_SHIFT_MASK
5538 /* inherent to GTK not all keys will be caught at all times */
5539 /* XXX sort key bindings */
5540 struct key_binding
{
5545 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5547 { "cookiejar", MOD1
, 0, GDK_j
},
5548 { "downloadmgr", MOD1
, 0, GDK_d
},
5549 { "history", MOD1
, 0, GDK_h
},
5550 { "print", CTRL
, 0, GDK_p
},
5551 { "search", 0, 0, GDK_slash
},
5552 { "searchb", 0, 0, GDK_question
},
5553 { "statustoggle", CTRL
, 0, GDK_n
},
5554 { "command", 0, 0, GDK_colon
},
5555 { "qa", CTRL
, 0, GDK_q
},
5556 { "restart", MOD1
, 0, GDK_q
},
5557 { "js toggle", CTRL
, 0, GDK_j
},
5558 { "cookie toggle", MOD1
, 0, GDK_c
},
5559 { "togglesrc", CTRL
, 0, GDK_s
},
5560 { "yankuri", 0, 0, GDK_y
},
5561 { "pasteuricur", 0, 0, GDK_p
},
5562 { "pasteurinew", 0, 0, GDK_P
},
5563 { "toplevel toggle", 0, 0, GDK_F4
},
5564 { "help", 0, 0, GDK_F1
},
5565 { "run_script", MOD1
, 0, GDK_r
},
5568 { "searchnext", 0, 0, GDK_n
},
5569 { "searchprevious", 0, 0, GDK_N
},
5572 { "focusaddress", 0, 0, GDK_F6
},
5573 { "focussearch", 0, 0, GDK_F7
},
5576 { "hinting", 0, 0, GDK_f
},
5578 /* custom stylesheet */
5579 { "userstyle", 0, 0, GDK_i
},
5582 { "goback", 0, 0, GDK_BackSpace
},
5583 { "goback", MOD1
, 0, GDK_Left
},
5584 { "goforward", SHFT
, 0, GDK_BackSpace
},
5585 { "goforward", MOD1
, 0, GDK_Right
},
5586 { "reload", 0, 0, GDK_F5
},
5587 { "reload", CTRL
, 0, GDK_r
},
5588 { "reload", CTRL
, 0, GDK_l
},
5589 { "favorites", MOD1
, 1, GDK_f
},
5591 /* vertical movement */
5592 { "scrolldown", 0, 0, GDK_j
},
5593 { "scrolldown", 0, 0, GDK_Down
},
5594 { "scrollup", 0, 0, GDK_Up
},
5595 { "scrollup", 0, 0, GDK_k
},
5596 { "scrollbottom", 0, 0, GDK_G
},
5597 { "scrollbottom", 0, 0, GDK_End
},
5598 { "scrolltop", 0, 0, GDK_Home
},
5599 { "scrollpagedown", 0, 0, GDK_space
},
5600 { "scrollpagedown", CTRL
, 0, GDK_f
},
5601 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5602 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5603 { "scrollpageup", 0, 0, GDK_Page_Up
},
5604 { "scrollpageup", CTRL
, 0, GDK_b
},
5605 { "scrollhalfup", CTRL
, 0, GDK_u
},
5606 /* horizontal movement */
5607 { "scrollright", 0, 0, GDK_l
},
5608 { "scrollright", 0, 0, GDK_Right
},
5609 { "scrollleft", 0, 0, GDK_Left
},
5610 { "scrollleft", 0, 0, GDK_h
},
5611 { "scrollfarright", 0, 0, GDK_dollar
},
5612 { "scrollfarleft", 0, 0, GDK_0
},
5615 { "tabnew", CTRL
, 0, GDK_t
},
5616 { "999tabnew", CTRL
, 0, GDK_T
},
5617 { "tabclose", CTRL
, 1, GDK_w
},
5618 { "tabundoclose", 0, 0, GDK_U
},
5619 { "tabnext 1", CTRL
, 0, GDK_1
},
5620 { "tabnext 2", CTRL
, 0, GDK_2
},
5621 { "tabnext 3", CTRL
, 0, GDK_3
},
5622 { "tabnext 4", CTRL
, 0, GDK_4
},
5623 { "tabnext 5", CTRL
, 0, GDK_5
},
5624 { "tabnext 6", CTRL
, 0, GDK_6
},
5625 { "tabnext 7", CTRL
, 0, GDK_7
},
5626 { "tabnext 8", CTRL
, 0, GDK_8
},
5627 { "tabnext 9", CTRL
, 0, GDK_9
},
5628 { "tabfirst", CTRL
, 0, GDK_less
},
5629 { "tablast", CTRL
, 0, GDK_greater
},
5630 { "tabprevious", CTRL
, 0, GDK_Left
},
5631 { "tabnext", CTRL
, 0, GDK_Right
},
5632 { "focusout", CTRL
, 0, GDK_minus
},
5633 { "focusin", CTRL
, 0, GDK_plus
},
5634 { "focusin", CTRL
, 0, GDK_equal
},
5635 { "focusreset", CTRL
, 0, GDK_0
},
5637 /* command aliases (handy when -S flag is used) */
5638 { "promptopen", 0, 0, GDK_F9
},
5639 { "promptopencurrent", 0, 0, GDK_F10
},
5640 { "prompttabnew", 0, 0, GDK_F11
},
5641 { "prompttabnewcurrent",0, 0, GDK_F12
},
5643 TAILQ_HEAD(keybinding_list
, key_binding
);
5646 walk_kb(struct settings
*s
,
5647 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5649 struct key_binding
*k
;
5652 if (s
== NULL
|| cb
== NULL
) {
5653 show_oops(NULL
, "walk_kb invalid parameters");
5657 TAILQ_FOREACH(k
, &kbl
, entry
) {
5663 if (gdk_keyval_name(k
->key
) == NULL
)
5666 strlcat(str
, k
->cmd
, sizeof str
);
5667 strlcat(str
, ",", sizeof str
);
5669 if (k
->mask
& GDK_SHIFT_MASK
)
5670 strlcat(str
, "S-", sizeof str
);
5671 if (k
->mask
& GDK_CONTROL_MASK
)
5672 strlcat(str
, "C-", sizeof str
);
5673 if (k
->mask
& GDK_MOD1_MASK
)
5674 strlcat(str
, "M1-", sizeof str
);
5675 if (k
->mask
& GDK_MOD2_MASK
)
5676 strlcat(str
, "M2-", sizeof str
);
5677 if (k
->mask
& GDK_MOD3_MASK
)
5678 strlcat(str
, "M3-", sizeof str
);
5679 if (k
->mask
& GDK_MOD4_MASK
)
5680 strlcat(str
, "M4-", sizeof str
);
5681 if (k
->mask
& GDK_MOD5_MASK
)
5682 strlcat(str
, "M5-", sizeof str
);
5684 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5685 cb(s
, str
, cb_args
);
5690 init_keybindings(void)
5693 struct key_binding
*k
;
5695 for (i
= 0; i
< LENGTH(keys
); i
++) {
5696 k
= g_malloc0(sizeof *k
);
5697 k
->cmd
= keys
[i
].cmd
;
5698 k
->mask
= keys
[i
].mask
;
5699 k
->use_in_entry
= keys
[i
].use_in_entry
;
5700 k
->key
= keys
[i
].key
;
5701 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5703 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5704 k
->cmd
? k
->cmd
: "unnamed key");
5709 keybinding_clearall(void)
5711 struct key_binding
*k
, *next
;
5713 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5714 next
= TAILQ_NEXT(k
, entry
);
5718 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5719 k
->cmd
? k
->cmd
: "unnamed key");
5720 TAILQ_REMOVE(&kbl
, k
, entry
);
5726 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5728 struct key_binding
*k
;
5729 guint keyval
, mask
= 0;
5732 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5734 /* Keys which are to be used in entry have been prefixed with an
5735 * exclamation mark. */
5739 /* find modifier keys */
5740 if (strstr(key
, "S-"))
5741 mask
|= GDK_SHIFT_MASK
;
5742 if (strstr(key
, "C-"))
5743 mask
|= GDK_CONTROL_MASK
;
5744 if (strstr(key
, "M1-"))
5745 mask
|= GDK_MOD1_MASK
;
5746 if (strstr(key
, "M2-"))
5747 mask
|= GDK_MOD2_MASK
;
5748 if (strstr(key
, "M3-"))
5749 mask
|= GDK_MOD3_MASK
;
5750 if (strstr(key
, "M4-"))
5751 mask
|= GDK_MOD4_MASK
;
5752 if (strstr(key
, "M5-"))
5753 mask
|= GDK_MOD5_MASK
;
5756 for (i
= strlen(key
) - 1; i
> 0; i
--)
5760 /* validate keyname */
5761 keyval
= gdk_keyval_from_name(key
);
5762 if (keyval
== GDK_VoidSymbol
) {
5763 warnx("invalid keybinding name %s", key
);
5766 /* must run this test too, gtk+ doesn't handle 10 for example */
5767 if (gdk_keyval_name(keyval
) == NULL
) {
5768 warnx("invalid keybinding name %s", key
);
5772 /* Remove eventual dupes. */
5773 TAILQ_FOREACH(k
, &kbl
, entry
)
5774 if (k
->key
== keyval
&& k
->mask
== mask
) {
5775 TAILQ_REMOVE(&kbl
, k
, entry
);
5781 k
= g_malloc0(sizeof *k
);
5782 k
->cmd
= g_strdup(cmd
);
5784 k
->use_in_entry
= use_in_entry
;
5787 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5792 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5793 k
->cmd
, gdk_keyval_name(keyval
));
5795 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5801 add_kb(struct settings
*s
, char *entry
)
5805 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5807 /* clearall is special */
5808 if (!strcmp(entry
, "clearall")) {
5809 keybinding_clearall();
5813 kb
= strstr(entry
, ",");
5819 return (keybinding_add(entry
, key
, key
[0] == '!'));
5825 int (*func
)(struct tab
*, struct karg
*);
5829 { "command", 0, command
, ':', 0 },
5830 { "search", 0, command
, '/', 0 },
5831 { "searchb", 0, command
, '?', 0 },
5832 { "togglesrc", 0, toggle_src
, 0, 0 },
5834 /* yanking and pasting */
5835 { "yankuri", 0, yank_uri
, 0, 0 },
5836 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5837 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5838 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5841 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5842 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5845 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5846 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5849 { "hinting", 0, hint
, 0, 0 },
5851 /* custom stylesheet */
5852 { "userstyle", 0, userstyle
, 0, 0 },
5855 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5856 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5857 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5859 /* vertical movement */
5860 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5861 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5862 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5863 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5864 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5865 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5866 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5867 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5868 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5869 /* horizontal movement */
5870 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5871 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5872 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5873 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5875 { "favorites", 0, xtp_page_fl
, 0, 0 },
5876 { "fav", 0, xtp_page_fl
, 0, 0 },
5877 { "favadd", 0, add_favorite
, 0, 0 },
5879 { "qall", 0, quit
, 0, 0 },
5880 { "quitall", 0, quit
, 0, 0 },
5881 { "w", 0, save_tabs
, 0, 0 },
5882 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5883 { "help", 0, help
, 0, 0 },
5884 { "about", 0, about
, 0, 0 },
5885 { "stats", 0, stats
, 0, 0 },
5886 { "version", 0, about
, 0, 0 },
5889 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5890 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5891 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5892 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5893 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5894 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5895 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5896 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5897 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5898 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5899 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5901 /* cookie command */
5902 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5903 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5904 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5905 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5906 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5907 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5908 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5909 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5910 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5911 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5912 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5914 /* toplevel (domain) command */
5915 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5916 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5919 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5922 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5923 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5924 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5926 { "ca", 0, ca_cmd
, 0, 0 },
5927 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5928 { "dl", 0, xtp_page_dl
, 0, 0 },
5929 { "h", 0, xtp_page_hl
, 0, 0 },
5930 { "history", 0, xtp_page_hl
, 0, 0 },
5931 { "home", 0, go_home
, 0, 0 },
5932 { "restart", 0, restart
, 0, 0 },
5933 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5934 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5935 { "statustoggle", 0, statustoggle
, 0, 0 },
5936 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5938 { "print", 0, print_page
, 0, 0 },
5941 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5942 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5943 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5944 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5945 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5946 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5947 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5948 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5949 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5950 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5951 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5952 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5953 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5954 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5955 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5956 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5957 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5958 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5959 { "buffers", 0, buffers
, 0, 0 },
5960 { "ls", 0, buffers
, 0, 0 },
5961 { "tabs", 0, buffers
, 0, 0 },
5962 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
5964 /* command aliases (handy when -S flag is used) */
5965 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5966 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5967 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5968 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5971 { "set", 0, set
, 0, XT_SETARG
},
5973 { "fullscreen", 0, fullscreen
, 0, 0 },
5974 { "f", 0, fullscreen
, 0, 0 },
5977 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5978 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
5979 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
5980 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5981 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5988 } cmd_status
= {-1, 0};
5991 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5994 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
6001 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6008 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6010 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
6016 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
6018 a
.i
= XT_NAV_FORWARD
;
6028 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6030 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6032 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6039 * cancel, remove, etc. downloads
6042 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6044 struct download find
, *d
= NULL
;
6046 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6048 /* some commands require a valid download id */
6049 if (cmd
!= XT_XTP_DL_LIST
) {
6050 /* lookup download in question */
6052 d
= RB_FIND(download_list
, &downloads
, &find
);
6055 show_oops(t
, "%s: no such download", __func__
);
6060 /* decide what to do */
6062 case XT_XTP_DL_CANCEL
:
6063 webkit_download_cancel(d
->download
);
6065 case XT_XTP_DL_REMOVE
:
6066 webkit_download_cancel(d
->download
); /* just incase */
6067 g_object_unref(d
->download
);
6068 RB_REMOVE(download_list
, &downloads
, d
);
6070 case XT_XTP_DL_LIST
:
6074 show_oops(t
, "%s: unknown command", __func__
);
6077 xtp_page_dl(t
, NULL
);
6081 * Actions on history, only does one thing for now, but
6082 * we provide the function for future actions
6085 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6087 struct history
*h
, *next
;
6091 case XT_XTP_HL_REMOVE
:
6092 /* walk backwards, as listed in reverse */
6093 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6094 next
= RB_PREV(history_list
, &hl
, h
);
6096 RB_REMOVE(history_list
, &hl
, h
);
6097 g_free((gpointer
) h
->title
);
6098 g_free((gpointer
) h
->uri
);
6105 case XT_XTP_HL_LIST
:
6106 /* Nothing - just xtp_page_hl() below */
6109 show_oops(t
, "%s: unknown command", __func__
);
6113 xtp_page_hl(t
, NULL
);
6116 /* remove a favorite */
6118 remove_favorite(struct tab
*t
, int index
)
6120 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6121 char *new_favs
, *tmp
;
6126 /* open favorites */
6127 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6129 if ((f
= fopen(file
, "r")) == NULL
) {
6130 show_oops(t
, "%s: can't open favorites: %s",
6131 __func__
, strerror(errno
));
6135 /* build a string which will become the new favroites file */
6136 new_favs
= g_strdup("");
6139 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6140 if (feof(f
) || ferror(f
))
6142 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6149 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6150 if (feof(f
) || ferror(f
)) {
6151 show_oops(t
, "%s: can't parse favorites %s",
6152 __func__
, strerror(errno
));
6157 /* as long as this isn't the one we are deleting add to file */
6160 new_favs
= g_strdup_printf("%s%s\n%s\n",
6161 new_favs
, title
, uri
);
6173 /* write back new favorites file */
6174 if ((f
= fopen(file
, "w")) == NULL
) {
6175 show_oops(t
, "%s: can't open favorites: %s",
6176 __func__
, strerror(errno
));
6180 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
6181 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
6194 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6197 case XT_XTP_FL_LIST
:
6198 /* nothing, just the below call to xtp_page_fl() */
6200 case XT_XTP_FL_REMOVE
:
6201 remove_favorite(t
, arg
);
6204 show_oops(t
, "%s: invalid favorites command", __func__
);
6208 xtp_page_fl(t
, NULL
);
6212 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6215 case XT_XTP_CL_LIST
:
6216 /* nothing, just xtp_page_cl() */
6218 case XT_XTP_CL_REMOVE
:
6222 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6226 xtp_page_cl(t
, NULL
);
6229 /* link an XTP class to it's session key and handler function */
6230 struct xtp_despatch
{
6233 void (*handle_func
)(struct tab
*, uint8_t, int);
6236 struct xtp_despatch xtp_despatches
[] = {
6237 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6238 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6239 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6240 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6241 { XT_XTP_INVALID
, NULL
, NULL
}
6245 * is the url xtp protocol? (xxxt://)
6246 * if so, parse and despatch correct bahvior
6249 parse_xtp_url(struct tab
*t
, const char *url
)
6251 char *dup
= NULL
, *p
, *last
= NULL
;
6252 uint8_t n_tokens
= 0;
6253 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6254 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6259 * tokens array meaning:
6261 * tokens[1] = session key
6262 * tokens[2] = action
6263 * tokens[3] = optional argument
6266 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6268 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6271 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6273 /* split out the url */
6274 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6275 (p
= strtok_r(NULL
, "/", &last
))) {
6277 tokens
[n_tokens
++] = p
;
6280 /* should be atleast three fields 'class/seskey/command/arg' */
6284 dsp
= xtp_despatches
;
6285 req_class
= atoi(tokens
[0]);
6286 while (dsp
->xtp_class
) {
6287 if (dsp
->xtp_class
== req_class
) {
6294 /* did we find one atall? */
6295 if (dsp_match
== NULL
) {
6296 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6300 /* check session key and call despatch function */
6301 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6302 ret
= TRUE
; /* all is well, this was a valid xtp request */
6303 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6316 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6318 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6320 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6323 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6328 show_oops(t
, "activate_uri_entry_cb no uri");
6332 uri
+= strspn(uri
, "\t ");
6334 /* if xxxt:// treat specially */
6335 if (parse_xtp_url(t
, uri
))
6338 /* otherwise continue to load page normally */
6339 load_uri(t
, (gchar
*)uri
);
6344 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6346 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6347 char *newuri
= NULL
;
6350 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6353 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6357 if (search_string
== NULL
) {
6358 show_oops(t
, "no search_string");
6362 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6364 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6365 newuri
= g_strdup_printf(search_string
, enc_search
);
6369 webkit_web_view_load_uri(t
->wv
, newuri
);
6377 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6379 struct domain
*d
= NULL
;
6382 if (uri
== NULL
|| t
== NULL
)
6385 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6390 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6391 es
? "enable" : "disable", uri
);
6393 g_object_set(G_OBJECT(t
->settings
),
6394 "enable-html5-local-storage", es
, (char *)NULL
);
6395 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6399 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6401 struct domain
*d
= NULL
;
6404 if (uri
== NULL
|| t
== NULL
)
6407 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6412 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6413 es
? "enable" : "disable", uri
);
6415 g_object_set(G_OBJECT(t
->settings
),
6416 "enable-scripts", es
, (char *)NULL
);
6417 g_object_set(G_OBJECT(t
->settings
),
6418 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6419 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6421 button_set_stockid(t
->js_toggle
,
6422 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6426 color_address_bar(gpointer p
)
6429 struct tab
*tt
, *t
= p
;
6430 gchar
*col_str
= XT_COLOR_WHITE
;
6431 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
6434 gdk_threads_enter();
6436 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6438 /* make sure t still exists */
6441 TAILQ_FOREACH(tt
, &tabs
, entry
)
6447 if ((uri
= get_uri(t
)) == NULL
)
6452 gdk_threads_leave();
6455 col_str
= XT_COLOR_YELLOW
;
6456 switch (load_compare_cert(u
, &error_str
)) {
6458 col_str
= XT_COLOR_BLUE
;
6461 col_str
= XT_COLOR_GREEN
;
6463 case CERT_UNTRUSTED
:
6464 col_str
= XT_COLOR_YELLOW
;
6467 col_str
= XT_COLOR_RED
;
6472 gdk_threads_enter();
6474 /* make sure t isn't deleted */
6475 TAILQ_FOREACH(tt
, &tabs
, entry
)
6482 /* test to see if the user navigated away and canceled the thread */
6483 if (t
->thread
!= g_thread_self())
6487 gdk_color_parse(col_str
, &color
);
6488 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6490 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6491 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6493 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6495 if (error_str
&& error_str
[0] != '\0')
6496 show_oops(t
, "%s", error_str
);
6501 /* t is invalid at this point */
6503 g_free((gpointer
)u
);
6505 gdk_threads_leave();
6510 show_ca_status(struct tab
*t
, const char *uri
)
6513 gchar
*col_str
= XT_COLOR_WHITE
;
6515 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6516 ssl_strict_certs
, ssl_ca_file
, uri
);
6523 if (ssl_ca_file
== NULL
) {
6524 if (g_str_has_prefix(uri
, "http://"))
6526 if (g_str_has_prefix(uri
, "https://")) {
6527 col_str
= XT_COLOR_RED
;
6532 if (g_str_has_prefix(uri
, "http://") ||
6533 !g_str_has_prefix(uri
, "https://"))
6537 * It is not necessary to see if the thread is already running.
6538 * If the thread is in progress setting it to something else aborts it
6542 /* thread the coloring of the address bar */
6543 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
6545 color_address_bar(t
);
6551 gdk_color_parse(col_str
, &color
);
6552 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6554 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6555 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6557 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6562 free_favicon(struct tab
*t
)
6564 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6565 __func__
, t
->icon_download
, t
->icon_request
);
6567 if (t
->icon_request
)
6568 g_object_unref(t
->icon_request
);
6569 if (t
->icon_dest_uri
)
6570 g_free(t
->icon_dest_uri
);
6572 t
->icon_request
= NULL
;
6573 t
->icon_dest_uri
= NULL
;
6577 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6579 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6580 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6582 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6583 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6585 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6586 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6590 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6592 GdkPixbuf
*pb_scaled
;
6594 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6595 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6596 GDK_INTERP_BILINEAR
);
6600 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6601 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6603 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6604 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6606 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6607 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6609 if (pb_scaled
!= pb
)
6610 g_object_unref(pb_scaled
);
6614 xt_icon_from_file(struct tab
*t
, char *file
)
6618 if (g_str_has_prefix(file
, "file://"))
6619 file
+= strlen("file://");
6621 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6623 xt_icon_from_pixbuf(t
, pb
);
6626 xt_icon_from_name(t
, "text-html");
6630 is_valid_icon(char *file
)
6633 const char *mime_type
;
6637 gf
= g_file_new_for_path(file
);
6638 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6640 mime_type
= g_file_info_get_content_type(fi
);
6641 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6642 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6643 g_strcmp0(mime_type
, "image/png") == 0 ||
6644 g_strcmp0(mime_type
, "image/gif") == 0 ||
6645 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6653 set_favicon_from_file(struct tab
*t
, char *file
)
6657 if (t
== NULL
|| file
== NULL
)
6660 if (g_str_has_prefix(file
, "file://"))
6661 file
+= strlen("file://");
6662 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6664 if (!stat(file
, &sb
)) {
6665 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6666 /* corrupt icon so trash it */
6667 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6670 /* no need to set icon to default here */
6674 xt_icon_from_file(t
, file
);
6678 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6681 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6682 struct tab
*tt
= NULL
, *t
= NULL
;
6685 * find the webview instead of passing in the tab as it could have been
6686 * deleted from underneath us.
6688 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6697 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6698 __func__
, t
->tab_id
, status
);
6701 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6703 t
->icon_download
= NULL
;
6706 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6709 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6712 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6714 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6715 __func__
, t
->tab_id
);
6716 t
->icon_download
= NULL
;
6719 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6722 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6723 __func__
, t
->icon_dest_uri
);
6724 set_favicon_from_file(t
, t
->icon_dest_uri
);
6725 /* these will be freed post callback */
6726 t
->icon_request
= NULL
;
6727 t
->icon_download
= NULL
;
6735 abort_favicon_download(struct tab
*t
)
6737 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6739 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6740 if (t
->icon_download
) {
6741 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6742 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6743 webkit_download_cancel(t
->icon_download
);
6744 t
->icon_download
= NULL
;
6749 xt_icon_from_name(t
, "text-html");
6753 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6755 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6757 if (uri
== NULL
|| t
== NULL
)
6760 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6761 /* take icon from WebKitIconDatabase */
6764 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6766 xt_icon_from_pixbuf(t
, pb
);
6769 xt_icon_from_name(t
, "text-html");
6770 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6771 /* download icon to cache dir */
6772 gchar
*name_hash
, file
[PATH_MAX
];
6775 if (t
->icon_request
) {
6776 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6780 /* check to see if we got the icon in cache */
6781 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6782 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6785 if (!stat(file
, &sb
)) {
6786 if (sb
.st_size
> 0) {
6787 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6789 set_favicon_from_file(t
, file
);
6793 /* corrupt icon so trash it */
6794 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6799 /* create download for icon */
6800 t
->icon_request
= webkit_network_request_new(uri
);
6801 if (t
->icon_request
== NULL
) {
6802 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6807 t
->icon_download
= webkit_download_new(t
->icon_request
);
6808 if (t
->icon_download
== NULL
)
6811 /* we have to free icon_dest_uri later */
6812 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6813 webkit_download_set_destination_uri(t
->icon_download
,
6816 if (webkit_download_get_status(t
->icon_download
) ==
6817 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6818 g_object_unref(t
->icon_request
);
6819 g_free(t
->icon_dest_uri
);
6820 t
->icon_request
= NULL
;
6821 t
->icon_dest_uri
= NULL
;
6825 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6826 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6828 webkit_download_start(t
->icon_download
);
6833 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6835 const gchar
*uri
= NULL
, *title
= NULL
;
6836 struct history
*h
, find
;
6840 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6841 webkit_web_view_get_load_status(wview
),
6842 get_uri(t
) ? get_uri(t
) : "NOTHING");
6845 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6849 switch (webkit_web_view_get_load_status(wview
)) {
6850 case WEBKIT_LOAD_PROVISIONAL
:
6852 abort_favicon_download(t
);
6853 #if GTK_CHECK_VERSION(2, 20, 0)
6854 gtk_widget_show(t
->spinner
);
6855 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6857 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6859 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6861 /* assume we are a new address */
6862 gdk_color_parse("white", &color
);
6863 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6864 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6866 /* take focus if we are visible */
6870 /* kill color thread */
6875 case WEBKIT_LOAD_COMMITTED
:
6880 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6886 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6888 /* check if js white listing is enabled */
6889 if (enable_cookie_whitelist
)
6890 check_and_set_cookie(uri
, t
);
6891 if (enable_js_whitelist
)
6892 check_and_set_js(uri
, t
);
6898 /* we know enough to autosave the session */
6899 if (session_autosave
) {
6904 show_ca_status(t
, uri
);
6907 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6911 case WEBKIT_LOAD_FINISHED
:
6917 if (!strncmp(uri
, "http://", strlen("http://")) ||
6918 !strncmp(uri
, "https://", strlen("https://")) ||
6919 !strncmp(uri
, "file://", strlen("file://"))) {
6921 h
= RB_FIND(history_list
, &hl
, &find
);
6923 title
= get_title(t
, FALSE
);
6924 h
= g_malloc(sizeof *h
);
6925 h
->uri
= g_strdup(uri
);
6926 h
->title
= g_strdup(title
);
6927 RB_INSERT(history_list
, &hl
, h
);
6928 completion_add_uri(h
->uri
);
6929 update_history_tabs(NULL
);
6933 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6934 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6935 case WEBKIT_LOAD_FAILED
:
6938 #if GTK_CHECK_VERSION(2, 20, 0)
6939 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6940 gtk_widget_hide(t
->spinner
);
6943 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6948 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6950 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6951 can_go_back_for_real(t
));
6953 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6954 can_go_forward_for_real(t
));
6958 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6960 const gchar
*title
= NULL
, *win_title
= NULL
;
6962 title
= get_title(t
, FALSE
);
6963 win_title
= get_title(t
, TRUE
);
6964 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6965 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6966 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6967 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6971 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6973 run_script(t
, JS_HINTING
);
6977 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6979 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6980 progress
== 100 ? 0 : (double)progress
/ 100);
6981 if (show_url
== 0) {
6982 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6983 progress
== 100 ? 0 : (double)progress
/ 100);
6986 update_statusbar_position(NULL
, NULL
);
6990 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6991 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6992 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6995 WebKitWebNavigationReason reason
;
6996 struct domain
*d
= NULL
;
6999 show_oops(NULL
, "webview_npd_cb invalid parameters");
7003 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
7005 webkit_network_request_get_uri(request
));
7007 uri
= (char *)webkit_network_request_get_uri(request
);
7009 /* if this is an xtp url, we don't load anything else */
7010 if (parse_xtp_url(t
, uri
))
7013 if (t
->ctrl_click
) {
7015 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
7016 webkit_web_policy_decision_ignore(pd
);
7017 return (TRUE
); /* we made the decission */
7021 * This is a little hairy but it comes down to this:
7022 * when we run in whitelist mode we have to assist the browser in
7023 * opening the URL that it would have opened in a new tab.
7025 reason
= webkit_web_navigation_action_get_reason(na
);
7026 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
7027 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7028 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
7029 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7031 webkit_web_policy_decision_use(pd
);
7032 return (TRUE
); /* we made the decision */
7039 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7042 struct domain
*d
= NULL
;
7044 WebKitWebView
*webview
= NULL
;
7046 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
7047 webkit_web_view_get_uri(wv
));
7050 /* open in current tab */
7052 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7053 uri
= webkit_web_view_get_uri(wv
);
7054 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7057 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7059 } else if (enable_scripts
== 1) {
7060 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7068 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
7071 struct domain
*d
= NULL
;
7073 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7075 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7076 uri
= webkit_web_view_get_uri(wv
);
7077 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7081 } else if (enable_scripts
== 1)
7088 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7090 /* we can not eat the event without throwing gtk off so defer it */
7092 /* catch middle click */
7093 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7098 /* catch ctrl click */
7099 if (e
->type
== GDK_BUTTON_RELEASE
&&
7100 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7105 return (XT_CB_PASSTHROUGH
);
7109 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7111 struct mime_type
*m
;
7113 m
= find_mime_type(mime_type
);
7121 show_oops(t
, "can't fork mime handler");
7131 execlp(m
->mt_action
, m
->mt_action
,
7132 webkit_network_request_get_uri(request
), (void *)NULL
);
7141 get_mime_type(const char *file
)
7144 char *mime_type
= NULL
;
7148 if (g_str_has_prefix(file
, "file://"))
7149 file
+= strlen("file://");
7151 gf
= g_file_new_for_path(file
);
7152 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7154 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
7155 mime_type
= g_strdup(m
);
7163 run_download_mimehandler(char *mime_type
, char *file
)
7165 struct mime_type
*m
;
7167 m
= find_mime_type(mime_type
);
7173 show_oops(NULL
, "can't fork download mime handler");
7183 if (g_str_has_prefix(file
, "file://"))
7184 file
+= strlen("file://");
7185 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7194 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7197 WebKitDownloadStatus status
;
7198 const char *file
= NULL
;
7201 if (download
== NULL
)
7203 status
= webkit_download_get_status(download
);
7204 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7207 file
= webkit_download_get_destination_uri(download
);
7210 mime
= get_mime_type(file
);
7214 run_download_mimehandler((char *)mime
, (char *)file
);
7219 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7220 WebKitNetworkRequest
*request
, char *mime_type
,
7221 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7224 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7228 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7229 t
->tab_id
, mime_type
);
7231 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7232 webkit_web_policy_decision_ignore(decision
);
7237 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7238 webkit_web_policy_decision_download(decision
);
7246 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7250 const gchar
*suggested_name
;
7251 gchar
*filename
= NULL
;
7253 struct download
*download_entry
;
7256 if (wk_download
== NULL
|| t
== NULL
) {
7257 show_oops(NULL
, "%s invalid parameters", __func__
);
7261 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7262 if (suggested_name
== NULL
)
7263 return (FALSE
); /* abort download */
7274 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7276 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7277 filename
: suggested_name
);
7279 } while (!stat(uri
+ strlen("file://"), &sb
));
7281 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7282 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7284 webkit_download_set_destination_uri(wk_download
, uri
);
7286 if (webkit_download_get_status(wk_download
) ==
7287 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7288 show_oops(t
, "%s: download failed to start", __func__
);
7290 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7292 /* connect "download first" mime handler */
7293 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7294 G_CALLBACK(download_status_changed_cb
), NULL
);
7296 download_entry
= g_malloc(sizeof(struct download
));
7297 download_entry
->download
= wk_download
;
7298 download_entry
->tab
= t
;
7299 download_entry
->id
= next_download_id
++;
7300 RB_INSERT(download_list
, &downloads
, download_entry
);
7301 /* get from history */
7302 g_object_ref(wk_download
);
7303 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7304 show_oops(t
, "Download of '%s' started...",
7305 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7314 /* sync other download manager tabs */
7315 update_download_tabs(NULL
);
7318 * NOTE: never redirect/render the current tab before this
7319 * function returns. This will cause the download to never start.
7321 return (ret
); /* start download */
7325 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7327 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7330 show_oops(NULL
, "webview_hover_cb");
7335 set_status(t
, uri
, XT_STATUS_LINK
);
7338 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7343 mark(struct tab
*t
, struct karg
*arg
)
7349 if ((index
= marktoindex(mark
)) == -1)
7352 if (arg
->i
== XT_MARK_SET
)
7353 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7354 else if (arg
->i
== XT_MARK_GOTO
) {
7355 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7356 show_oops(t
, "mark '%c' does not exist", mark
);
7359 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7360 something changes the document size */
7361 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7368 marks_clear(struct tab
*t
)
7372 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7373 t
->mark
[i
] = XT_INVALID_MARK
;
7379 char file
[PATH_MAX
];
7380 char *line
= NULL
, *p
;
7385 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7386 if ((f
= fopen(file
, "r+")) == NULL
) {
7387 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7391 for (i
= 1; ; i
++) {
7392 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7394 if (strlen(line
) == 0 || line
[0] == '#') {
7400 p
= strtok(line
, " \t");
7402 if (p
== NULL
|| strlen(p
) != 1 ||
7403 (index
= marktoindex(*p
)) == -1) {
7404 warnx("corrupt quickmarks file, line %d", i
);
7408 p
= strtok(NULL
, " \t");
7409 if (qmarks
[index
] != NULL
)
7410 g_free(qmarks
[index
]);
7411 qmarks
[index
] = g_strdup(p
);
7422 char file
[PATH_MAX
];
7426 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7427 if ((f
= fopen(file
, "r+")) == NULL
) {
7428 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7432 for (i
= 0; i
< XT_NOMARKS
; i
++)
7433 if (qmarks
[i
] != NULL
)
7434 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7442 qmark(struct tab
*t
, struct karg
*arg
)
7447 mark
= arg
->s
[strlen(arg
->s
)-1];
7448 index
= marktoindex(mark
);
7454 if (qmarks
[index
] != NULL
)
7455 g_free(qmarks
[index
]);
7457 qmarks_load(); /* sync if multiple instances */
7458 qmarks
[index
] = g_strdup(get_uri(t
));
7462 if (qmarks
[index
] != NULL
)
7463 load_uri(t
, qmarks
[index
]);
7465 show_oops(t
, "quickmark \"%c\" does not exist",
7471 if (qmarks
[index
] != NULL
)
7472 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7474 show_oops(t
, "quickmark \"%c\" does not exist",
7485 go_up(struct tab
*t
, struct karg
*args
)
7491 levels
= atoi(args
->s
);
7495 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7496 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7498 tmp
+= strlen(XT_PROTO_DELIM
);
7500 /* if an uri starts with a slash, leave it alone (for file:///) */
7507 p
= strrchr(tmp
, '/');
7521 gototab(struct tab
*t
, struct karg
*args
)
7524 struct karg arg
= {0, NULL
, -1};
7526 tab
= atoi(args
->s
);
7528 arg
.i
= XT_TAB_NEXT
;
7537 zoom_amount(struct tab
*t
, struct karg
*arg
)
7539 struct karg narg
= {0, NULL
, -1};
7541 narg
.i
= atoi(arg
->s
);
7542 resizetab(t
, &narg
);
7548 flip_colon(struct tab
*t
, struct karg
*arg
)
7550 struct karg narg
= {0, NULL
, -1};
7553 if (t
== NULL
|| arg
== NULL
)
7556 p
= strstr(arg
->s
, ":");
7568 /* buffer commands receive the regex that triggered them in arg.s */
7569 char bcmd
[XT_BUFCMD_SZ
];
7573 #define XT_PRE_NO (0)
7574 #define XT_PRE_YES (1)
7575 #define XT_PRE_MAYBE (2)
7577 int (*func
)(struct tab
*, struct karg
*);
7581 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7582 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7583 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7584 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7585 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7586 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7587 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7588 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7589 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7590 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7591 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7592 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7593 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7594 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7595 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7596 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7597 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7598 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7602 buffercmd_init(void)
7606 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7607 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7608 REG_EXTENDED
| REG_NOSUB
))
7609 startpage_add("invalid buffercmd regex %s",
7610 buffercmds
[i
].regex
);
7614 buffercmd_abort(struct tab
*t
)
7618 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7619 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7622 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7623 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7627 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7629 struct karg arg
= {0, NULL
, -1};
7632 arg
.s
= g_strdup(bcmd
);
7634 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7635 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7645 buffercmd_addkey(struct tab
*t
, guint keyval
)
7648 char s
[XT_BUFCMD_SZ
];
7650 if (keyval
== GDK_Escape
) {
7652 return (XT_CB_HANDLED
);
7655 /* key with modifier or non-ascii character */
7656 if (!isascii(keyval
))
7657 return (XT_CB_PASSTHROUGH
);
7659 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7660 "to buffer \"%s\"\n", keyval
, bcmd
);
7662 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7663 if (bcmd
[i
] == '\0') {
7668 /* buffer full, ignore input */
7669 if (i
>= LENGTH(bcmd
) -1) {
7670 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7672 return (XT_CB_HANDLED
);
7675 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7677 /* find exact match */
7678 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7679 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7680 (size_t) 0, NULL
, 0) == 0) {
7681 buffercmd_execute(t
, &buffercmds
[i
]);
7685 /* find non exact matches to see if we need to abort ot not */
7686 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7687 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7690 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7691 if (isdigit(bcmd
[0])) {
7692 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7696 if (sscanf(bcmd
, "%s", s
) == 0)
7699 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7700 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7703 if (sscanf(bcmd
, "%s", s
) == 0)
7706 if (c
== -1 && buffercmds
[i
].precount
)
7708 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7711 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7712 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7715 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7720 return (XT_CB_HANDLED
);
7724 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7726 struct key_binding
*k
;
7728 /* handle keybindings if buffercmd is empty.
7729 if not empty, allow commands like C-n */
7730 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7731 TAILQ_FOREACH(k
, &kbl
, entry
)
7732 if (e
->keyval
== k
->key
7733 && (entry
? k
->use_in_entry
: 1)) {
7735 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7736 return (cmd_execute(t
, k
->cmd
));
7737 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7738 return (cmd_execute(t
, k
->cmd
));
7742 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7743 return buffercmd_addkey(t
, e
->keyval
);
7745 return (XT_CB_PASSTHROUGH
);
7749 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7751 char s
[2], buf
[128];
7752 const char *errstr
= NULL
;
7754 /* don't use w directly; use t->whatever instead */
7757 show_oops(NULL
, "wv_keypress_after_cb");
7758 return (XT_CB_PASSTHROUGH
);
7761 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7762 e
->keyval
, e
->state
, t
);
7766 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7768 return (XT_CB_HANDLED
);
7772 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7774 /* we have a string */
7776 /* we have a number */
7777 snprintf(buf
, sizeof buf
,
7778 "vimprobable_fire(%s)", t
->hint_num
);
7785 /* XXX unfuck this */
7786 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7787 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7788 /* last input was numerical */
7790 l
= strlen(t
->hint_num
);
7797 t
->hint_num
[l
] = '\0';
7801 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7802 /* last input was alphanumerical */
7804 l
= strlen(t
->hint_buf
);
7811 t
->hint_buf
[l
] = '\0';
7821 /* numerical input */
7822 if (CLEAN(e
->state
) == 0 &&
7823 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7824 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7825 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7826 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7827 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7831 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7832 "invalid link number\n");
7835 snprintf(buf
, sizeof buf
,
7836 "vimprobable_update_hints(%s)",
7838 t
->hint_mode
= XT_HINT_NUMERICAL
;
7842 /* empty the counter buffer */
7843 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7844 return (XT_CB_HANDLED
);
7847 /* alphanumerical input */
7848 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7849 e
->keyval
<= GDK_z
) ||
7850 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7851 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7852 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7853 e
->keyval
<= GDK_9
) ||
7854 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7855 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7856 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7857 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7858 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7859 " %s\n", t
->hint_buf
);
7861 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7864 snprintf(buf
, sizeof buf
,
7865 "vimprobable_show_hints('%s')", t
->hint_buf
);
7866 t
->hint_mode
= XT_HINT_ALPHANUM
;
7869 /* empty the counter buffer */
7870 bzero(t
->hint_num
, sizeof t
->hint_num
);
7871 return (XT_CB_HANDLED
);
7874 return (XT_CB_HANDLED
);
7877 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7878 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7879 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7882 return (handle_keypress(t
, e
, 0));
7886 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7890 /* Hide buffers, if they are visible, with escape. */
7891 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7892 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7893 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7895 return (XT_CB_HANDLED
);
7898 return (XT_CB_PASSTHROUGH
);
7902 search_continue(struct tab
*t
)
7904 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7905 gboolean rv
= FALSE
;
7909 if (strlen(c
) == 1) {
7910 webkit_web_view_unmark_text_matches(t
->wv
);
7915 t
->search_forward
= TRUE
;
7916 else if (c
[0] == '?')
7917 t
->search_forward
= FALSE
;
7927 search_cb(struct tab
*t
)
7929 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7932 if (search_continue(t
) == FALSE
)
7936 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7938 /* not found, mark red */
7939 gdk_color_parse(XT_COLOR_RED
, &color
);
7940 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7941 /* unmark and remove selection */
7942 webkit_web_view_unmark_text_matches(t
->wv
);
7943 /* my kingdom for a way to unselect text in webview */
7945 /* found, highlight all */
7946 webkit_web_view_unmark_text_matches(t
->wv
);
7947 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7948 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7949 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7950 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7958 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7960 const gchar
*c
= gtk_entry_get_text(w
);
7963 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7964 return (XT_CB_PASSTHROUGH
);
7967 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7968 e
->keyval
, e
->state
, t
);
7970 if (search_continue(t
) == FALSE
)
7973 /* if search length is > 4 then no longer play timeout games */
7974 if (strlen(c
) > 4) {
7976 g_source_remove(t
->search_id
);
7983 /* reestablish a new timer if the user types fast */
7985 g_source_remove(t
->search_id
);
7986 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7989 return (XT_CB_PASSTHROUGH
);
7993 match_uri(const gchar
*uri
, const gchar
*key
) {
7996 gboolean match
= FALSE
;
8000 if (!strncmp(key
, uri
, len
))
8003 voffset
= strstr(uri
, "/") + 2;
8004 if (!strncmp(key
, voffset
, len
))
8006 else if (g_str_has_prefix(voffset
, "www.")) {
8007 voffset
= voffset
+ strlen("www.");
8008 if (!strncmp(key
, voffset
, len
))
8017 match_session(const gchar
*name
, const gchar
*key
) {
8020 sub
= strcasestr(name
, key
);
8026 cmd_getlist(int id
, char *key
)
8033 if (cmds
[id
].type
& XT_URLARG
) {
8034 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
8035 if (match_uri(h
->uri
, key
)) {
8036 cmd_status
.list
[c
] = (char *)h
->uri
;
8042 } else if (cmds
[id
].type
& XT_SESSARG
) {
8043 TAILQ_FOREACH(s
, &sessions
, entry
)
8044 if (match_session(s
->name
, key
)) {
8045 cmd_status
.list
[c
] = (char *)s
->name
;
8051 } else if (cmds
[id
].type
& XT_SETARG
) {
8052 for (i
= 0; i
< LENGTH(rs
); i
++)
8053 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
8054 cmd_status
.list
[c
++] = rs
[i
].name
;
8060 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
8062 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
8063 if (cmds
[i
].level
< dep
)
8065 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
8066 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
8067 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
8075 cmd_getnext(int dir
)
8077 cmd_status
.index
+= dir
;
8079 if (cmd_status
.index
< 0)
8080 cmd_status
.index
= cmd_status
.len
- 1;
8081 else if (cmd_status
.index
>= cmd_status
.len
)
8082 cmd_status
.index
= 0;
8084 return cmd_status
.list
[cmd_status
.index
];
8088 cmd_tokenize(char *s
, char *tokens
[])
8091 char *tok
, *last
= NULL
;
8092 size_t len
= strlen(s
);
8095 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8096 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8097 tok
= strtok_r(NULL
, " ", &last
), i
++)
8107 cmd_complete(struct tab
*t
, char *str
, int dir
)
8109 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8110 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8112 char *tok
, *match
, *s
= g_strdup(str
);
8114 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8117 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8120 for (i
= 0; isdigit(s
[i
]); i
++)
8123 for (; isspace(s
[i
]); i
++)
8128 levels
= cmd_tokenize(s
, tokens
);
8130 for (i
= 0; i
< levels
- 1; i
++) {
8133 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8134 if (cmds
[j
].level
< dep
)
8136 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8140 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8147 if (matchcount
== 1) {
8148 strlcat(res
, tok
, sizeof res
);
8149 strlcat(res
, " ", sizeof res
);
8159 if (cmd_status
.index
== -1)
8160 cmd_getlist(parent
, tokens
[i
]);
8162 if (cmd_status
.len
> 0) {
8163 match
= cmd_getnext(dir
);
8164 strlcat(res
, match
, sizeof res
);
8165 gtk_entry_set_text(w
, res
);
8166 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8173 cmd_execute(struct tab
*t
, char *str
)
8175 struct cmd
*cmd
= NULL
;
8176 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
8178 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8179 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8180 struct karg arg
= {0, NULL
, -1};
8185 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8191 while (isspace(s
[0]))
8194 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8195 prefix
= atoi(prefixstr
);
8199 for (tok
= strtok_r(s
, " ", &last
); tok
;
8200 tok
= strtok_r(NULL
, " ", &last
)) {
8202 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8203 if (cmds
[j
].level
< dep
)
8205 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8207 if (cmds
[j
].level
== dep
&&
8208 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8212 if (len
== strlen(cmds
[j
].cmd
)) {
8218 if (matchcount
== 1) {
8223 show_oops(t
, "Invalid command: %s", str
);
8229 show_oops(t
, "Empty command");
8235 arg
.precount
= prefix
;
8236 else if (cmd_prefix
> 0)
8237 arg
.precount
= cmd_prefix
;
8239 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8240 show_oops(t
, "No prefix allowed: %s", str
);
8244 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8245 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8246 if (arg
.s
== NULL
) {
8247 show_oops(t
, "Invalid command");
8250 arg
.precount
= atoi(arg
.s
);
8251 if (arg
.precount
<= 0) {
8252 if (arg
.s
[0] == '0')
8253 show_oops(t
, "Zero count");
8255 show_oops(t
, "Trailing characters");
8260 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8261 __func__
, arg
.precount
, arg
.s
);
8277 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8280 show_oops(NULL
, "entry_key_cb invalid parameters");
8281 return (XT_CB_PASSTHROUGH
);
8284 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8285 e
->keyval
, e
->state
, t
);
8289 if (e
->keyval
== GDK_Escape
) {
8290 /* don't use focus_webview(t) because we want to type :cmds */
8291 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8294 return (handle_keypress(t
, e
, 1));
8297 struct command_entry
*
8298 history_prev(struct command_list
*l
, struct command_entry
*at
)
8301 at
= TAILQ_LAST(l
, command_list
);
8303 at
= TAILQ_PREV(at
, command_list
, entry
);
8305 at
= TAILQ_LAST(l
, command_list
);
8311 struct command_entry
*
8312 history_next(struct command_list
*l
, struct command_entry
*at
)
8315 at
= TAILQ_FIRST(l
);
8317 at
= TAILQ_NEXT(at
, entry
);
8319 at
= TAILQ_FIRST(l
);
8326 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8328 int rv
= XT_CB_HANDLED
;
8329 const gchar
*c
= gtk_entry_get_text(w
);
8332 show_oops(NULL
, "cmd_keypress_cb parameters");
8333 return (XT_CB_PASSTHROUGH
);
8336 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8337 e
->keyval
, e
->state
, t
);
8341 e
->keyval
= GDK_Escape
;
8342 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8343 e
->keyval
= GDK_Escape
;
8345 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8346 e
->keyval
!= GDK_ISO_Left_Tab
)
8347 cmd_status
.index
= -1;
8349 switch (e
->keyval
) {
8352 cmd_complete(t
, (char *)&c
[1], 1);
8354 case GDK_ISO_Left_Tab
:
8356 cmd_complete(t
, (char *)&c
[1], -1);
8361 if ((search_at
= history_next(&shl
, search_at
))) {
8362 search_at
->line
[0] = c
[0];
8363 gtk_entry_set_text(w
, search_at
->line
);
8364 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8367 if ((history_at
= history_prev(&chl
, history_at
))) {
8368 history_at
->line
[0] = c
[0];
8369 gtk_entry_set_text(w
, history_at
->line
);
8370 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8377 if ((search_at
= history_next(&shl
, search_at
))) {
8378 search_at
->line
[0] = c
[0];
8379 gtk_entry_set_text(w
, search_at
->line
);
8380 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8383 if ((history_at
= history_next(&chl
, history_at
))) {
8384 history_at
->line
[0] = c
[0];
8385 gtk_entry_set_text(w
, history_at
->line
);
8386 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8392 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8400 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8401 webkit_web_view_unmark_text_matches(t
->wv
);
8405 rv
= XT_CB_PASSTHROUGH
;
8411 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8413 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8417 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8419 /* popup menu enabled */
8424 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8427 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8428 return (XT_CB_PASSTHROUGH
);
8431 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8432 t
->tab_id
, t
->popup
);
8434 /* if popup is enabled don't lose focus */
8437 return (XT_CB_PASSTHROUGH
);
8443 if (show_url
== 0 || t
->focus_wv
)
8446 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8448 return (XT_CB_PASSTHROUGH
);
8452 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8455 const gchar
*c
= gtk_entry_get_text(entry
);
8458 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8462 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8469 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8475 if (c
[0] == '/' || c
[0] == '?') {
8476 /* see if there is a timer pending */
8478 g_source_remove(t
->search_id
);
8483 if (t
->search_text
) {
8484 g_free(t
->search_text
);
8485 t
->search_text
= NULL
;
8488 t
->search_text
= g_strdup(s
);
8490 g_free(global_search
);
8491 global_search
= g_strdup(s
);
8492 t
->search_forward
= c
[0] == '/';
8494 history_add(&shl
, search_file
, s
, &search_history_count
);
8498 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8505 backward_cb(GtkWidget
*w
, struct tab
*t
)
8510 show_oops(NULL
, "backward_cb invalid parameters");
8514 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8521 forward_cb(GtkWidget
*w
, struct tab
*t
)
8526 show_oops(NULL
, "forward_cb invalid parameters");
8530 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8532 a
.i
= XT_NAV_FORWARD
;
8537 home_cb(GtkWidget
*w
, struct tab
*t
)
8540 show_oops(NULL
, "home_cb invalid parameters");
8544 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8550 stop_cb(GtkWidget
*w
, struct tab
*t
)
8552 WebKitWebFrame
*frame
;
8555 show_oops(NULL
, "stop_cb invalid parameters");
8559 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8561 frame
= webkit_web_view_get_main_frame(t
->wv
);
8562 if (frame
== NULL
) {
8563 show_oops(t
, "stop_cb: no frame");
8567 webkit_web_frame_stop_loading(frame
);
8568 abort_favicon_download(t
);
8572 setup_webkit(struct tab
*t
)
8574 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8575 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8576 FALSE
, (char *)NULL
);
8578 warnx("webkit does not have \"enable-dns-prefetching\" property");
8579 g_object_set(G_OBJECT(t
->settings
),
8580 "user-agent", t
->user_agent
, (char *)NULL
);
8581 g_object_set(G_OBJECT(t
->settings
),
8582 "enable-scripts", enable_scripts
, (char *)NULL
);
8583 g_object_set(G_OBJECT(t
->settings
),
8584 "enable-plugins", enable_plugins
, (char *)NULL
);
8585 g_object_set(G_OBJECT(t
->settings
),
8586 "javascript-can-open-windows-automatically", enable_scripts
,
8588 g_object_set(G_OBJECT(t
->settings
),
8589 "enable-html5-database", FALSE
, (char *)NULL
);
8590 g_object_set(G_OBJECT(t
->settings
),
8591 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8592 g_object_set(G_OBJECT(t
->settings
),
8593 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8594 g_object_set(G_OBJECT(t
->settings
),
8595 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8596 g_object_set(G_OBJECT(t
->wv
),
8597 "full-content-zoom", TRUE
, (char *)NULL
);
8599 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8603 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8605 struct tab
*ti
, *t
= NULL
;
8606 gdouble view_size
, value
, max
;
8609 TAILQ_FOREACH(ti
, &tabs
, entry
)
8610 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8618 if (adjustment
== NULL
)
8619 adjustment
= gtk_scrolled_window_get_vadjustment(
8620 GTK_SCROLLED_WINDOW(t
->browser_win
));
8622 view_size
= gtk_adjustment_get_page_size(adjustment
);
8623 value
= gtk_adjustment_get_value(adjustment
);
8624 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8627 position
= g_strdup("All");
8628 else if (value
== max
)
8629 position
= g_strdup("Bot");
8630 else if (value
== 0)
8631 position
= g_strdup("Top");
8633 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8635 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8642 create_browser(struct tab
*t
)
8646 GtkAdjustment
*adjustment
;
8649 show_oops(NULL
, "create_browser invalid parameters");
8653 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8654 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8655 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8656 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8658 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8659 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8660 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8662 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8663 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8666 t
->settings
= webkit_web_settings_new();
8668 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
8670 if (user_agent
== NULL
) {
8671 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8673 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8676 t
->user_agent
= g_strdup(user_agent
);
8678 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8681 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8682 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8683 G_CALLBACK(update_statusbar_position
), NULL
);
8695 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8696 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8697 gtk_widget_set_name(w
, "xxxterm");
8698 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8699 g_signal_connect(G_OBJECT(w
), "delete_event",
8700 G_CALLBACK (gtk_main_quit
), NULL
);
8706 create_kiosk_toolbar(struct tab
*t
)
8708 GtkWidget
*toolbar
= NULL
, *b
;
8710 b
= gtk_hbox_new(FALSE
, 0);
8712 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8714 /* backward button */
8715 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8716 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8717 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8718 G_CALLBACK(backward_cb
), t
);
8719 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8721 /* forward button */
8722 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8723 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8724 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8725 G_CALLBACK(forward_cb
), t
);
8726 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8729 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8730 gtk_widget_set_sensitive(t
->gohome
, true);
8731 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8732 G_CALLBACK(home_cb
), t
);
8733 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8735 /* create widgets but don't use them */
8736 t
->uri_entry
= gtk_entry_new();
8737 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8738 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8739 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8745 create_toolbar(struct tab
*t
)
8747 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8749 b
= gtk_hbox_new(FALSE
, 0);
8751 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8753 /* backward button */
8754 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8755 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8756 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8757 G_CALLBACK(backward_cb
), t
);
8758 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8760 /* forward button */
8761 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8762 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8763 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8764 G_CALLBACK(forward_cb
), t
);
8765 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8769 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8770 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8771 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8772 G_CALLBACK(stop_cb
), t
);
8773 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8777 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8778 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8779 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8780 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8781 G_CALLBACK(js_toggle_cb
), t
);
8782 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8784 t
->uri_entry
= gtk_entry_new();
8785 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8786 G_CALLBACK(activate_uri_entry_cb
), t
);
8787 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8788 G_CALLBACK(entry_key_cb
), t
);
8790 eb1
= gtk_hbox_new(FALSE
, 0);
8791 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8792 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8793 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8796 if (search_string
) {
8798 t
->search_entry
= gtk_entry_new();
8799 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8800 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8801 G_CALLBACK(activate_search_entry_cb
), t
);
8802 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8803 G_CALLBACK(entry_key_cb
), t
);
8804 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8805 eb2
= gtk_hbox_new(FALSE
, 0);
8806 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8807 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8809 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8816 create_buffers(struct tab
*t
)
8818 GtkCellRenderer
*renderer
;
8821 view
= gtk_tree_view_new();
8823 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8825 renderer
= gtk_cell_renderer_text_new();
8826 gtk_tree_view_insert_column_with_attributes
8827 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8829 renderer
= gtk_cell_renderer_text_new();
8830 gtk_tree_view_insert_column_with_attributes
8831 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8834 gtk_tree_view_set_model
8835 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8841 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8842 GtkTreeViewColumn
*col
, struct tab
*t
)
8847 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8849 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8852 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8853 set_current_tab(id
- 1);
8859 /* after tab reordering/creation/removal */
8866 TAILQ_FOREACH(t
, &tabs
, entry
) {
8867 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8868 if (t
->tab_id
> maxid
)
8871 gtk_widget_show(t
->tab_elems
.sep
);
8874 TAILQ_FOREACH(t
, &tabs
, entry
) {
8875 if (t
->tab_id
== maxid
) {
8876 gtk_widget_hide(t
->tab_elems
.sep
);
8882 /* after active tab change */
8884 recolor_compact_tabs(void)
8890 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8891 TAILQ_FOREACH(t
, &tabs
, entry
)
8892 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8895 curid
= gtk_notebook_get_current_page(notebook
);
8896 TAILQ_FOREACH(t
, &tabs
, entry
)
8897 if (t
->tab_id
== curid
) {
8898 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8899 gtk_widget_modify_fg(t
->tab_elems
.label
,
8900 GTK_STATE_NORMAL
, &color
);
8906 set_current_tab(int page_num
)
8908 buffercmd_abort(get_current_tab());
8909 gtk_notebook_set_current_page(notebook
, page_num
);
8910 recolor_compact_tabs();
8914 undo_close_tab_save(struct tab
*t
)
8918 struct undo
*u1
, *u2
;
8920 WebKitWebHistoryItem
*item
;
8922 if ((uri
= get_uri(t
)) == NULL
)
8925 u1
= g_malloc0(sizeof(struct undo
));
8926 u1
->uri
= g_strdup(uri
);
8928 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8930 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8931 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8934 /* forward history */
8935 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8939 u1
->history
= g_list_prepend(u1
->history
,
8940 webkit_web_history_item_copy(item
));
8941 items
= g_list_next(items
);
8946 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8947 u1
->history
= g_list_prepend(u1
->history
,
8948 webkit_web_history_item_copy(item
));
8952 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8956 u1
->history
= g_list_prepend(u1
->history
,
8957 webkit_web_history_item_copy(item
));
8958 items
= g_list_next(items
);
8961 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8963 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8964 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8965 TAILQ_REMOVE(&undos
, u2
, entry
);
8967 g_list_free(u2
->history
);
8976 delete_tab(struct tab
*t
)
8980 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8986 * no need to join thread here because it won't access t on completion
8990 TAILQ_REMOVE(&tabs
, t
, entry
);
8992 /* Halt all webkit activity. */
8993 abort_favicon_download(t
);
8994 webkit_web_view_stop_loading(t
->wv
);
8996 /* Save the tab, so we can undo the close. */
8997 undo_close_tab_save(t
);
8999 if (browser_mode
== XT_BM_KIOSK
) {
9000 gtk_widget_destroy(t
->uri_entry
);
9001 gtk_widget_destroy(t
->stop
);
9002 gtk_widget_destroy(t
->js_toggle
);
9005 gtk_widget_destroy(t
->tab_elems
.eventbox
);
9006 gtk_widget_destroy(t
->vbox
);
9010 g_source_remove(t
->search_id
);
9012 g_free(t
->user_agent
);
9013 g_free(t
->stylesheet
);
9017 if (TAILQ_EMPTY(&tabs
)) {
9018 if (browser_mode
== XT_BM_KIOSK
)
9019 create_new_tab(home
, NULL
, 1, -1);
9021 create_new_tab(NULL
, NULL
, 1, -1);
9024 /* recreate session */
9025 if (session_autosave
) {
9031 recolor_compact_tabs();
9035 update_statusbar_zoom(struct tab
*t
)
9038 char s
[16] = { '\0' };
9040 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9041 if ((zoom
<= 0.99 || zoom
>= 1.01))
9042 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
9043 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
9047 setzoom_webkit(struct tab
*t
, int adjust
)
9049 #define XT_ZOOMPERCENT 0.04
9054 show_oops(NULL
, "setzoom_webkit invalid parameters");
9058 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9059 if (adjust
== XT_ZOOM_IN
)
9060 zoom
+= XT_ZOOMPERCENT
;
9061 else if (adjust
== XT_ZOOM_OUT
)
9062 zoom
-= XT_ZOOMPERCENT
;
9063 else if (adjust
> 0)
9064 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
9066 show_oops(t
, "setzoom_webkit invalid zoom value");
9070 if (zoom
< XT_ZOOMPERCENT
)
9071 zoom
= XT_ZOOMPERCENT
;
9072 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
9073 update_statusbar_zoom(t
);
9077 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
9079 struct tab
*t
= (struct tab
*) data
;
9081 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
9083 switch (event
->button
) {
9085 set_current_tab(t
->tab_id
);
9096 append_tab(struct tab
*t
)
9101 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9102 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9106 create_sbe(int width
)
9110 sbe
= gtk_entry_new();
9111 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9112 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9113 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9114 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9115 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9116 gtk_widget_set_size_request(sbe
, width
, -1);
9122 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9127 WebKitWebHistoryItem
*item
;
9131 int sbe_p
= 0, sbe_b
= 0,
9134 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9136 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9137 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9141 t
= g_malloc0(sizeof *t
);
9143 if (title
== NULL
) {
9144 title
= "(untitled)";
9148 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9150 /* label + button for tab */
9151 b
= gtk_hbox_new(FALSE
, 0);
9154 #if GTK_CHECK_VERSION(2, 20, 0)
9155 t
->spinner
= gtk_spinner_new();
9157 t
->label
= gtk_label_new(title
);
9158 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9159 gtk_widget_set_size_request(t
->label
, 100, 0);
9160 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9161 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9162 gtk_widget_set_size_request(b
, 130, 0);
9164 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9165 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9166 #if GTK_CHECK_VERSION(2, 20, 0)
9167 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9171 if (browser_mode
== XT_BM_KIOSK
) {
9172 t
->toolbar
= create_kiosk_toolbar(t
);
9173 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9176 t
->toolbar
= create_toolbar(t
);
9178 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9186 t
->browser_win
= create_browser(t
);
9187 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9189 /* oops message for user feedback */
9190 t
->oops
= gtk_entry_new();
9191 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9192 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9193 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9194 gdk_color_parse(XT_COLOR_RED
, &color
);
9195 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9196 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9197 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9200 t
->cmd
= gtk_entry_new();
9201 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9202 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9203 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9204 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9207 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9209 t
->sbe
.statusbar
= gtk_entry_new();
9210 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9211 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9212 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9213 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9215 /* create these widgets only if specified in statusbar_elems */
9217 t
->sbe
.position
= create_sbe(40);
9218 t
->sbe
.zoom
= create_sbe(40);
9219 t
->sbe
.buffercmd
= create_sbe(60);
9221 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9223 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9226 /* gtk widgets cannot be added to a box twice. sbe_* variables
9227 make sure of this */
9228 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9232 GtkWidget
*sep
= gtk_vseparator_new();
9234 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9235 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9236 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9237 FALSE
, FALSE
, FALSE
);
9242 warnx("flag \"%c\" specified more than "
9243 "once in statusbar_elems\n", *p
);
9247 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9248 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9252 warnx("flag \"%c\" specified more than "
9253 "once in statusbar_elems\n", *p
);
9257 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9258 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9262 warnx("flag \"%c\" specified more than "
9263 "once in statusbar_elems\n", *p
);
9267 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9268 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9271 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9276 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9279 t
->buffers
= create_buffers(t
);
9280 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9282 /* xtp meaning is normal by default */
9283 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9285 /* set empty favicon */
9286 xt_icon_from_name(t
, "text-html");
9288 /* and show it all */
9289 gtk_widget_show_all(b
);
9290 gtk_widget_show_all(t
->vbox
);
9292 /* compact tab bar */
9293 t
->tab_elems
.label
= gtk_label_new(title
);
9294 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9295 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9296 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9297 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9299 t
->tab_elems
.eventbox
= gtk_event_box_new();
9300 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9301 t
->tab_elems
.sep
= gtk_vseparator_new();
9303 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9304 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9305 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9306 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9307 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9308 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9310 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9312 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9314 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9317 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9319 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9321 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9324 id
= position
>= 0 ? position
:
9325 gtk_notebook_get_current_page(notebook
) + 1;
9326 if (id
> gtk_notebook_get_n_pages(notebook
))
9329 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9330 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9331 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9332 t
->tab_elems
.eventbox
, id
);
9337 #if GTK_CHECK_VERSION(2, 20, 0)
9338 /* turn spinner off if we are a new tab without uri */
9340 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9341 gtk_widget_hide(t
->spinner
);
9344 /* make notebook tabs reorderable */
9345 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9347 /* compact tabs clickable */
9348 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9349 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9351 g_object_connect(G_OBJECT(t
->cmd
),
9352 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9353 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9354 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9355 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9356 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9359 /* reuse wv_button_cb to hide oops */
9360 g_object_connect(G_OBJECT(t
->oops
),
9361 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9364 g_signal_connect(t
->buffers
,
9365 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9366 g_object_connect(G_OBJECT(t
->buffers
),
9367 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9369 g_object_connect(G_OBJECT(t
->wv
),
9370 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9371 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9372 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9373 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9374 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9375 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9376 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9377 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9378 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9379 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9380 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9381 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9382 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9383 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9384 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9385 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9387 g_signal_connect(t
->wv
,
9388 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9389 g_signal_connect(t
->wv
,
9390 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9392 /* hijack the unused keys as if we were the browser */
9393 g_object_connect(G_OBJECT(t
->toolbar
),
9394 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9397 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9398 G_CALLBACK(tab_close_cb
), t
);
9401 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9402 /* restore the tab's history */
9403 if (u
&& u
->history
) {
9407 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9408 items
= g_list_next(items
);
9411 item
= g_list_nth_data(u
->history
, u
->back
);
9413 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9416 g_list_free(u
->history
);
9418 webkit_web_back_forward_list_clear(t
->bfl
);
9424 url_set_visibility();
9425 statusbar_set_visibility();
9428 set_current_tab(t
->tab_id
);
9429 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9433 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9437 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9444 recolor_compact_tabs();
9445 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9450 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9456 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9458 if (gtk_notebook_get_current_page(notebook
) == -1)
9461 TAILQ_FOREACH(t
, &tabs
, entry
) {
9462 if (t
->tab_id
== pn
) {
9463 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9466 uri
= get_title(t
, TRUE
);
9467 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9473 /* can't use focus_webview here */
9474 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9481 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9484 struct tab
*t
= NULL
, *tt
;
9488 TAILQ_FOREACH(tt
, &tabs
, entry
)
9489 if (tt
->tab_id
== pn
) {
9495 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9497 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9502 menuitem_response(struct tab
*t
)
9504 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9508 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9510 GtkWidget
*menu
, *menu_items
;
9511 GdkEventButton
*bevent
;
9515 if (event
->type
== GDK_BUTTON_PRESS
) {
9516 bevent
= (GdkEventButton
*) event
;
9517 menu
= gtk_menu_new();
9519 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9520 if ((uri
= get_uri(ti
)) == NULL
)
9521 /* XXX make sure there is something to print */
9522 /* XXX add gui pages in here to look purdy */
9524 menu_items
= gtk_menu_item_new_with_label(uri
);
9525 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9526 gtk_widget_show(menu_items
);
9528 g_signal_connect_swapped((menu_items
),
9529 "activate", G_CALLBACK(menuitem_response
),
9533 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9534 bevent
->button
, bevent
->time
);
9536 /* unref object so it'll free itself when popped down */
9537 #if !GTK_CHECK_VERSION(3, 0, 0)
9538 /* XXX does not need unref with gtk+3? */
9539 g_object_ref_sink(menu
);
9540 g_object_unref(menu
);
9543 return (TRUE
/* eat event */);
9546 return (FALSE
/* propagate */);
9550 icon_size_map(int icon_size
)
9552 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9553 icon_size
> GTK_ICON_SIZE_DIALOG
)
9554 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9560 create_button(char *name
, char *stockid
, int size
)
9562 GtkWidget
*button
, *image
;
9566 rcstring
= g_strdup_printf(
9567 "style \"%s-style\"\n"
9569 " GtkWidget::focus-padding = 0\n"
9570 " GtkWidget::focus-line-width = 0\n"
9574 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9575 gtk_rc_parse_string(rcstring
);
9577 button
= gtk_button_new();
9578 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9579 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9581 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9582 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9583 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9584 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9585 gtk_widget_set_name(button
, name
);
9586 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9592 button_set_stockid(GtkWidget
*button
, char *stockid
)
9596 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9597 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9598 gtk_button_set_image(GTK_BUTTON(button
), image
);
9602 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9605 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9608 if (xterm_workaround
== 0)
9612 * xterm doesn't play nice with clipboards because it clears the
9613 * primary when clicked. We rely on primary being set to properly
9614 * handle middle mouse button clicks (paste). So when someone clears
9615 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9616 * other application behavior (as in DON'T clear primary).
9619 p
= gtk_clipboard_wait_for_text(primary
);
9621 if (gdk_property_get(gdk_get_default_root_window(),
9623 gdk_atom_intern("STRING", FALSE
),
9625 1024 * 1024 /* picked out of my butt */,
9631 /* yes sir, we need to NUL the string */
9633 gtk_clipboard_set_text(primary
, p
, -1);
9647 char file
[PATH_MAX
];
9650 vbox
= gtk_vbox_new(FALSE
, 0);
9651 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9652 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9653 #if !GTK_CHECK_VERSION(3, 0, 0)
9654 /* XXX seems to be needed with gtk+2 */
9655 gtk_notebook_set_tab_hborder(notebook
, 0);
9656 gtk_notebook_set_tab_vborder(notebook
, 0);
9658 gtk_notebook_set_scrollable(notebook
, TRUE
);
9659 gtk_notebook_set_show_border(notebook
, FALSE
);
9660 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9662 abtn
= gtk_button_new();
9663 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9664 gtk_widget_set_size_request(arrow
, -1, -1);
9665 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9666 gtk_widget_set_size_request(abtn
, -1, 20);
9668 #if GTK_CHECK_VERSION(2, 20, 0)
9669 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9671 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9673 /* compact tab bar */
9674 tab_bar
= gtk_hbox_new(TRUE
, 0);
9676 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9677 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9678 gtk_widget_set_size_request(vbox
, -1, -1);
9680 g_object_connect(G_OBJECT(notebook
),
9681 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9683 g_object_connect(G_OBJECT(notebook
),
9684 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9685 NULL
, (char *)NULL
);
9686 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9687 G_CALLBACK(arrow_cb
), NULL
);
9689 main_window
= create_window();
9690 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9693 for (i
= 0; i
< LENGTH(icons
); i
++) {
9694 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9695 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9696 l
= g_list_append(l
, pb
);
9698 gtk_window_set_default_icon_list(l
);
9700 /* clipboard work around */
9701 if (xterm_workaround
)
9703 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9704 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9706 gtk_widget_show_all(abtn
);
9707 gtk_widget_show_all(main_window
);
9708 notebook_tab_set_visibility();
9712 set_hook(void **hook
, char *name
)
9715 errx(1, "set_hook");
9717 if (*hook
== NULL
) {
9718 *hook
= dlsym(RTLD_NEXT
, name
);
9720 errx(1, "can't hook %s", name
);
9724 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9726 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9728 g_return_val_if_fail(cookie1
, FALSE
);
9729 g_return_val_if_fail(cookie2
, FALSE
);
9731 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9732 !strcmp (cookie1
->value
, cookie2
->value
) &&
9733 !strcmp (cookie1
->path
, cookie2
->path
) &&
9734 !strcmp (cookie1
->domain
, cookie2
->domain
));
9738 transfer_cookies(void)
9741 SoupCookie
*sc
, *pc
;
9743 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9745 for (;cf
; cf
= cf
->next
) {
9747 sc
= soup_cookie_copy(pc
);
9748 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9751 soup_cookies_free(cf
);
9755 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9760 print_cookie("soup_cookie_jar_delete_cookie", c
);
9762 if (cookies_enabled
== 0)
9765 if (jar
== NULL
|| c
== NULL
)
9768 /* find and remove from persistent jar */
9769 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9771 for (;cf
; cf
= cf
->next
) {
9773 if (soup_cookie_equal(ci
, c
)) {
9774 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9779 soup_cookies_free(cf
);
9781 /* delete from session jar */
9782 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9786 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9788 struct domain
*d
= NULL
;
9792 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9793 jar
, p_cookiejar
, s_cookiejar
);
9795 if (cookies_enabled
== 0)
9798 /* see if we are up and running */
9799 if (p_cookiejar
== NULL
) {
9800 _soup_cookie_jar_add_cookie(jar
, cookie
);
9803 /* disallow p_cookiejar adds, shouldn't happen */
9804 if (jar
== p_cookiejar
)
9808 if (jar
== NULL
|| cookie
== NULL
)
9811 if (enable_cookie_whitelist
&&
9812 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9814 DNPRINTF(XT_D_COOKIE
,
9815 "soup_cookie_jar_add_cookie: reject %s\n",
9817 if (save_rejected_cookies
) {
9818 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9819 show_oops(NULL
, "can't open reject cookie file");
9822 fseek(r_cookie_f
, 0, SEEK_END
);
9823 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9824 cookie
->http_only
? "#HttpOnly_" : "",
9826 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9828 cookie
->secure
? "TRUE" : "FALSE",
9830 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9837 if (!allow_volatile_cookies
)
9841 if (cookie
->expires
== NULL
&& session_timeout
) {
9842 soup_cookie_set_expires(cookie
,
9843 soup_date_new_from_now(session_timeout
));
9844 print_cookie("modified add cookie", cookie
);
9847 /* see if we are white listed for persistence */
9848 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9849 /* add to persistent jar */
9850 c
= soup_cookie_copy(cookie
);
9851 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9852 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9855 /* add to session jar */
9856 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9857 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9863 char file
[PATH_MAX
];
9865 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9866 "soup_cookie_jar_add_cookie");
9867 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9868 "soup_cookie_jar_delete_cookie");
9870 if (cookies_enabled
== 0)
9874 * the following code is intricate due to overriding several libsoup
9876 * do not alter order of these operations.
9879 /* rejected cookies */
9880 if (save_rejected_cookies
)
9881 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9884 /* persistent cookies */
9885 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9886 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9888 /* session cookies */
9889 s_cookiejar
= soup_cookie_jar_new();
9890 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9891 cookie_policy
, (void *)NULL
);
9894 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9898 setup_proxy(char *uri
)
9901 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9902 soup_uri_free(proxy_uri
);
9906 if (http_proxy
!= uri
) {
9913 http_proxy
= g_strdup(uri
);
9914 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9915 proxy_uri
= soup_uri_new(http_proxy
);
9916 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
9917 g_object_set(session
, "proxy-uri", proxy_uri
,
9923 set_http_proxy(char *proxy
)
9930 /* see if we need to clear it instead */
9931 if (strlen(proxy
) == 0) {
9936 uri
= soup_uri_new(proxy
);
9937 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9948 send_cmd_to_socket(char *cmd
)
9951 struct sockaddr_un sa
;
9953 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9954 warnx("%s: socket", __func__
);
9958 sa
.sun_family
= AF_UNIX
;
9959 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9960 work_dir
, XT_SOCKET_FILE
);
9963 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9964 warnx("%s: connect", __func__
);
9968 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9969 warnx("%s: send", __func__
);
9980 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9983 char str
[XT_MAX_URL_LENGTH
];
9984 socklen_t t
= sizeof(struct sockaddr_un
);
9985 struct sockaddr_un sa
;
9990 gint fd
= g_io_channel_unix_get_fd(source
);
9992 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9997 if (getpeereid(s
, &uid
, &gid
) == -1) {
10001 if (uid
!= getuid() || gid
!= getgid()) {
10002 warnx("unauthorized user");
10008 warnx("not a valid user");
10012 n
= recv(s
, str
, sizeof(str
), 0);
10016 tt
= TAILQ_LAST(&tabs
, tab_list
);
10017 cmd_execute(tt
, str
);
10024 int s
, len
, rv
= 1;
10025 struct sockaddr_un sa
;
10027 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10028 warn("is_running: socket");
10032 sa
.sun_family
= AF_UNIX
;
10033 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10034 work_dir
, XT_SOCKET_FILE
);
10035 len
= SUN_LEN(&sa
);
10037 /* connect to see if there is a listener */
10038 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
10039 rv
= 0; /* not running */
10041 rv
= 1; /* already running */
10052 struct sockaddr_un sa
;
10054 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10055 warn("build_socket: socket");
10059 sa
.sun_family
= AF_UNIX
;
10060 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10061 work_dir
, XT_SOCKET_FILE
);
10062 len
= SUN_LEN(&sa
);
10064 /* connect to see if there is a listener */
10065 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10066 /* no listener so we will */
10067 unlink(sa
.sun_path
);
10069 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10070 warn("build_socket: bind");
10074 if (listen(s
, 1) == -1) {
10075 warn("build_socket: listen");
10088 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10089 GtkTreeIter
*iter
, struct tab
*t
)
10093 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10094 load_uri(t
, value
);
10101 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10102 GtkTreeIter
*iter
, struct tab
*t
)
10106 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10107 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10108 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10115 completion_add_uri(const gchar
*uri
)
10119 /* add uri to list_store */
10120 gtk_list_store_append(completion_model
, &iter
);
10121 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10125 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10126 GtkTreeIter
*iter
, gpointer user_data
)
10129 gboolean match
= FALSE
;
10131 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10137 match
= match_uri(value
, key
);
10144 completion_add(struct tab
*t
)
10146 /* enable completion for tab */
10147 t
->completion
= gtk_entry_completion_new();
10148 gtk_entry_completion_set_text_column(t
->completion
, 0);
10149 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10150 gtk_entry_completion_set_model(t
->completion
,
10151 GTK_TREE_MODEL(completion_model
));
10152 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10154 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10155 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10156 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10157 G_CALLBACK(completion_select_cb
), t
);
10158 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10159 G_CALLBACK(completion_hover_cb
), t
);
10167 if (stat(dir
, &sb
)) {
10168 if (mkdir(dir
, S_IRWXU
) == -1)
10169 err(1, "mkdir %s", dir
);
10170 if (stat(dir
, &sb
))
10171 err(1, "stat %s", dir
);
10173 if (S_ISDIR(sb
.st_mode
) == 0)
10174 errx(1, "%s not a dir", dir
);
10175 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10176 warnx("fixing invalid permissions on %s", dir
);
10177 if (chmod(dir
, S_IRWXU
) == -1)
10178 err(1, "chmod %s", dir
);
10186 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10191 main(int argc
, char *argv
[])
10194 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10195 char conf
[PATH_MAX
] = { '\0' };
10196 char file
[PATH_MAX
];
10197 char *env_proxy
= NULL
;
10201 struct sigaction sact
;
10202 GIOChannel
*channel
;
10209 g_thread_init(NULL
);
10210 #if !defined(__linux__)
10211 /* this call hangs the flash plugin and isn't needed on linux it seems */
10212 gdk_threads_init();
10214 gdk_threads_enter();
10216 gtk_init(&argc
, &argv
);
10219 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pthread
);
10221 gnutls_global_init();
10223 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10227 RB_INIT(&downloads
);
10229 TAILQ_INIT(&sessions
);
10232 TAILQ_INIT(&aliases
);
10233 TAILQ_INIT(&undos
);
10239 /* fiddle with ulimits */
10240 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10243 /* just use them all */
10244 rlp
.rlim_cur
= rlp
.rlim_max
;
10245 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10247 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10249 else if (rlp
.rlim_cur
<= 256)
10250 startpage_add("%s requires at least 256 file "
10251 "descriptors, currently it has up to %d available",
10252 __progname
, rlp
.rlim_cur
);
10255 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10264 errx(0 , "Version: %s", version
);
10267 strlcpy(conf
, optarg
, sizeof(conf
));
10270 strlcpy(named_session
, optarg
, sizeof(named_session
));
10289 init_keybindings();
10291 /* generate session keys for xtp pages */
10292 generate_xtp_session_key(&dl_session_key
);
10293 generate_xtp_session_key(&hl_session_key
);
10294 generate_xtp_session_key(&cl_session_key
);
10295 generate_xtp_session_key(&fl_session_key
);
10298 bzero(&sact
, sizeof(sact
));
10299 sigemptyset(&sact
.sa_mask
);
10300 sact
.sa_handler
= sigchild
;
10301 sact
.sa_flags
= SA_NOCLDSTOP
;
10302 sigaction(SIGCHLD
, &sact
, NULL
);
10304 /* set download dir */
10305 pwd
= getpwuid(getuid());
10307 errx(1, "invalid user %d", getuid());
10308 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10310 /* compile buffer command regexes */
10313 /* set default string settings */
10314 home
= g_strdup("https://www.cyphertite.com");
10315 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10316 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10317 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10318 cmd_font_name
= g_strdup("monospace normal 9");
10319 oops_font_name
= g_strdup("monospace normal 9");
10320 statusbar_font_name
= g_strdup("monospace normal 9");
10321 tabbar_font_name
= g_strdup("monospace normal 9");
10322 statusbar_elems
= g_strdup("BP");
10323 encoding
= g_strdup("ISO-8859-1");
10325 /* read config file */
10326 if (strlen(conf
) == 0)
10327 snprintf(conf
, sizeof conf
, "%s/.%s",
10328 pwd
->pw_dir
, XT_CONF_FILE
);
10329 config_parse(conf
, 0);
10332 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10333 oops_font
= pango_font_description_from_string(oops_font_name
);
10334 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10335 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10337 /* working directory */
10338 if (strlen(work_dir
) == 0)
10339 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10340 pwd
->pw_dir
, XT_DIR
);
10343 /* icon cache dir */
10344 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10345 xxx_dir(cache_dir
);
10348 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10349 xxx_dir(certs_dir
);
10352 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10353 work_dir
, XT_SESSIONS_DIR
);
10354 xxx_dir(sessions_dir
);
10356 /* runtime settings that can override config file */
10357 if (runtime_settings
[0] != '\0')
10358 config_parse(runtime_settings
, 1);
10361 if (!strcmp(download_dir
, pwd
->pw_dir
))
10362 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10363 xxx_dir(download_dir
);
10365 /* favorites file */
10366 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10367 if (stat(file
, &sb
)) {
10368 warnx("favorites file doesn't exist, creating it");
10369 if ((f
= fopen(file
, "w")) == NULL
)
10370 err(1, "favorites");
10374 /* quickmarks file */
10375 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10376 if (stat(file
, &sb
)) {
10377 warnx("quickmarks file doesn't exist, creating it");
10378 if ((f
= fopen(file
, "w")) == NULL
)
10379 err(1, "quickmarks");
10383 /* search history */
10384 if (history_autosave
) {
10385 snprintf(search_file
, sizeof search_file
, "%s/%s",
10386 work_dir
, XT_SEARCH_FILE
);
10387 if (stat(search_file
, &sb
)) {
10388 warnx("search history file doesn't exist, creating it");
10389 if ((f
= fopen(search_file
, "w")) == NULL
)
10390 err(1, "search_history");
10393 history_read(&shl
, search_file
, &search_history_count
);
10396 /* command history */
10397 if (history_autosave
) {
10398 snprintf(command_file
, sizeof command_file
, "%s/%s",
10399 work_dir
, XT_COMMAND_FILE
);
10400 if (stat(command_file
, &sb
)) {
10401 warnx("command history file doesn't exist, creating it");
10402 if ((f
= fopen(command_file
, "w")) == NULL
)
10403 err(1, "command_history");
10406 history_read(&chl
, command_file
, &cmd_history_count
);
10410 session
= webkit_get_default_session();
10415 if (stat(ssl_ca_file
, &sb
)) {
10416 warnx("no CA file: %s", ssl_ca_file
);
10417 g_free(ssl_ca_file
);
10418 ssl_ca_file
= NULL
;
10420 g_object_set(session
,
10421 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10422 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10426 /* guess_search regex */
10427 if (url_regex
== NULL
)
10428 url_regex
= g_strdup(XT_URL_REGEX
);
10430 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10431 startpage_add("invalid url regex %s", url_regex
);
10434 env_proxy
= getenv("http_proxy");
10436 setup_proxy(env_proxy
);
10438 setup_proxy(http_proxy
);
10441 send_cmd_to_socket(argv
[0]);
10445 /* set some connection parameters */
10446 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10447 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10450 /* see if there is already an xxxterm running */
10451 if (single_instance
&& is_running()) {
10453 warnx("already running");
10458 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10459 send_cmd_to_socket(cmd
);
10469 /* uri completion */
10470 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10473 buffers_store
= gtk_list_store_new
10474 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10480 notebook_tab_set_visibility();
10482 if (save_global_history
)
10483 restore_global_history();
10485 /* restore session list */
10486 restore_sessions_list();
10488 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10489 restore_saved_tabs();
10491 a
.s
= named_session
;
10492 a
.i
= XT_SES_DONOTHING
;
10493 open_tabs(NULL
, &a
);
10496 /* see if we have an exception */
10497 if (!TAILQ_EMPTY(&spl
)) {
10498 create_new_tab("about:startpage", NULL
, focus
, -1);
10503 create_new_tab(argv
[0], NULL
, focus
, -1);
10510 if (TAILQ_EMPTY(&tabs
))
10511 create_new_tab(home
, NULL
, 1, -1);
10514 if ((s
= build_socket()) != -1) {
10515 channel
= g_io_channel_unix_new(s
);
10516 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10521 gnutls_global_deinit();
10527 gdk_threads_leave();