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 #elif defined(__FreeBSD__)
51 #include "freebsd/util.h"
57 #include <sys/queue.h>
58 #include <sys/resource.h>
59 #include <sys/socket.h>
65 #include <gdk/gdkkeysyms.h>
67 #if GTK_CHECK_VERSION(3,0,0)
68 /* we still use GDK_* instead of GDK_KEY_* */
69 #include <gdk/gdkkeysyms-compat.h>
72 #include <webkit/webkit.h>
73 #include <libsoup/soup.h>
74 #include <gnutls/gnutls.h>
75 #include <JavaScriptCore/JavaScript.h>
76 #include <gnutls/x509.h>
78 #include "javascript.h"
81 javascript.h borrowed from vimprobable2 under the following license:
83 Copyright (c) 2009 Leon Winter
84 Copyright (c) 2009 Hannes Schueller
85 Copyright (c) 2009 Matto Fransen
87 Permission is hereby granted, free of charge, to any person obtaining a copy
88 of this software and associated documentation files (the "Software"), to deal
89 in the Software without restriction, including without limitation the rights
90 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
91 copies of the Software, and to permit persons to whom the Software is
92 furnished to do so, subject to the following conditions:
94 The above copyright notice and this permission notice shall be included in
95 all copies or substantial portions of the Software.
97 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
98 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
99 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
100 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
101 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
102 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
106 static char *version
= "$xxxterm$";
108 /* hooked functions */
109 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
110 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
115 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
116 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
117 #define XT_D_MOVE 0x0001
118 #define XT_D_KEY 0x0002
119 #define XT_D_TAB 0x0004
120 #define XT_D_URL 0x0008
121 #define XT_D_CMD 0x0010
122 #define XT_D_NAV 0x0020
123 #define XT_D_DOWNLOAD 0x0040
124 #define XT_D_CONFIG 0x0080
125 #define XT_D_JS 0x0100
126 #define XT_D_FAVORITE 0x0200
127 #define XT_D_PRINTING 0x0400
128 #define XT_D_COOKIE 0x0800
129 #define XT_D_KEYBINDING 0x1000
130 #define XT_D_CLIP 0x2000
131 #define XT_D_BUFFERCMD 0x4000
132 u_int32_t swm_debug
= 0
150 #define DPRINTF(x...)
151 #define DNPRINTF(n,x...)
154 #define LENGTH(x) (sizeof x / sizeof x[0])
155 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
156 ~(GDK_BUTTON1_MASK) & \
157 ~(GDK_BUTTON2_MASK) & \
158 ~(GDK_BUTTON3_MASK) & \
159 ~(GDK_BUTTON4_MASK) & \
162 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
173 TAILQ_ENTRY(tab
) entry
;
175 GtkWidget
*tab_content
;
184 GtkWidget
*uri_entry
;
185 GtkWidget
*search_entry
;
187 GtkWidget
*browser_win
;
188 GtkWidget
*statusbar_box
;
190 GtkWidget
*statusbar
;
191 GtkWidget
*buffercmd
;
202 GtkWidget
*js_toggle
;
203 GtkEntryCompletion
*completion
;
207 WebKitWebHistoryItem
*item
;
208 WebKitWebBackForwardList
*bfl
;
211 WebKitNetworkRequest
*icon_request
;
212 WebKitDownload
*icon_download
;
213 gchar
*icon_dest_uri
;
215 /* adjustments for browser */
218 GtkAdjustment
*adjust_h
;
219 GtkAdjustment
*adjust_v
;
225 int xtp_meaning
; /* identifies dls/favorites */
227 int popup
; /* 1 if cmd_entry has popup visible */
232 #define XT_HINT_NONE (0)
233 #define XT_HINT_NUMERICAL (1)
234 #define XT_HINT_ALPHANUM (2)
238 /* custom stylesheet */
248 WebKitWebSettings
*settings
;
252 double mark
[XT_NOMARKS
];
254 TAILQ_HEAD(tab_list
, tab
);
257 RB_ENTRY(history
) entry
;
261 RB_HEAD(history_list
, history
);
264 TAILQ_ENTRY(session
) entry
;
267 TAILQ_HEAD(session_list
, session
);
270 RB_ENTRY(download
) entry
;
272 WebKitDownload
*download
;
275 RB_HEAD(download_list
, download
);
278 RB_ENTRY(domain
) entry
;
280 int handy
; /* app use */
282 RB_HEAD(domain_list
, domain
);
285 TAILQ_ENTRY(undo
) entry
;
288 int back
; /* Keeps track of how many back
289 * history items there are. */
291 TAILQ_HEAD(undo_tailq
, undo
);
295 TAILQ_ENTRY(sp
) entry
;
297 TAILQ_HEAD(sp_list
, sp
);
299 struct command_entry
{
301 TAILQ_ENTRY(command_entry
) entry
;
303 TAILQ_HEAD(command_list
, command_entry
);
305 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
306 int next_download_id
= 1;
315 #define XT_NAME ("XXXTerm")
316 #define XT_DIR (".xxxterm")
317 #define XT_CACHE_DIR ("cache")
318 #define XT_CERT_DIR ("certs/")
319 #define XT_SESSIONS_DIR ("sessions/")
320 #define XT_CONF_FILE ("xxxterm.conf")
321 #define XT_FAVS_FILE ("favorites")
322 #define XT_QMARKS_FILE ("quickmarks")
323 #define XT_SAVED_TABS_FILE ("main_session")
324 #define XT_RESTART_TABS_FILE ("restart_tabs")
325 #define XT_SOCKET_FILE ("socket")
326 #define XT_HISTORY_FILE ("history")
327 #define XT_REJECT_FILE ("rejected.txt")
328 #define XT_COOKIE_FILE ("cookies.txt")
329 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
330 #define XT_SEARCH_FILE ("search_history")
331 #define XT_COMMAND_FILE ("command_history")
332 #define XT_CB_HANDLED (TRUE)
333 #define XT_CB_PASSTHROUGH (FALSE)
334 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
335 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
336 #define XT_DLMAN_REFRESH "10"
337 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
338 "td{overflow: hidden;" \
339 " padding: 2px 2px 2px 2px;" \
340 " border: 1px solid black;" \
341 " vertical-align:top;" \
342 " word-wrap: break-word}\n" \
343 "tr:hover{background: #ffff99}\n" \
344 "th{background-color: #cccccc;" \
345 " border: 1px solid black}\n" \
346 "table{width: 100%%;" \
347 " border: 1px black solid;" \
348 " border-collapse:collapse}\n" \
350 "border: 1px solid black;" \
353 ".progress-inner{float: left;" \
355 " background: green}\n" \
356 ".dlstatus{font-size: small;" \
357 " text-align: center}\n" \
359 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
360 #define XT_MAX_UNDO_CLOSE_TAB (32)
361 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
362 #define XT_PRINT_EXTRA_MARGIN 10
363 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
364 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
367 #define XT_COLOR_RED "#cc0000"
368 #define XT_COLOR_YELLOW "#ffff66"
369 #define XT_COLOR_BLUE "lightblue"
370 #define XT_COLOR_GREEN "#99ff66"
371 #define XT_COLOR_WHITE "white"
372 #define XT_COLOR_BLACK "black"
374 #define XT_COLOR_CT_BACKGROUND "#000000"
375 #define XT_COLOR_CT_INACTIVE "#dddddd"
376 #define XT_COLOR_CT_ACTIVE "#bbbb00"
377 #define XT_COLOR_CT_SEPARATOR "#555555"
379 #define XT_COLOR_SB_SEPARATOR "#555555"
381 #define XT_PROTO_DELIM "://"
384 * xxxterm "protocol" (xtp)
385 * We use this for managing stuff like downloads and favorites. They
386 * make magical HTML pages in memory which have xxxt:// links in order
387 * to communicate with xxxterm's internals. These links take the format:
388 * xxxt://class/session_key/action/arg
390 * Don't begin xtp class/actions as 0. atoi returns that on error.
392 * Typically we have not put addition of items in this framework, as
393 * adding items is either done via an ex-command or via a keybinding instead.
396 #define XT_XTP_STR "xxxt://"
398 /* XTP classes (xxxt://<class>) */
399 #define XT_XTP_INVALID 0 /* invalid */
400 #define XT_XTP_DL 1 /* downloads */
401 #define XT_XTP_HL 2 /* history */
402 #define XT_XTP_CL 3 /* cookies */
403 #define XT_XTP_FL 4 /* favorites */
405 /* XTP download actions */
406 #define XT_XTP_DL_LIST 1
407 #define XT_XTP_DL_CANCEL 2
408 #define XT_XTP_DL_REMOVE 3
410 /* XTP history actions */
411 #define XT_XTP_HL_LIST 1
412 #define XT_XTP_HL_REMOVE 2
414 /* XTP cookie actions */
415 #define XT_XTP_CL_LIST 1
416 #define XT_XTP_CL_REMOVE 2
418 /* XTP cookie actions */
419 #define XT_XTP_FL_LIST 1
420 #define XT_XTP_FL_REMOVE 2
423 #define XT_MOVE_INVALID (0)
424 #define XT_MOVE_DOWN (1)
425 #define XT_MOVE_UP (2)
426 #define XT_MOVE_BOTTOM (3)
427 #define XT_MOVE_TOP (4)
428 #define XT_MOVE_PAGEDOWN (5)
429 #define XT_MOVE_PAGEUP (6)
430 #define XT_MOVE_HALFDOWN (7)
431 #define XT_MOVE_HALFUP (8)
432 #define XT_MOVE_LEFT (9)
433 #define XT_MOVE_FARLEFT (10)
434 #define XT_MOVE_RIGHT (11)
435 #define XT_MOVE_FARRIGHT (12)
436 #define XT_MOVE_PERCENT (13)
438 #define XT_QMARK_SET (0)
439 #define XT_QMARK_OPEN (1)
440 #define XT_QMARK_TAB (2)
442 #define XT_MARK_SET (0)
443 #define XT_MARK_GOTO (1)
445 #define XT_TAB_LAST (-4)
446 #define XT_TAB_FIRST (-3)
447 #define XT_TAB_PREV (-2)
448 #define XT_TAB_NEXT (-1)
449 #define XT_TAB_INVALID (0)
450 #define XT_TAB_NEW (1)
451 #define XT_TAB_DELETE (2)
452 #define XT_TAB_DELQUIT (3)
453 #define XT_TAB_OPEN (4)
454 #define XT_TAB_UNDO_CLOSE (5)
455 #define XT_TAB_SHOW (6)
456 #define XT_TAB_HIDE (7)
457 #define XT_TAB_NEXTSTYLE (8)
459 #define XT_NAV_INVALID (0)
460 #define XT_NAV_BACK (1)
461 #define XT_NAV_FORWARD (2)
462 #define XT_NAV_RELOAD (3)
464 #define XT_FOCUS_INVALID (0)
465 #define XT_FOCUS_URI (1)
466 #define XT_FOCUS_SEARCH (2)
468 #define XT_SEARCH_INVALID (0)
469 #define XT_SEARCH_NEXT (1)
470 #define XT_SEARCH_PREV (2)
472 #define XT_PASTE_CURRENT_TAB (0)
473 #define XT_PASTE_NEW_TAB (1)
475 #define XT_ZOOM_IN (-1)
476 #define XT_ZOOM_OUT (-2)
477 #define XT_ZOOM_NORMAL (100)
479 #define XT_URL_SHOW (1)
480 #define XT_URL_HIDE (2)
482 #define XT_WL_TOGGLE (1<<0)
483 #define XT_WL_ENABLE (1<<1)
484 #define XT_WL_DISABLE (1<<2)
485 #define XT_WL_FQDN (1<<3) /* default */
486 #define XT_WL_TOPLEVEL (1<<4)
487 #define XT_WL_PERSISTENT (1<<5)
488 #define XT_WL_SESSION (1<<6)
489 #define XT_WL_RELOAD (1<<7)
491 #define XT_SHOW (1<<7)
492 #define XT_DELETE (1<<8)
493 #define XT_SAVE (1<<9)
494 #define XT_OPEN (1<<10)
496 #define XT_CMD_OPEN (0)
497 #define XT_CMD_OPEN_CURRENT (1)
498 #define XT_CMD_TABNEW (2)
499 #define XT_CMD_TABNEW_CURRENT (3)
501 #define XT_STATUS_NOTHING (0)
502 #define XT_STATUS_LINK (1)
503 #define XT_STATUS_URI (2)
504 #define XT_STATUS_LOADING (3)
506 #define XT_SES_DONOTHING (0)
507 #define XT_SES_CLOSETABS (1)
509 #define XT_BM_NORMAL (0)
510 #define XT_BM_WHITELIST (1)
511 #define XT_BM_KIOSK (2)
513 #define XT_PREFIX (1<<0)
514 #define XT_USERARG (1<<1)
515 #define XT_URLARG (1<<2)
516 #define XT_INTARG (1<<3)
517 #define XT_SESSARG (1<<4)
518 #define XT_SETARG (1<<5)
520 #define XT_TABS_NORMAL 0
521 #define XT_TABS_COMPACT 1
523 #define XT_BUFCMD_SZ (8)
531 TAILQ_ENTRY(mime_type
) entry
;
533 TAILQ_HEAD(mime_type_list
, mime_type
);
539 TAILQ_ENTRY(alias
) entry
;
541 TAILQ_HEAD(alias_list
, alias
);
543 /* settings that require restart */
544 int tabless
= 0; /* allow only 1 tab */
545 int enable_socket
= 0;
546 int single_instance
= 0; /* only allow one xxxterm to run */
547 int fancy_bar
= 1; /* fancy toolbar */
548 int browser_mode
= XT_BM_NORMAL
;
549 int enable_localstorage
= 1;
550 char *statusbar_elems
= NULL
;
552 /* runtime settings */
553 int show_tabs
= 1; /* show tabs on notebook */
554 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
555 int show_url
= 1; /* show url toolbar on notebook */
556 int show_statusbar
= 0; /* vimperator style status bar */
557 int ctrl_click_focus
= 0; /* ctrl click gets focus */
558 int cookies_enabled
= 1; /* enable cookies */
559 int read_only_cookies
= 0; /* enable to not write cookies */
560 int enable_scripts
= 1;
561 int enable_plugins
= 0;
562 gfloat default_zoom_level
= 1.0;
563 char default_script
[PATH_MAX
];
564 int window_height
= 768;
565 int window_width
= 1024;
566 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
567 int refresh_interval
= 10; /* download refresh interval */
568 int enable_cookie_whitelist
= 0;
569 int enable_js_whitelist
= 0;
570 int session_timeout
= 3600; /* cookie session timeout */
571 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
572 char *ssl_ca_file
= NULL
;
573 char *resource_dir
= NULL
;
574 gboolean ssl_strict_certs
= FALSE
;
575 int append_next
= 1; /* append tab after current tab */
577 char *search_string
= NULL
;
578 char *http_proxy
= NULL
;
579 char download_dir
[PATH_MAX
];
580 char runtime_settings
[PATH_MAX
]; /* override of settings */
581 int allow_volatile_cookies
= 0;
582 int save_global_history
= 0; /* save global history to disk */
583 char *user_agent
= NULL
;
584 int save_rejected_cookies
= 0;
585 int session_autosave
= 0;
586 int guess_search
= 0;
587 int dns_prefetch
= FALSE
;
588 gint max_connections
= 25;
589 gint max_host_connections
= 5;
590 gint enable_spell_checking
= 0;
591 char *spell_check_languages
= NULL
;
592 int xterm_workaround
= 0;
593 char *url_regex
= NULL
;
594 int history_autosave
= 0;
595 char search_file
[PATH_MAX
];
596 char command_file
[PATH_MAX
];
598 char *cmd_font_name
= NULL
;
599 char *oops_font_name
= NULL
;
600 char *statusbar_font_name
= NULL
;
601 char *tabbar_font_name
= NULL
;
602 PangoFontDescription
*cmd_font
;
603 PangoFontDescription
*oops_font
;
604 PangoFontDescription
*statusbar_font
;
605 PangoFontDescription
*tabbar_font
;
606 char *qmarks
[XT_NOMARKS
];
608 int btn_down
; /* M1 down in any wv */
609 regex_t url_re
; /* guess_search regex */
613 int set_browser_mode(struct settings
*, char *);
614 int set_cookie_policy(struct settings
*, char *);
615 int set_download_dir(struct settings
*, char *);
616 int set_default_script(struct settings
*, char *);
617 int set_runtime_dir(struct settings
*, char *);
618 int set_tab_style(struct settings
*, char *);
619 int set_work_dir(struct settings
*, char *);
620 int add_alias(struct settings
*, char *);
621 int add_mime_type(struct settings
*, char *);
622 int add_cookie_wl(struct settings
*, char *);
623 int add_js_wl(struct settings
*, char *);
624 int add_kb(struct settings
*, char *);
625 void button_set_stockid(GtkWidget
*, char *);
626 GtkWidget
* create_button(char *, char *, int);
628 char *get_browser_mode(struct settings
*);
629 char *get_cookie_policy(struct settings
*);
630 char *get_download_dir(struct settings
*);
631 char *get_default_script(struct settings
*);
632 char *get_runtime_dir(struct settings
*);
633 char *get_tab_style(struct settings
*);
634 char *get_work_dir(struct settings
*);
635 void startpage_add(const char *, ...);
637 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
638 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
639 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
640 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
641 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
643 void recalc_tabs(void);
644 void recolor_compact_tabs(void);
645 void set_current_tab(int page_num
);
646 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
647 void marks_clear(struct tab
*t
);
649 int set_http_proxy(char *);
652 int (*set
)(struct settings
*, char *);
653 char *(*get
)(struct settings
*);
654 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
657 struct special s_browser_mode
= {
663 struct special s_cookie
= {
669 struct special s_alias
= {
675 struct special s_mime
= {
681 struct special s_js
= {
687 struct special s_kb
= {
693 struct special s_cookie_wl
= {
699 struct special s_default_script
= {
705 struct special s_download_dir
= {
711 struct special s_work_dir
= {
717 struct special s_tab_style
= {
726 #define XT_S_INVALID (0)
729 #define XT_S_FLOAT (3)
731 #define XT_SF_RESTART (1<<0)
732 #define XT_SF_RUNTIME (1<<1)
737 int (*activate
)(char *);
739 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
740 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
741 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
742 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
743 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
744 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
745 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
746 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
747 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
748 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
749 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
750 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
751 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
752 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
753 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
754 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
755 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
756 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
757 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
758 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
759 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
760 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
761 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
762 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
763 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
764 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
765 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
766 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
767 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
768 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
769 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
770 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
771 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
772 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
773 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
774 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
775 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
776 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
777 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
778 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
779 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
780 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
781 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
782 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
783 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
784 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
785 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
788 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
789 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
790 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
791 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
793 /* runtime settings */
794 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
795 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
796 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
797 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
798 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
801 int about(struct tab
*, struct karg
*);
802 int blank(struct tab
*, struct karg
*);
803 int ca_cmd(struct tab
*, struct karg
*);
804 int cookie_show_wl(struct tab
*, struct karg
*);
805 int js_show_wl(struct tab
*, struct karg
*);
806 int help(struct tab
*, struct karg
*);
807 int set(struct tab
*, struct karg
*);
808 int stats(struct tab
*, struct karg
*);
809 int marco(struct tab
*, struct karg
*);
810 int startpage(struct tab
*, struct karg
*);
811 const char * marco_message(int *);
812 int xtp_page_cl(struct tab
*, struct karg
*);
813 int xtp_page_dl(struct tab
*, struct karg
*);
814 int xtp_page_fl(struct tab
*, struct karg
*);
815 int xtp_page_hl(struct tab
*, struct karg
*);
816 void xt_icon_from_file(struct tab
*, char *);
817 const gchar
*get_uri(struct tab
*);
818 const gchar
*get_title(struct tab
*, bool);
820 #define XT_URI_ABOUT ("about:")
821 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
822 #define XT_URI_ABOUT_ABOUT ("about")
823 #define XT_URI_ABOUT_BLANK ("blank")
824 #define XT_URI_ABOUT_CERTS ("certs")
825 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
826 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
827 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
828 #define XT_URI_ABOUT_FAVORITES ("favorites")
829 #define XT_URI_ABOUT_HELP ("help")
830 #define XT_URI_ABOUT_HISTORY ("history")
831 #define XT_URI_ABOUT_JSWL ("jswl")
832 #define XT_URI_ABOUT_SET ("set")
833 #define XT_URI_ABOUT_STATS ("stats")
834 #define XT_URI_ABOUT_MARCO ("marco")
835 #define XT_URI_ABOUT_STARTPAGE ("startpage")
839 int (*func
)(struct tab
*, struct karg
*);
841 { XT_URI_ABOUT_ABOUT
, about
},
842 { XT_URI_ABOUT_BLANK
, blank
},
843 { XT_URI_ABOUT_CERTS
, ca_cmd
},
844 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
845 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
846 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
847 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
848 { XT_URI_ABOUT_HELP
, help
},
849 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
850 { XT_URI_ABOUT_JSWL
, js_show_wl
},
851 { XT_URI_ABOUT_SET
, set
},
852 { XT_URI_ABOUT_STATS
, stats
},
853 { XT_URI_ABOUT_MARCO
, marco
},
854 { XT_URI_ABOUT_STARTPAGE
, startpage
},
857 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
858 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
859 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
860 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
861 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
862 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
863 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
866 extern char *__progname
;
869 GtkWidget
*main_window
;
870 GtkNotebook
*notebook
;
872 GtkWidget
*arrow
, *abtn
;
873 struct tab_list tabs
;
874 struct history_list hl
;
875 struct session_list sessions
;
876 struct download_list downloads
;
877 struct domain_list c_wl
;
878 struct domain_list js_wl
;
879 struct undo_tailq undos
;
880 struct keybinding_list kbl
;
882 struct command_list chl
;
883 struct command_list shl
;
884 struct command_entry
*history_at
;
885 struct command_entry
*search_at
;
887 int updating_dl_tabs
= 0;
888 int updating_hl_tabs
= 0;
889 int updating_cl_tabs
= 0;
890 int updating_fl_tabs
= 0;
891 int cmd_history_count
= 0;
892 int search_history_count
= 0;
894 uint64_t blocked_cookies
= 0;
895 char named_session
[PATH_MAX
];
896 GtkListStore
*completion_model
;
897 GtkListStore
*buffers_store
;
899 void xxx_dir(char *);
900 int icon_size_map(int);
901 void completion_add(struct tab
*);
902 void completion_add_uri(const gchar
*);
903 void show_oops(struct tab
*, const char *, ...);
906 history_delete(struct command_list
*l
, int *counter
)
908 struct command_entry
*c
;
910 if (l
== NULL
|| counter
== NULL
)
913 c
= TAILQ_LAST(l
, command_list
);
917 TAILQ_REMOVE(l
, c
, entry
);
924 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
926 struct command_entry
*c
;
929 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
932 /* don't add the same line */
933 c
= TAILQ_FIRST(list
);
935 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
938 c
= g_malloc0(sizeof *c
);
939 c
->line
= g_strdup_printf(" %s", l
);
942 TAILQ_INSERT_HEAD(list
, c
, entry
);
945 history_delete(list
, counter
);
947 if (history_autosave
&& file
) {
948 f
= fopen(file
, "w");
950 show_oops(NULL
, "couldn't write history %s", file
);
954 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
956 fprintf(f
, "%s\n", c
->line
);
964 history_read(struct command_list
*list
, char *file
, int *counter
)
967 char *s
, line
[65536];
969 if (list
== NULL
|| file
== NULL
)
972 f
= fopen(file
, "r");
974 startpage_add("couldn't open history file %s", file
);
979 s
= fgets(line
, sizeof line
, f
);
980 if (s
== NULL
|| feof(f
) || ferror(f
))
982 if ((s
= strchr(line
, '\n')) == NULL
) {
983 startpage_add("invalid history file %s", file
);
989 history_add(list
, NULL
, line
+ 1, counter
);
997 /* marks and quickmarks array storage.
998 * first a-z, then A-Z, then 0-9 */
1005 if (i
>= 0 && i
<= 'z' - 'a')
1009 if (i
>= 0 && i
<= 'Z' - 'A')
1024 if (m
>= 'a' && m
<= 'z')
1025 return ret
+ m
- 'a';
1027 ret
+= 'z' - 'a' + 1;
1028 if (m
>= 'A' && m
<= 'Z')
1029 return ret
+ m
- 'A';
1031 ret
+= 'Z' - 'A' + 1;
1032 if (m
>= '0' && m
<= '9')
1033 return ret
+ m
- '0';
1042 int saved_errno
, status
;
1045 saved_errno
= errno
;
1047 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1051 if (errno
!= ECHILD
) {
1053 clog_warn("sigchild: waitpid:");
1059 if (WIFEXITED(status
)) {
1060 if (WEXITSTATUS(status
) != 0) {
1062 clog_warnx("sigchild: child exit status: %d",
1063 WEXITSTATUS(status));
1068 clog_warnx("sigchild: child is terminated abnormally");
1073 errno
= saved_errno
;
1077 is_g_object_setting(GObject
*o
, char *str
)
1079 guint n_props
= 0, i
;
1080 GParamSpec
**proplist
;
1082 if (! G_IS_OBJECT(o
))
1085 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1088 for (i
=0; i
< n_props
; i
++) {
1089 if (! strcmp(proplist
[i
]->name
, str
))
1096 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1100 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1102 "<title>%s</title>\n"
1111 addstyles
? XT_PAGE_STYLE
: "",
1120 * Display a web page from a HTML string in memory, rather than from a URL
1123 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1125 char file
[PATH_MAX
];
1128 /* we set this to indicate we want to manually do navaction */
1130 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1132 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1134 /* set t->xtp_meaning */
1135 for (i
= 0; i
< LENGTH(about_list
); i
++)
1136 if (!strcmp(title
, about_list
[i
].name
)) {
1141 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1142 #if GTK_CHECK_VERSION(2, 20, 0)
1143 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1144 gtk_widget_hide(t
->spinner
);
1146 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1147 xt_icon_from_file(t
, file
);
1152 get_current_tab(void)
1156 TAILQ_FOREACH(t
, &tabs
, entry
) {
1157 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1161 warnx("%s: no current tab", __func__
);
1167 set_status(struct tab
*t
, gchar
*s
, int status
)
1175 case XT_STATUS_LOADING
:
1176 type
= g_strdup_printf("Loading: %s", s
);
1179 case XT_STATUS_LINK
:
1180 type
= g_strdup_printf("Link: %s", s
);
1182 t
->status
= g_strdup(gtk_entry_get_text(
1183 GTK_ENTRY(t
->sbe
.statusbar
)));
1187 type
= g_strdup_printf("%s", s
);
1189 t
->status
= g_strdup(type
);
1193 t
->status
= g_strdup(s
);
1195 case XT_STATUS_NOTHING
:
1200 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1206 hide_cmd(struct tab
*t
)
1208 history_at
= NULL
; /* just in case */
1209 search_at
= NULL
; /* just in case */
1210 gtk_widget_hide(t
->cmd
);
1214 show_cmd(struct tab
*t
)
1218 gtk_widget_hide(t
->oops
);
1219 gtk_widget_show(t
->cmd
);
1223 hide_buffers(struct tab
*t
)
1225 gtk_widget_hide(t
->buffers
);
1226 gtk_list_store_clear(buffers_store
);
1236 sort_tabs_by_page_num(struct tab
***stabs
)
1241 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1243 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1245 TAILQ_FOREACH(t
, &tabs
, entry
)
1246 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1252 buffers_make_list(void)
1255 const gchar
*title
= NULL
;
1257 struct tab
**stabs
= NULL
;
1259 num_tabs
= sort_tabs_by_page_num(&stabs
);
1261 for (i
= 0; i
< num_tabs
; i
++)
1263 gtk_list_store_append(buffers_store
, &iter
);
1264 title
= get_title(stabs
[i
], FALSE
);
1265 gtk_list_store_set(buffers_store
, &iter
,
1266 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1276 show_buffers(struct tab
*t
)
1278 buffers_make_list();
1279 gtk_widget_show(t
->buffers
);
1280 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1284 toggle_buffers(struct tab
*t
)
1286 if (gtk_widget_get_visible(t
->buffers
))
1293 buffers(struct tab
*t
, struct karg
*args
)
1301 hide_oops(struct tab
*t
)
1303 gtk_widget_hide(t
->oops
);
1307 show_oops(struct tab
*at
, const char *fmt
, ...)
1311 struct tab
*t
= NULL
;
1317 if ((t
= get_current_tab()) == NULL
)
1323 if (vasprintf(&msg
, fmt
, ap
) == -1)
1324 errx(1, "show_oops failed");
1327 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1328 gtk_widget_hide(t
->cmd
);
1329 gtk_widget_show(t
->oops
);
1336 get_as_string(struct settings
*s
)
1347 warnx("get_as_string skip %s\n", s
->name
);
1348 } else if (s
->type
== XT_S_INT
)
1349 r
= g_strdup_printf("%d", *s
->ival
);
1350 else if (s
->type
== XT_S_STR
)
1351 r
= g_strdup(*s
->sval
);
1352 else if (s
->type
== XT_S_FLOAT
)
1353 r
= g_strdup_printf("%f", *s
->fval
);
1355 r
= g_strdup_printf("INVALID TYPE");
1361 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1366 for (i
= 0; i
< LENGTH(rs
); i
++) {
1367 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1368 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1370 s
= get_as_string(&rs
[i
]);
1371 cb(&rs
[i
], s
, cb_args
);
1378 set_browser_mode(struct settings
*s
, char *val
)
1380 if (!strcmp(val
, "whitelist")) {
1381 browser_mode
= XT_BM_WHITELIST
;
1382 allow_volatile_cookies
= 0;
1383 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1384 cookies_enabled
= 1;
1385 enable_cookie_whitelist
= 1;
1386 read_only_cookies
= 0;
1387 save_rejected_cookies
= 0;
1388 session_timeout
= 3600;
1390 enable_js_whitelist
= 1;
1391 enable_localstorage
= 0;
1392 } else if (!strcmp(val
, "normal")) {
1393 browser_mode
= XT_BM_NORMAL
;
1394 allow_volatile_cookies
= 0;
1395 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1396 cookies_enabled
= 1;
1397 enable_cookie_whitelist
= 0;
1398 read_only_cookies
= 0;
1399 save_rejected_cookies
= 0;
1400 session_timeout
= 3600;
1402 enable_js_whitelist
= 0;
1403 enable_localstorage
= 1;
1404 } else if (!strcmp(val
, "kiosk")) {
1405 browser_mode
= XT_BM_KIOSK
;
1406 allow_volatile_cookies
= 0;
1407 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1408 cookies_enabled
= 1;
1409 enable_cookie_whitelist
= 0;
1410 read_only_cookies
= 0;
1411 save_rejected_cookies
= 0;
1412 session_timeout
= 3600;
1414 enable_js_whitelist
= 0;
1415 enable_localstorage
= 1;
1425 get_browser_mode(struct settings
*s
)
1429 if (browser_mode
== XT_BM_WHITELIST
)
1430 r
= g_strdup("whitelist");
1431 else if (browser_mode
== XT_BM_NORMAL
)
1432 r
= g_strdup("normal");
1433 else if (browser_mode
== XT_BM_KIOSK
)
1434 r
= g_strdup("kiosk");
1442 set_cookie_policy(struct settings
*s
, char *val
)
1444 if (!strcmp(val
, "no3rdparty"))
1445 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1446 else if (!strcmp(val
, "accept"))
1447 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1448 else if (!strcmp(val
, "reject"))
1449 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1457 get_cookie_policy(struct settings
*s
)
1461 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1462 r
= g_strdup("no3rdparty");
1463 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1464 r
= g_strdup("accept");
1465 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1466 r
= g_strdup("reject");
1474 get_default_script(struct settings
*s
)
1476 if (default_script
[0] == '\0')
1478 return (g_strdup(default_script
));
1482 set_default_script(struct settings
*s
, char *val
)
1485 snprintf(default_script
, sizeof default_script
, "%s/%s",
1486 pwd
->pw_dir
, &val
[1]);
1488 strlcpy(default_script
, val
, sizeof default_script
);
1494 get_download_dir(struct settings
*s
)
1496 if (download_dir
[0] == '\0')
1498 return (g_strdup(download_dir
));
1502 set_download_dir(struct settings
*s
, char *val
)
1505 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1506 pwd
->pw_dir
, &val
[1]);
1508 strlcpy(download_dir
, val
, sizeof download_dir
);
1515 * We use these to prevent people putting xxxt:// URLs on
1516 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1518 #define XT_XTP_SES_KEY_SZ 8
1519 #define XT_XTP_SES_KEY_HEX_FMT \
1520 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1521 char *dl_session_key
; /* downloads */
1522 char *hl_session_key
; /* history list */
1523 char *cl_session_key
; /* cookie list */
1524 char *fl_session_key
; /* favorites list */
1526 char work_dir
[PATH_MAX
];
1527 char certs_dir
[PATH_MAX
];
1528 char cache_dir
[PATH_MAX
];
1529 char sessions_dir
[PATH_MAX
];
1530 char cookie_file
[PATH_MAX
];
1531 SoupURI
*proxy_uri
= NULL
;
1532 SoupSession
*session
;
1533 SoupCookieJar
*s_cookiejar
;
1534 SoupCookieJar
*p_cookiejar
;
1535 char rc_fname
[PATH_MAX
];
1537 struct mime_type_list mtl
;
1538 struct alias_list aliases
;
1541 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1542 void delete_tab(struct tab
*);
1543 void setzoom_webkit(struct tab
*, int);
1544 int run_script(struct tab
*, char *);
1545 int download_rb_cmp(struct download
*, struct download
*);
1546 gboolean
cmd_execute(struct tab
*t
, char *str
);
1549 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1551 return (strcmp(h1
->uri
, h2
->uri
));
1553 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1556 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1558 return (strcmp(d1
->d
, d2
->d
));
1560 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1563 get_work_dir(struct settings
*s
)
1565 if (work_dir
[0] == '\0')
1567 return (g_strdup(work_dir
));
1571 set_work_dir(struct settings
*s
, char *val
)
1574 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1575 pwd
->pw_dir
, &val
[1]);
1577 strlcpy(work_dir
, val
, sizeof work_dir
);
1583 get_tab_style(struct settings
*s
)
1585 if (tab_style
== XT_TABS_NORMAL
)
1586 return (g_strdup("normal"));
1588 return (g_strdup("compact"));
1592 set_tab_style(struct settings
*s
, char *val
)
1594 if (!strcmp(val
, "normal"))
1595 tab_style
= XT_TABS_NORMAL
;
1596 else if (!strcmp(val
, "compact"))
1597 tab_style
= XT_TABS_COMPACT
;
1605 * generate a session key to secure xtp commands.
1606 * pass in a ptr to the key in question and it will
1607 * be modified in place.
1610 generate_xtp_session_key(char **key
)
1612 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1618 /* make a new one */
1619 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1620 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1621 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1622 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1624 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1628 * validate a xtp session key.
1632 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1634 if (strcmp(trusted
, untrusted
) != 0) {
1635 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1644 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1646 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1648 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1650 struct valid_url_types
{
1661 valid_url_type(char *url
)
1665 for (i
= 0; i
< LENGTH(vut
); i
++)
1666 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1673 print_cookie(char *msg
, SoupCookie
*c
)
1679 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1680 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1681 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1682 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1683 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1684 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1685 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1686 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1687 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1688 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1692 walk_alias(struct settings
*s
,
1693 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1698 if (s
== NULL
|| cb
== NULL
) {
1699 show_oops(NULL
, "walk_alias invalid parameters");
1703 TAILQ_FOREACH(a
, &aliases
, entry
) {
1704 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1705 cb(s
, str
, cb_args
);
1711 match_alias(char *url_in
)
1715 char *url_out
= NULL
, *search
, *enc_arg
;
1717 search
= g_strdup(url_in
);
1719 if (strsep(&arg
, " \t") == NULL
) {
1720 show_oops(NULL
, "match_alias: NULL URL");
1724 TAILQ_FOREACH(a
, &aliases
, entry
) {
1725 if (!strcmp(search
, a
->a_name
))
1730 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1733 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1734 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1737 url_out
= g_strdup_printf(a
->a_uri
, "");
1745 guess_url_type(char *url_in
)
1748 char *url_out
= NULL
, *enc_search
= NULL
;
1751 /* substitute aliases */
1752 url_out
= match_alias(url_in
);
1753 if (url_out
!= NULL
)
1756 /* see if we are an about page */
1757 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1758 for (i
= 0; i
< LENGTH(about_list
); i
++)
1759 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1760 about_list
[i
].name
)) {
1761 url_out
= g_strdup(url_in
);
1765 if (guess_search
&& url_regex
&&
1766 !(g_str_has_prefix(url_in
, "http://") ||
1767 g_str_has_prefix(url_in
, "https://"))) {
1768 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1769 /* invalid URI so search instead */
1770 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1771 url_out
= g_strdup_printf(search_string
, enc_search
);
1777 /* XXX not sure about this heuristic */
1778 if (stat(url_in
, &sb
) == 0)
1779 url_out
= g_strdup_printf("file://%s", url_in
);
1781 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1783 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1789 load_uri(struct tab
*t
, gchar
*uri
)
1792 gchar
*newuri
= NULL
;
1798 /* Strip leading spaces. */
1799 while (*uri
&& isspace(*uri
))
1802 if (strlen(uri
) == 0) {
1807 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1809 if (valid_url_type(uri
)) {
1810 newuri
= guess_url_type(uri
);
1814 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1815 for (i
= 0; i
< LENGTH(about_list
); i
++)
1816 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1817 bzero(&args
, sizeof args
);
1818 about_list
[i
].func(t
, &args
);
1819 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1823 show_oops(t
, "invalid about page");
1827 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1829 webkit_web_view_load_uri(t
->wv
, uri
);
1836 get_uri(struct tab
*t
)
1838 const gchar
*uri
= NULL
;
1840 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1842 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1843 uri
= webkit_web_view_get_uri(t
->wv
);
1845 /* use tmp_uri to make sure it is g_freed */
1848 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1849 about_list
[t
->xtp_meaning
].name
);
1856 get_title(struct tab
*t
, bool window
)
1858 const gchar
*set
= NULL
, *title
= NULL
;
1859 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1861 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1862 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1865 title
= webkit_web_view_get_title(t
->wv
);
1866 if ((set
= title
? title
: get_uri(t
)))
1870 set
= window
? XT_NAME
: "(untitled)";
1876 add_alias(struct settings
*s
, char *line
)
1879 struct alias
*a
= NULL
;
1881 if (s
== NULL
|| line
== NULL
) {
1882 show_oops(NULL
, "add_alias invalid parameters");
1887 a
= g_malloc(sizeof(*a
));
1889 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1890 show_oops(NULL
, "add_alias: incomplete alias definition");
1893 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1894 show_oops(NULL
, "add_alias: invalid alias definition");
1898 a
->a_name
= g_strdup(alias
);
1899 a
->a_uri
= g_strdup(l
);
1901 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1903 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1913 add_mime_type(struct settings
*s
, char *line
)
1917 struct mime_type
*m
= NULL
;
1918 int downloadfirst
= 0;
1920 /* XXX this could be smarter */
1922 if (line
== NULL
|| strlen(line
) == 0) {
1923 show_oops(NULL
, "add_mime_type invalid parameters");
1932 m
= g_malloc(sizeof(*m
));
1934 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1935 show_oops(NULL
, "add_mime_type: invalid mime_type");
1938 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1939 mime_type
[strlen(mime_type
) - 1] = '\0';
1944 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1945 show_oops(NULL
, "add_mime_type: invalid mime_type");
1949 m
->mt_type
= g_strdup(mime_type
);
1950 m
->mt_action
= g_strdup(l
);
1951 m
->mt_download
= downloadfirst
;
1953 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1954 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1956 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1966 find_mime_type(char *mime_type
)
1968 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1970 TAILQ_FOREACH(m
, &mtl
, entry
) {
1971 if (m
->mt_default
&&
1972 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1975 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1988 walk_mime_type(struct settings
*s
,
1989 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1991 struct mime_type
*m
;
1994 if (s
== NULL
|| cb
== NULL
) {
1995 show_oops(NULL
, "walk_mime_type invalid parameters");
1999 TAILQ_FOREACH(m
, &mtl
, entry
) {
2000 str
= g_strdup_printf("%s%s --> %s",
2002 m
->mt_default
? "*" : "",
2004 cb(s
, str
, cb_args
);
2010 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2016 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2019 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2021 /* treat *.moo.com the same as .moo.com */
2022 if (str
[0] == '*' && str
[1] == '.')
2024 else if (str
[0] == '.')
2029 /* slice off port number */
2030 p
= g_strrstr(str
, ":");
2034 d
= g_malloc(sizeof *d
);
2036 d
->d
= g_strdup_printf(".%s", str
);
2038 d
->d
= g_strdup(str
);
2041 if (RB_INSERT(domain_list
, wl
, d
))
2044 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2055 add_cookie_wl(struct settings
*s
, char *entry
)
2057 wl_add(entry
, &c_wl
, 1);
2062 walk_cookie_wl(struct settings
*s
,
2063 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2067 if (s
== NULL
|| cb
== NULL
) {
2068 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2072 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2073 cb(s
, d
->d
, cb_args
);
2077 walk_js_wl(struct settings
*s
,
2078 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2082 if (s
== NULL
|| cb
== NULL
) {
2083 show_oops(NULL
, "walk_js_wl invalid parameters");
2087 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2088 cb(s
, d
->d
, cb_args
);
2092 add_js_wl(struct settings
*s
, char *entry
)
2094 wl_add(entry
, &js_wl
, 1 /* persistent */);
2099 wl_find(const gchar
*search
, struct domain_list
*wl
)
2102 struct domain
*d
= NULL
, dfind
;
2105 if (search
== NULL
|| wl
== NULL
)
2107 if (strlen(search
) < 2)
2110 if (search
[0] != '.')
2111 s
= g_strdup_printf(".%s", search
);
2113 s
= g_strdup(search
);
2115 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2118 d
= RB_FIND(domain_list
, wl
, &dfind
);
2132 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2138 if (s
== NULL
|| wl
== NULL
)
2141 if (!strncmp(s
, "http://", strlen("http://")))
2142 s
= &s
[strlen("http://")];
2143 else if (!strncmp(s
, "https://", strlen("https://")))
2144 s
= &s
[strlen("https://")];
2149 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2150 /* chop string at first slash */
2151 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2154 r
= wl_find(ss
, wl
);
2163 settings_add(char *var
, char *val
)
2170 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2171 if (strcmp(var
, rs
[i
].name
))
2175 if (rs
[i
].s
->set(&rs
[i
], val
))
2176 errx(1, "invalid value for %s: %s", var
, val
);
2180 switch (rs
[i
].type
) {
2189 errx(1, "invalid sval for %s",
2203 errx(1, "invalid type for %s", var
);
2212 config_parse(char *filename
, int runtime
)
2215 char *line
, *cp
, *var
, *val
;
2216 size_t len
, lineno
= 0;
2218 char file
[PATH_MAX
];
2221 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2223 if (filename
== NULL
)
2226 if (runtime
&& runtime_settings
[0] != '\0') {
2227 snprintf(file
, sizeof file
, "%s/%s",
2228 work_dir
, runtime_settings
);
2229 if (stat(file
, &sb
)) {
2230 warnx("runtime file doesn't exist, creating it");
2231 if ((f
= fopen(file
, "w")) == NULL
)
2233 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2237 strlcpy(file
, filename
, sizeof file
);
2239 if ((config
= fopen(file
, "r")) == NULL
) {
2240 warn("config_parse: cannot open %s", filename
);
2245 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2246 if (feof(config
) || ferror(config
))
2250 cp
+= (long)strspn(cp
, WS
);
2251 if (cp
[0] == '\0') {
2257 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2258 startpage_add("invalid configuration file entry: %s",
2261 cp
+= (long)strspn(cp
, WS
);
2263 if ((val
= strsep(&cp
, "\0")) == NULL
)
2266 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2267 handled
= settings_add(var
, val
);
2269 startpage_add("invalid configuration file entry: %s=%s",
2279 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2285 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2289 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2292 JSStringGetUTF8CString(jsref
, s
, l
);
2293 JSStringRelease(jsref
);
2299 disable_hints(struct tab
*t
)
2301 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2302 bzero(t
->hint_num
, sizeof t
->hint_num
);
2303 run_script(t
, "vimprobable_clear()");
2305 t
->hint_mode
= XT_HINT_NONE
;
2309 enable_hints(struct tab
*t
)
2311 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2312 run_script(t
, "vimprobable_show_hints()");
2314 t
->hint_mode
= XT_HINT_NONE
;
2317 #define XT_JS_OPEN ("open;")
2318 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2319 #define XT_JS_FIRE ("fire;")
2320 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2321 #define XT_JS_FOUND ("found;")
2322 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2325 run_script(struct tab
*t
, char *s
)
2327 JSGlobalContextRef ctx
;
2328 WebKitWebFrame
*frame
;
2330 JSValueRef val
, exception
;
2333 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2334 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2336 frame
= webkit_web_view_get_main_frame(t
->wv
);
2337 ctx
= webkit_web_frame_get_global_context(frame
);
2339 str
= JSStringCreateWithUTF8CString(s
);
2340 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2341 NULL
, 0, &exception
);
2342 JSStringRelease(str
);
2344 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2346 es
= js_ref_to_string(ctx
, exception
);
2347 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2351 es
= js_ref_to_string(ctx
, val
);
2352 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2354 /* handle return value right here */
2355 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2358 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2361 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2362 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2363 &es
[XT_JS_FIRE_LEN
]);
2368 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2369 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2380 hint(struct tab
*t
, struct karg
*args
)
2383 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2385 if (t
->hints_on
== 0)
2394 apply_style(struct tab
*t
)
2396 g_object_set(G_OBJECT(t
->settings
),
2397 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2401 userstyle(struct tab
*t
, struct karg
*args
)
2403 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2407 g_object_set(G_OBJECT(t
->settings
),
2408 "user-stylesheet-uri", NULL
, (char *)NULL
);
2417 * Doesn't work fully, due to the following bug:
2418 * https://bugs.webkit.org/show_bug.cgi?id=51747
2421 restore_global_history(void)
2423 char file
[PATH_MAX
];
2428 const char delim
[3] = {'\\', '\\', '\0'};
2430 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2432 if ((f
= fopen(file
, "r")) == NULL
) {
2433 warnx("%s: fopen", __func__
);
2438 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2439 if (feof(f
) || ferror(f
))
2442 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2443 if (feof(f
) || ferror(f
)) {
2445 warnx("%s: broken history file\n", __func__
);
2449 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2450 webkit_web_history_item_new_with_data(uri
, title
);
2451 h
= g_malloc(sizeof(struct history
));
2452 h
->uri
= g_strdup(uri
);
2453 h
->title
= g_strdup(title
);
2454 RB_INSERT(history_list
, &hl
, h
);
2455 completion_add_uri(h
->uri
);
2457 warnx("%s: failed to restore history\n", __func__
);
2473 save_global_history_to_disk(struct tab
*t
)
2475 char file
[PATH_MAX
];
2479 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2481 if ((f
= fopen(file
, "w")) == NULL
) {
2482 show_oops(t
, "%s: global history file: %s",
2483 __func__
, strerror(errno
));
2487 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2488 if (h
->uri
&& h
->title
)
2489 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2498 quit(struct tab
*t
, struct karg
*args
)
2500 if (save_global_history
)
2501 save_global_history_to_disk(t
);
2509 restore_sessions_list(void)
2512 struct dirent
*dp
= NULL
;
2515 sdir
= opendir(sessions_dir
);
2517 while ((dp
= readdir(sdir
)) != NULL
)
2518 if (dp
->d_type
== DT_REG
) {
2519 s
= g_malloc(sizeof(struct session
));
2520 s
->name
= g_strdup(dp
->d_name
);
2521 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2528 open_tabs(struct tab
*t
, struct karg
*a
)
2530 char file
[PATH_MAX
];
2534 struct tab
*ti
, *tt
;
2539 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2540 if ((f
= fopen(file
, "r")) == NULL
)
2543 ti
= TAILQ_LAST(&tabs
, tab_list
);
2546 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2547 if (feof(f
) || ferror(f
))
2550 /* retrieve session name */
2551 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2552 strlcpy(named_session
,
2553 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2554 sizeof named_session
);
2558 if (uri
&& strlen(uri
))
2559 create_new_tab(uri
, NULL
, 1, -1);
2565 /* close open tabs */
2566 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2568 tt
= TAILQ_FIRST(&tabs
);
2588 restore_saved_tabs(void)
2590 char file
[PATH_MAX
];
2591 int unlink_file
= 0;
2596 snprintf(file
, sizeof file
, "%s/%s",
2597 sessions_dir
, XT_RESTART_TABS_FILE
);
2598 if (stat(file
, &sb
) == -1)
2599 a
.s
= XT_SAVED_TABS_FILE
;
2602 a
.s
= XT_RESTART_TABS_FILE
;
2605 a
.i
= XT_SES_DONOTHING
;
2606 rv
= open_tabs(NULL
, &a
);
2615 save_tabs(struct tab
*t
, struct karg
*a
)
2617 char file
[PATH_MAX
];
2619 int num_tabs
= 0, i
;
2620 struct tab
**stabs
= NULL
;
2625 snprintf(file
, sizeof file
, "%s/%s",
2626 sessions_dir
, named_session
);
2628 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2630 if ((f
= fopen(file
, "w")) == NULL
) {
2631 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2635 /* save session name */
2636 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2638 /* Save tabs, in the order they are arranged in the notebook. */
2639 num_tabs
= sort_tabs_by_page_num(&stabs
);
2641 for (i
= 0; i
< num_tabs
; i
++)
2643 if (get_uri(stabs
[i
]) != NULL
)
2644 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2645 else if (gtk_entry_get_text(GTK_ENTRY(
2646 stabs
[i
]->uri_entry
)))
2647 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2648 stabs
[i
]->uri_entry
)));
2653 /* try and make sure this gets to disk NOW. XXX Backup first? */
2654 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2655 show_oops(t
, "May not have managed to save session: %s",
2665 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2677 run_page_script(struct tab
*t
, struct karg
*args
)
2680 char *tmp
, script
[PATH_MAX
];
2682 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2683 if (tmp
[0] == '\0') {
2684 show_oops(t
, "no script specified");
2688 if ((uri
= get_uri(t
)) == NULL
) {
2689 show_oops(t
, "tab is empty, not running script");
2694 snprintf(script
, sizeof script
, "%s/%s",
2695 pwd
->pw_dir
, &tmp
[1]);
2697 strlcpy(script
, tmp
, sizeof script
);
2701 show_oops(t
, "can't fork to run script");
2711 execlp(script
, script
, uri
, (void *)NULL
);
2721 yank_uri(struct tab
*t
, struct karg
*args
)
2724 GtkClipboard
*clipboard
;
2726 if ((uri
= get_uri(t
)) == NULL
)
2729 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2730 gtk_clipboard_set_text(clipboard
, uri
, -1);
2736 paste_uri(struct tab
*t
, struct karg
*args
)
2738 GtkClipboard
*clipboard
;
2739 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2741 gchar
*p
= NULL
, *uri
;
2743 /* try primary clipboard first */
2744 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2745 p
= gtk_clipboard_wait_for_text(clipboard
);
2747 /* if it failed get whatever text is in cut_buffer0 */
2748 if (p
== NULL
&& xterm_workaround
)
2749 if (gdk_property_get(gdk_get_default_root_window(),
2751 gdk_atom_intern("STRING", FALSE
),
2753 1024 * 1024 /* picked out of my butt */,
2759 /* yes sir, we need to NUL the string */
2765 while (*uri
&& isspace(*uri
))
2767 if (strlen(uri
) == 0) {
2768 show_oops(t
, "empty paste buffer");
2771 if (guess_search
== 0 && valid_url_type(uri
)) {
2772 /* we can be clever and paste this in search box */
2773 show_oops(t
, "not a valid URL");
2777 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2779 else if (args
->i
== XT_PASTE_NEW_TAB
)
2780 create_new_tab(uri
, NULL
, 1, -1);
2791 find_domain(const gchar
*s
, int toplevel
)
2799 uri
= soup_uri_new(s
);
2801 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2805 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2806 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2807 while(--p
>= uri
->host
&& *p
!= '.');
2814 ret
= g_strdup_printf(".%s", p
);
2822 toggle_cwl(struct tab
*t
, struct karg
*args
)
2833 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2835 if (uri
== NULL
|| dom
== NULL
||
2836 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2837 show_oops(t
, "Can't toggle domain in cookie white list");
2840 d
= wl_find(dom
, &c_wl
);
2847 if (args
->i
& XT_WL_TOGGLE
)
2849 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2851 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2855 /* enable cookies for domain */
2856 wl_add(dom
, &c_wl
, 0);
2858 /* disable cookies for domain */
2859 RB_REMOVE(domain_list
, &c_wl
, d
);
2861 if (args
->i
& XT_WL_RELOAD
)
2862 webkit_web_view_reload(t
->wv
);
2870 toggle_js(struct tab
*t
, struct karg
*args
)
2880 g_object_get(G_OBJECT(t
->settings
),
2881 "enable-scripts", &es
, (char *)NULL
);
2882 if (args
->i
& XT_WL_TOGGLE
)
2884 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2886 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2892 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2894 if (uri
== NULL
|| dom
== NULL
||
2895 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2896 show_oops(t
, "Can't toggle domain in JavaScript white list");
2901 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2902 wl_add(dom
, &js_wl
, 0 /* session */);
2904 d
= wl_find(dom
, &js_wl
);
2906 RB_REMOVE(domain_list
, &js_wl
, d
);
2907 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2909 g_object_set(G_OBJECT(t
->settings
),
2910 "enable-scripts", es
, (char *)NULL
);
2911 g_object_set(G_OBJECT(t
->settings
),
2912 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2913 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2915 if (args
->i
& XT_WL_RELOAD
)
2916 webkit_web_view_reload(t
->wv
);
2924 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2928 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2931 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2936 toggle_src(struct tab
*t
, struct karg
*args
)
2943 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2944 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2945 webkit_web_view_reload(t
->wv
);
2951 focus_webview(struct tab
*t
)
2956 /* only grab focus if we are visible */
2957 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2958 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2962 focus(struct tab
*t
, struct karg
*args
)
2964 if (t
== NULL
|| args
== NULL
)
2970 if (args
->i
== XT_FOCUS_URI
)
2971 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2972 else if (args
->i
== XT_FOCUS_SEARCH
)
2973 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2979 stats(struct tab
*t
, struct karg
*args
)
2981 char *page
, *body
, *s
, line
[64 * 1024];
2982 uint64_t line_count
= 0;
2986 show_oops(NULL
, "stats invalid parameters");
2989 if (save_rejected_cookies
) {
2990 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2992 s
= fgets(line
, sizeof line
, r_cookie_f
);
2993 if (s
== NULL
|| feof(r_cookie_f
) ||
2999 snprintf(line
, sizeof line
,
3000 "<br/>Cookies blocked(*) total: %llu", line_count
);
3002 show_oops(t
, "Can't open blocked cookies file: %s",
3006 body
= g_strdup_printf(
3007 "Cookies blocked(*) this session: %llu"
3009 "<p><small><b>*</b> results vary based on settings</small></p>",
3013 page
= get_html_page("Statistics", body
, "", 0);
3016 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3023 marco(struct tab
*t
, struct karg
*args
)
3025 char *page
, line
[64 * 1024];
3029 show_oops(NULL
, "marco invalid parameters");
3032 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3034 page
= get_html_page("Marco Sez...", line
, "", 0);
3036 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3043 blank(struct tab
*t
, struct karg
*args
)
3046 show_oops(NULL
, "blank invalid parameters");
3048 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3054 about(struct tab
*t
, struct karg
*args
)
3059 show_oops(NULL
, "about invalid parameters");
3061 body
= g_strdup_printf("<b>Version: %s</b><p>"
3064 "<li>Marco Peereboom <marco@peereboom.us></li>"
3065 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3066 "<li>Edd Barrett <vext01@gmail.com> </li>"
3067 "<li>Todd T. Fries <todd@fries.net> </li>"
3068 "<li>Raphael Graf <r@undefined.ch> </li>"
3070 "Copyrights and licenses can be found on the XXXTerm "
3071 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
3075 page
= get_html_page("About", body
, "", 0);
3078 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3085 help(struct tab
*t
, struct karg
*args
)
3087 char *page
, *head
, *body
;
3090 show_oops(NULL
, "help invalid parameters");
3092 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3093 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3095 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3096 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3097 "cgi-bin/man-cgi?xxxterm</a>";
3099 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3101 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3108 startpage(struct tab
*t
, struct karg
*args
)
3110 char *page
, *body
, *b
;
3114 show_oops(NULL
, "startpage invalid parameters");
3116 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3118 TAILQ_FOREACH(s
, &spl
, entry
) {
3120 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3124 page
= get_html_page("Startup Exception", body
, "", 0);
3127 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3134 startpage_add(const char *fmt
, ...)
3144 if (vasprintf(&msg
, fmt
, ap
) == -1)
3145 errx(1, "startpage_add failed");
3148 s
= g_malloc0(sizeof *s
);
3151 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3155 * update all favorite tabs apart from one. Pass NULL if
3156 * you want to update all.
3159 update_favorite_tabs(struct tab
*apart_from
)
3162 if (!updating_fl_tabs
) {
3163 updating_fl_tabs
= 1; /* stop infinite recursion */
3164 TAILQ_FOREACH(t
, &tabs
, entry
)
3165 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3166 && (t
!= apart_from
))
3167 xtp_page_fl(t
, NULL
);
3168 updating_fl_tabs
= 0;
3172 /* show a list of favorites (bookmarks) */
3174 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3176 char file
[PATH_MAX
];
3178 char *uri
= NULL
, *title
= NULL
;
3179 size_t len
, lineno
= 0;
3181 char *body
, *tmp
, *page
= NULL
;
3182 const char delim
[3] = {'\\', '\\', '\0'};
3184 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3187 warn("%s: bad param", __func__
);
3189 /* new session key */
3190 if (!updating_fl_tabs
)
3191 generate_xtp_session_key(&fl_session_key
);
3193 /* open favorites */
3194 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3195 if ((f
= fopen(file
, "r")) == NULL
) {
3196 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3201 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3202 "<th style='width: 40px'>#</th><th>Link</th>"
3203 "<th style='width: 40px'>Rm</th></tr>\n");
3206 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3207 if (feof(f
) || ferror(f
))
3209 if (strlen(title
) == 0 || title
[0] == '#') {
3215 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3216 if (feof(f
) || ferror(f
)) {
3217 show_oops(t
, "favorites file corrupt");
3223 body
= g_strdup_printf("%s<tr>"
3225 "<td><a href='%s'>%s</a></td>"
3226 "<td style='text-align: center'>"
3227 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3229 body
, i
, uri
, title
,
3230 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3242 /* if none, say so */
3245 body
= g_strdup_printf("%s<tr>"
3246 "<td colspan='3' style='text-align: center'>"
3247 "No favorites - To add one use the 'favadd' command."
3248 "</td></tr>", body
);
3253 body
= g_strdup_printf("%s</table>", body
);
3263 page
= get_html_page("Favorites", body
, "", 1);
3264 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3268 update_favorite_tabs(t
);
3277 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3278 size_t cert_count
, char *title
)
3280 gnutls_datum_t cinfo
;
3284 body
= g_strdup("");
3286 for (i
= 0; i
< cert_count
; i
++) {
3287 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3292 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3293 body
, i
, cinfo
.data
);
3294 gnutls_free(cinfo
.data
);
3298 tmp
= get_html_page(title
, body
, "", 0);
3301 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3306 ca_cmd(struct tab
*t
, struct karg
*args
)
3309 int rv
= 1, certs
= 0, certs_read
;
3312 gnutls_x509_crt_t
*c
= NULL
;
3313 char *certs_buf
= NULL
, *s
;
3315 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3316 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3320 if (fstat(fileno(f
), &sb
) == -1) {
3321 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3325 certs_buf
= g_malloc(sb
.st_size
+ 1);
3326 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3327 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3330 certs_buf
[sb
.st_size
] = '\0';
3333 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3335 s
+= strlen("BEGIN CERTIFICATE");
3338 bzero(&dt
, sizeof dt
);
3339 dt
.data
= (unsigned char *)certs_buf
;
3340 dt
.size
= sb
.st_size
;
3341 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3342 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3343 GNUTLS_X509_FMT_PEM
, 0);
3344 if (certs_read
<= 0) {
3345 show_oops(t
, "No cert(s) available");
3348 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3361 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3365 struct addrinfo hints
, *res
= NULL
, *ai
;
3366 int rv
= -1, s
= -1, on
, error
;
3369 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3370 show_oops(t
, "invalid URI");
3374 su
= soup_uri_new(uri
);
3376 show_oops(t
, "invalid soup URI");
3379 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3380 show_oops(t
, "invalid HTTPS URI");
3384 snprintf(port
, sizeof port
, "%d", su
->port
);
3385 bzero(&hints
, sizeof(struct addrinfo
));
3386 hints
.ai_flags
= AI_CANONNAME
;
3387 hints
.ai_family
= AF_UNSPEC
;
3388 hints
.ai_socktype
= SOCK_STREAM
;
3390 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3391 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3395 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3401 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3403 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3406 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3409 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3413 show_oops(t
, "could not obtain certificates from: %s",
3419 strlcpy(domain
, su
->host
, domain_sz
);
3426 if (rv
== -1 && s
!= -1)
3433 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3436 gnutls_deinit(gsession
);
3438 gnutls_certificate_free_credentials(xcred
);
3444 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3445 gnutls_certificate_credentials_t
*xc
)
3447 gnutls_certificate_credentials_t xcred
;
3448 gnutls_session_t gsession
;
3451 if (gs
== NULL
|| xc
== NULL
)
3457 gnutls_certificate_allocate_credentials(&xcred
);
3458 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3459 GNUTLS_X509_FMT_PEM
);
3461 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3462 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3463 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3464 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3465 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3466 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3468 gnutls_error_is_fatal(rv
),
3469 gnutls_strerror_name(rv
));
3470 stop_tls(gsession
, xcred
);
3474 gnutls_credentials_type_t cred
;
3475 cred
= gnutls_auth_get_type(gsession
);
3476 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3477 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3478 stop_tls(gsession
, xcred
);
3490 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3494 const gnutls_datum_t
*cl
;
3495 gnutls_x509_crt_t
*all_certs
;
3498 if (certs
== NULL
|| cert_count
== NULL
)
3500 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3502 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3506 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3507 for (i
= 0; i
< len
; i
++) {
3508 gnutls_x509_crt_init(&all_certs
[i
]);
3509 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3510 GNUTLS_X509_FMT_PEM
< 0)) {
3524 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3528 for (i
= 0; i
< cert_count
; i
++)
3529 gnutls_x509_crt_deinit(certs
[i
]);
3534 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3536 GdkColor c_text
, c_base
;
3538 gdk_color_parse(text
, &c_text
);
3539 gdk_color_parse(base
, &c_base
);
3541 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3542 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3543 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3544 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3546 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3547 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3548 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3549 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3553 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3554 size_t cert_count
, char *domain
)
3557 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3562 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3565 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3566 if ((f
= fopen(file
, "w")) == NULL
) {
3567 show_oops(t
, "Can't create cert file %s %s",
3568 file
, strerror(errno
));
3572 for (i
= 0; i
< cert_count
; i
++) {
3573 cert_buf_sz
= sizeof cert_buf
;
3574 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3575 cert_buf
, &cert_buf_sz
)) {
3576 show_oops(t
, "gnutls_x509_crt_export failed");
3579 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3580 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3585 /* not the best spot but oh well */
3586 gdk_color_parse("lightblue", &color
);
3587 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3588 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3601 load_compare_cert(struct tab
*t
, struct karg
*args
)
3604 char domain
[8182], file
[PATH_MAX
];
3605 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3609 size_t cert_buf_sz
, cert_count
;
3610 enum cert_trust rv
= CERT_UNTRUSTED
;
3612 gnutls_session_t gsession
;
3613 gnutls_x509_crt_t
*certs
;
3614 gnutls_certificate_credentials_t xcred
;
3616 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3621 if ((uri
= get_uri(t
)) == NULL
)
3623 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3625 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3627 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3630 if (start_tls(t
, s
, &gsession
, &xcred
))
3632 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3634 /* verify certs in case cert file doesn't exist */
3635 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3637 show_oops(t
, "Invalid certificates");
3642 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3643 show_oops(t
, "Can't get connection certificates");
3647 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3648 if ((f
= fopen(file
, "r")) == NULL
) {
3654 for (i
= 0; i
< cert_count
; i
++) {
3655 cert_buf_sz
= sizeof cert_buf
;
3656 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3657 cert_buf
, &cert_buf_sz
)) {
3660 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3661 rv
= CERT_BAD
; /* critical */
3664 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3665 rv
= CERT_BAD
; /* critical */
3674 free_connection_certs(certs
, cert_count
);
3676 /* we close the socket first for speed */
3680 /* only complain if we didn't save it locally */
3681 if (error
&& rv
!= CERT_LOCAL
) {
3682 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3683 if (error
& GNUTLS_CERT_INVALID
)
3684 strlcat(serr
, "invalid, ", sizeof serr
);
3685 if (error
& GNUTLS_CERT_REVOKED
)
3686 strlcat(serr
, "revoked, ", sizeof serr
);
3687 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3688 strlcat(serr
, "signer not found, ", sizeof serr
);
3689 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3690 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3691 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3692 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3693 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3694 strlcat(serr
, "not activated, ", sizeof serr
);
3695 if (error
& GNUTLS_CERT_EXPIRED
)
3696 strlcat(serr
, "expired, ", sizeof serr
);
3697 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3698 if (serr
[i
] == ',') {
3705 stop_tls(gsession
, xcred
);
3711 cert_cmd(struct tab
*t
, struct karg
*args
)
3717 gnutls_session_t gsession
;
3718 gnutls_x509_crt_t
*certs
;
3719 gnutls_certificate_credentials_t xcred
;
3724 if (ssl_ca_file
== NULL
) {
3725 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3729 if ((uri
= get_uri(t
)) == NULL
) {
3730 show_oops(t
, "Invalid URI");
3734 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3735 show_oops(t
, "Invalid certificate URI: %s", uri
);
3740 if (start_tls(t
, s
, &gsession
, &xcred
))
3744 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3745 show_oops(t
, "get_connection_certs failed");
3749 if (args
->i
& XT_SHOW
)
3750 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3751 else if (args
->i
& XT_SAVE
)
3752 save_certs(t
, certs
, cert_count
, domain
);
3754 free_connection_certs(certs
, cert_count
);
3756 /* we close the socket first for speed */
3759 stop_tls(gsession
, xcred
);
3765 remove_cookie(int index
)
3771 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3773 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3775 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3779 print_cookie("remove cookie", c
);
3780 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3785 soup_cookies_free(cf
);
3791 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3796 body
= g_strdup("");
3799 if (args
->i
& XT_WL_PERSISTENT
) {
3801 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3803 RB_FOREACH(d
, domain_list
, wl
) {
3807 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3813 if (args
->i
& XT_WL_SESSION
) {
3815 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3817 RB_FOREACH(d
, domain_list
, wl
) {
3821 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3826 tmp
= get_html_page(title
, body
, "", 0);
3829 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3831 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3837 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3839 char file
[PATH_MAX
];
3841 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3849 if (t
== NULL
|| args
== NULL
)
3852 if (runtime_settings
[0] == '\0')
3855 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3856 if ((f
= fopen(file
, "r+")) == NULL
)
3860 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3861 if (uri
== NULL
|| dom
== NULL
||
3862 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3863 show_oops(t
, "Can't add domain to %s white list",
3864 js
? "JavaScript" : "cookie");
3868 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3871 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3874 if (!strcmp(line
, lt
))
3880 fprintf(f
, "%s\n", lt
);
3885 d
= wl_find(dom
, &js_wl
);
3887 settings_add("js_wl", dom
);
3888 d
= wl_find(dom
, &js_wl
);
3892 d
= wl_find(dom
, &c_wl
);
3894 settings_add("cookie_wl", dom
);
3895 d
= wl_find(dom
, &c_wl
);
3899 /* find and add to persistent jar */
3900 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3901 for (;cf
; cf
= cf
->next
) {
3903 if (!strcmp(dom
, ci
->domain
) ||
3904 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3905 c
= soup_cookie_copy(ci
);
3906 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3909 soup_cookies_free(cf
);
3927 js_show_wl(struct tab
*t
, struct karg
*args
)
3929 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3930 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3936 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3938 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3939 wl_show(t
, args
, "Cookie White List", &c_wl
);
3945 cookie_cmd(struct tab
*t
, struct karg
*args
)
3947 if (args
->i
& XT_SHOW
)
3948 wl_show(t
, args
, "Cookie White List", &c_wl
);
3949 else if (args
->i
& XT_WL_TOGGLE
) {
3950 args
->i
|= XT_WL_RELOAD
;
3951 toggle_cwl(t
, args
);
3952 } else if (args
->i
& XT_SAVE
) {
3953 args
->i
|= XT_WL_RELOAD
;
3954 wl_save(t
, args
, 0);
3955 } else if (args
->i
& XT_DELETE
)
3956 show_oops(t
, "'cookie delete' currently unimplemented");
3962 js_cmd(struct tab
*t
, struct karg
*args
)
3964 if (args
->i
& XT_SHOW
)
3965 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3966 else if (args
->i
& XT_SAVE
) {
3967 args
->i
|= XT_WL_RELOAD
;
3968 wl_save(t
, args
, 1);
3969 } else if (args
->i
& XT_WL_TOGGLE
) {
3970 args
->i
|= XT_WL_RELOAD
;
3972 } else if (args
->i
& XT_DELETE
)
3973 show_oops(t
, "'js delete' currently unimplemented");
3979 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3981 js_toggle_cb(t
->js_toggle
, t
);
3987 add_favorite(struct tab
*t
, struct karg
*args
)
3989 char file
[PATH_MAX
];
3992 size_t urilen
, linelen
;
3993 const gchar
*uri
, *title
;
3998 /* don't allow adding of xtp pages to favorites */
3999 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4000 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4004 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4005 if ((f
= fopen(file
, "r+")) == NULL
) {
4006 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4010 title
= get_title(t
, FALSE
);
4013 if (title
== NULL
|| uri
== NULL
) {
4014 show_oops(t
, "can't add page to favorites");
4018 urilen
= strlen(uri
);
4021 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4022 if (feof(f
) || ferror(f
))
4025 if (linelen
== urilen
&& !strcmp(line
, uri
))
4032 fprintf(f
, "\n%s\n%s", title
, uri
);
4038 update_favorite_tabs(NULL
);
4044 can_go_back_for_real(struct tab
*t
)
4047 WebKitWebHistoryItem
*item
;
4049 /* rely on webkit to make sure we can go backward when on an about page */
4050 if (get_uri(t
) == NULL
|| g_str_has_prefix(get_uri(t
), "about:"))
4051 return (webkit_web_view_can_go_forward(t
->wv
));
4054 /* the back/forwars list is stupid so help determine if we can go back */
4055 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4057 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4058 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
)))
4066 can_go_forward_for_real(struct tab
*t
)
4069 WebKitWebHistoryItem
*item
;
4071 /* rely on webkit to make sure we can go forward when on an about page */
4072 if (get_uri(t
) == NULL
|| g_str_has_prefix(get_uri(t
), "about:"))
4073 return (webkit_web_view_can_go_forward(t
->wv
));
4075 /* the back/forwars list is stupid so help selecting a different item */
4076 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4078 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4079 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
)))
4087 go_back_for_real(struct tab
*t
)
4090 WebKitWebHistoryItem
*item
;
4092 /* the back/forwars list is stupid so help selecting a different item */
4093 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4095 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4096 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
))) {
4097 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4104 go_forward_for_real(struct tab
*t
)
4107 WebKitWebHistoryItem
*item
;
4109 /* the back/forwars list is stupid so help selecting a different item */
4110 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4112 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4113 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
))) {
4114 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4121 navaction(struct tab
*t
, struct karg
*args
)
4123 WebKitWebHistoryItem
*item
;
4124 WebKitWebFrame
*frame
;
4126 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4127 t
->tab_id
, args
->i
);
4129 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4131 if (args
->i
== XT_NAV_BACK
)
4132 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4134 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4136 return (XT_CB_PASSTHROUGH
);
4137 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4139 return (XT_CB_PASSTHROUGH
);
4145 go_back_for_real(t
);
4147 case XT_NAV_FORWARD
:
4149 go_forward_for_real(t
);
4152 frame
= webkit_web_view_get_main_frame(t
->wv
);
4153 webkit_web_frame_reload(frame
);
4156 return (XT_CB_PASSTHROUGH
);
4160 move(struct tab
*t
, struct karg
*args
)
4162 GtkAdjustment
*adjust
;
4163 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4169 case XT_MOVE_BOTTOM
:
4171 case XT_MOVE_PAGEDOWN
:
4172 case XT_MOVE_PAGEUP
:
4173 case XT_MOVE_HALFDOWN
:
4174 case XT_MOVE_HALFUP
:
4175 case XT_MOVE_PERCENT
:
4176 adjust
= t
->adjust_v
;
4179 adjust
= t
->adjust_h
;
4183 pos
= gtk_adjustment_get_value(adjust
);
4184 ps
= gtk_adjustment_get_page_size(adjust
);
4185 upper
= gtk_adjustment_get_upper(adjust
);
4186 lower
= gtk_adjustment_get_lower(adjust
);
4187 si
= gtk_adjustment_get_step_increment(adjust
);
4188 pi
= gtk_adjustment_get_page_increment(adjust
);
4191 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4192 "max %f si %f pi %f\n",
4193 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4194 pos
, ps
, upper
, lower
, max
, si
, pi
);
4200 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4205 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4207 case XT_MOVE_BOTTOM
:
4208 case XT_MOVE_FARRIGHT
:
4209 gtk_adjustment_set_value(adjust
, max
);
4212 case XT_MOVE_FARLEFT
:
4213 gtk_adjustment_set_value(adjust
, lower
);
4215 case XT_MOVE_PAGEDOWN
:
4217 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4219 case XT_MOVE_PAGEUP
:
4221 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4223 case XT_MOVE_HALFDOWN
:
4225 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4227 case XT_MOVE_HALFUP
:
4229 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4231 case XT_MOVE_PERCENT
:
4232 percent
= atoi(args
->s
) / 100.0;
4233 pos
= max
* percent
;
4234 if (pos
< 0.0 || pos
> max
)
4236 gtk_adjustment_set_value(adjust
, pos
);
4239 return (XT_CB_PASSTHROUGH
);
4242 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4244 return (XT_CB_HANDLED
);
4248 url_set_visibility(void)
4252 TAILQ_FOREACH(t
, &tabs
, entry
)
4253 if (show_url
== 0) {
4254 gtk_widget_hide(t
->toolbar
);
4257 gtk_widget_show(t
->toolbar
);
4261 notebook_tab_set_visibility(void)
4263 if (show_tabs
== 0) {
4264 gtk_widget_hide(tab_bar
);
4265 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4267 if (tab_style
== XT_TABS_NORMAL
) {
4268 gtk_widget_hide(tab_bar
);
4269 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4270 } else if (tab_style
== XT_TABS_COMPACT
) {
4271 gtk_widget_show(tab_bar
);
4272 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4278 statusbar_set_visibility(void)
4282 TAILQ_FOREACH(t
, &tabs
, entry
)
4283 if (show_statusbar
== 0) {
4284 gtk_widget_hide(t
->statusbar_box
);
4287 gtk_widget_show(t
->statusbar_box
);
4291 url_set(struct tab
*t
, int enable_url_entry
)
4296 show_url
= enable_url_entry
;
4298 if (enable_url_entry
) {
4299 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4300 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4301 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4303 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4304 GTK_ENTRY_ICON_PRIMARY
);
4306 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4307 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4308 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4309 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4315 fullscreen(struct tab
*t
, struct karg
*args
)
4317 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4320 return (XT_CB_PASSTHROUGH
);
4322 if (show_url
== 0) {
4330 url_set_visibility();
4331 notebook_tab_set_visibility();
4333 return (XT_CB_HANDLED
);
4337 statustoggle(struct tab
*t
, struct karg
*args
)
4339 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4341 if (show_statusbar
== 1) {
4343 statusbar_set_visibility();
4344 } else if (show_statusbar
== 0) {
4346 statusbar_set_visibility();
4348 return (XT_CB_HANDLED
);
4352 urlaction(struct tab
*t
, struct karg
*args
)
4354 int rv
= XT_CB_HANDLED
;
4356 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4359 return (XT_CB_PASSTHROUGH
);
4363 if (show_url
== 0) {
4365 url_set_visibility();
4369 if (show_url
== 1) {
4371 url_set_visibility();
4379 tabaction(struct tab
*t
, struct karg
*args
)
4381 int rv
= XT_CB_HANDLED
;
4382 char *url
= args
->s
;
4386 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4389 return (XT_CB_PASSTHROUGH
);
4393 if (strlen(url
) > 0)
4394 create_new_tab(url
, NULL
, 1, args
->precount
);
4396 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4399 if (args
->precount
< 0)
4402 TAILQ_FOREACH(tt
, &tabs
, entry
)
4403 if (tt
->tab_id
== args
->precount
- 1) {
4408 case XT_TAB_DELQUIT
:
4409 if (gtk_notebook_get_n_pages(notebook
) > 1)
4415 if (strlen(url
) > 0)
4418 rv
= XT_CB_PASSTHROUGH
;
4424 if (show_tabs
== 0) {
4426 notebook_tab_set_visibility();
4430 if (show_tabs
== 1) {
4432 notebook_tab_set_visibility();
4435 case XT_TAB_NEXTSTYLE
:
4436 if (tab_style
== XT_TABS_NORMAL
) {
4437 tab_style
= XT_TABS_COMPACT
;
4438 recolor_compact_tabs();
4441 tab_style
= XT_TABS_NORMAL
;
4442 notebook_tab_set_visibility();
4444 case XT_TAB_UNDO_CLOSE
:
4445 if (undo_count
== 0) {
4446 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4451 u
= TAILQ_FIRST(&undos
);
4452 create_new_tab(u
->uri
, u
, 1, -1);
4454 TAILQ_REMOVE(&undos
, u
, entry
);
4456 /* u->history is freed in create_new_tab() */
4461 rv
= XT_CB_PASSTHROUGH
;
4475 resizetab(struct tab
*t
, struct karg
*args
)
4477 if (t
== NULL
|| args
== NULL
) {
4478 show_oops(NULL
, "resizetab invalid parameters");
4479 return (XT_CB_PASSTHROUGH
);
4482 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4483 t
->tab_id
, args
->i
);
4485 setzoom_webkit(t
, args
->i
);
4487 return (XT_CB_HANDLED
);
4491 movetab(struct tab
*t
, struct karg
*args
)
4495 if (t
== NULL
|| args
== NULL
) {
4496 show_oops(NULL
, "movetab invalid parameters");
4497 return (XT_CB_PASSTHROUGH
);
4500 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4501 t
->tab_id
, args
->i
);
4503 if (args
->i
>= XT_TAB_INVALID
)
4504 return (XT_CB_PASSTHROUGH
);
4506 if (TAILQ_EMPTY(&tabs
))
4507 return (XT_CB_PASSTHROUGH
);
4509 n
= gtk_notebook_get_n_pages(notebook
);
4510 dest
= gtk_notebook_get_current_page(notebook
);
4514 if (args
->precount
< 0)
4515 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4517 dest
= args
->precount
- 1;
4521 if (args
->precount
< 0)
4524 dest
-= args
->precount
% n
;
4537 return (XT_CB_PASSTHROUGH
);
4540 if (dest
< 0 || dest
>= n
)
4541 return (XT_CB_PASSTHROUGH
);
4542 if (t
->tab_id
== dest
) {
4543 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4544 return (XT_CB_HANDLED
);
4547 set_current_tab(dest
);
4549 return (XT_CB_HANDLED
);
4555 command(struct tab
*t
, struct karg
*args
)
4557 char *s
= NULL
, *ss
= NULL
;
4561 if (t
== NULL
|| args
== NULL
) {
4562 show_oops(NULL
, "command invalid parameters");
4563 return (XT_CB_PASSTHROUGH
);
4574 if (cmd_prefix
== 0)
4577 ss
= g_strdup_printf(":%d", cmd_prefix
);
4588 case XT_CMD_OPEN_CURRENT
:
4591 case XT_CMD_TABNEW_CURRENT
:
4592 if (!s
) /* FALL THROUGH? */
4594 if ((uri
= get_uri(t
)) != NULL
) {
4595 ss
= g_strdup_printf("%s%s", s
, uri
);
4600 show_oops(t
, "command: invalid opcode %d", args
->i
);
4601 return (XT_CB_PASSTHROUGH
);
4604 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4606 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4607 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4608 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4610 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4611 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4616 return (XT_CB_HANDLED
);
4620 * Return a new string with a download row (in html)
4621 * appended. Old string is freed.
4624 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4627 WebKitDownloadStatus stat
;
4628 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4630 char cur_sz
[FMT_SCALED_STRSIZE
];
4631 char tot_sz
[FMT_SCALED_STRSIZE
];
4634 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4636 /* All actions wil take this form:
4637 * xxxt://class/seskey
4639 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4640 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4642 stat
= webkit_download_get_status(dl
->download
);
4645 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4646 status_html
= g_strdup_printf("Finished");
4647 cmd_html
= g_strdup_printf(
4648 "<a href='%s%d/%d'>Remove</a>",
4649 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4651 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4652 /* gather size info */
4653 progress
= 100 * webkit_download_get_progress(dl
->download
);
4656 webkit_download_get_current_size(dl
->download
), cur_sz
);
4658 webkit_download_get_total_size(dl
->download
), tot_sz
);
4660 status_html
= g_strdup_printf(
4661 "<div style='width: 100%%' align='center'>"
4662 "<div class='progress-outer'>"
4663 "<div class='progress-inner' style='width: %.2f%%'>"
4664 "</div></div></div>"
4665 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4666 progress
, cur_sz
, tot_sz
, progress
);
4668 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4669 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4673 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4674 status_html
= g_strdup_printf("Cancelled");
4675 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4676 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4678 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4679 status_html
= g_strdup_printf("Error!");
4680 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4681 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4683 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4684 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4685 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4686 status_html
= g_strdup_printf("Starting");
4689 show_oops(t
, "%s: unknown download status", __func__
);
4692 new_html
= g_strdup_printf(
4693 "%s\n<tr><td>%s</td><td>%s</td>"
4694 "<td style='text-align:center'>%s</td></tr>\n",
4695 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4696 status_html
, cmd_html
);
4700 g_free(status_html
);
4711 * update all download tabs apart from one. Pass NULL if
4712 * you want to update all.
4715 update_download_tabs(struct tab
*apart_from
)
4718 if (!updating_dl_tabs
) {
4719 updating_dl_tabs
= 1; /* stop infinite recursion */
4720 TAILQ_FOREACH(t
, &tabs
, entry
)
4721 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4722 && (t
!= apart_from
))
4723 xtp_page_dl(t
, NULL
);
4724 updating_dl_tabs
= 0;
4729 * update all cookie tabs apart from one. Pass NULL if
4730 * you want to update all.
4733 update_cookie_tabs(struct tab
*apart_from
)
4736 if (!updating_cl_tabs
) {
4737 updating_cl_tabs
= 1; /* stop infinite recursion */
4738 TAILQ_FOREACH(t
, &tabs
, entry
)
4739 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4740 && (t
!= apart_from
))
4741 xtp_page_cl(t
, NULL
);
4742 updating_cl_tabs
= 0;
4747 * update all history tabs apart from one. Pass NULL if
4748 * you want to update all.
4751 update_history_tabs(struct tab
*apart_from
)
4755 if (!updating_hl_tabs
) {
4756 updating_hl_tabs
= 1; /* stop infinite recursion */
4757 TAILQ_FOREACH(t
, &tabs
, entry
)
4758 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4759 && (t
!= apart_from
))
4760 xtp_page_hl(t
, NULL
);
4761 updating_hl_tabs
= 0;
4765 /* cookie management XTP page */
4767 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4769 char *body
, *page
, *tmp
;
4770 int i
= 1; /* all ids start 1 */
4771 GSList
*sc
, *pc
, *pc_start
;
4773 char *type
, *table_headers
, *last_domain
;
4775 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4778 show_oops(NULL
, "%s invalid parameters", __func__
);
4782 /* Generate a new session key */
4783 if (!updating_cl_tabs
)
4784 generate_xtp_session_key(&cl_session_key
);
4787 table_headers
= g_strdup_printf("<table><tr>"
4790 "<th style='width:200px'>Value</th>"
4794 "<th>HTTP<br />only</th>"
4795 "<th style='width:40px'>Rm</th></tr>\n");
4797 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4798 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4802 last_domain
= strdup("");
4803 for (; sc
; sc
= sc
->next
) {
4806 if (strcmp(last_domain
, c
->domain
) != 0) {
4809 last_domain
= strdup(c
->domain
);
4813 body
= g_strdup_printf("%s</table>"
4815 body
, c
->domain
, table_headers
);
4819 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4820 c
->domain
, table_headers
);
4825 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4826 if (soup_cookie_equal(pc
->data
, c
)) {
4827 type
= "Session + Persistent";
4832 body
= g_strdup_printf(
4835 "<td style='word-wrap:normal'>%s</td>"
4837 " <textarea rows='4'>%s</textarea>"
4843 "<td style='text-align:center'>"
4844 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4851 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4866 soup_cookies_free(sc
);
4867 soup_cookies_free(pc
);
4869 /* small message if there are none */
4871 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4872 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4875 body
= g_strdup_printf("%s</table>", body
);
4878 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4880 g_free(table_headers
);
4881 g_free(last_domain
);
4883 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4884 update_cookie_tabs(t
);
4892 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4894 char *body
, *page
, *tmp
;
4896 int i
= 1; /* all ids start 1 */
4898 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4901 show_oops(NULL
, "%s invalid parameters", __func__
);
4905 /* Generate a new session key */
4906 if (!updating_hl_tabs
)
4907 generate_xtp_session_key(&hl_session_key
);
4910 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4911 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4913 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4915 body
= g_strdup_printf(
4917 "<td><a href='%s'>%s</a></td>"
4919 "<td style='text-align: center'>"
4920 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4921 body
, h
->uri
, h
->uri
, h
->title
,
4922 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4923 XT_XTP_HL_REMOVE
, i
);
4929 /* small message if there are none */
4932 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4933 "colspan='3'>No History</td></tr>\n", body
);
4938 body
= g_strdup_printf("%s</table>", body
);
4941 page
= get_html_page("History", body
, "", TRUE
);
4945 * update all history manager tabs as the xtp session
4946 * key has now changed. No need to update the current tab.
4947 * Already did that above.
4949 update_history_tabs(t
);
4951 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4958 * Generate a web page detailing the status of any downloads
4961 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4963 struct download
*dl
;
4964 char *body
, *page
, *tmp
;
4968 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4971 show_oops(NULL
, "%s invalid parameters", __func__
);
4976 * Generate a new session key for next page instance.
4977 * This only happens for the top level call to xtp_page_dl()
4978 * in which case updating_dl_tabs is 0.
4980 if (!updating_dl_tabs
)
4981 generate_xtp_session_key(&dl_session_key
);
4983 /* header - with refresh so as to update */
4984 if (refresh_interval
>= 1)
4985 ref
= g_strdup_printf(
4986 "<meta http-equiv='refresh' content='%u"
4987 ";url=%s%d/%s/%d' />\n",
4996 body
= g_strdup_printf("<div align='center'>"
4997 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4998 "</p><table><tr><th style='width: 60%%'>"
4999 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5000 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5002 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5003 body
= xtp_page_dl_row(t
, body
, dl
);
5007 /* message if no downloads in list */
5010 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5011 " style='text-align: center'>"
5012 "No downloads</td></tr>\n", body
);
5017 body
= g_strdup_printf("%s</table></div>", body
);
5020 page
= get_html_page("Downloads", body
, ref
, 1);
5025 * update all download manager tabs as the xtp session
5026 * key has now changed. No need to update the current tab.
5027 * Already did that above.
5029 update_download_tabs(t
);
5031 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5038 search(struct tab
*t
, struct karg
*args
)
5042 if (t
== NULL
|| args
== NULL
) {
5043 show_oops(NULL
, "search invalid parameters");
5046 if (t
->search_text
== NULL
) {
5047 if (global_search
== NULL
)
5048 return (XT_CB_PASSTHROUGH
);
5050 t
->search_text
= g_strdup(global_search
);
5051 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5052 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5056 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5057 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5060 case XT_SEARCH_NEXT
:
5061 d
= t
->search_forward
;
5063 case XT_SEARCH_PREV
:
5064 d
= !t
->search_forward
;
5067 return (XT_CB_PASSTHROUGH
);
5070 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5072 return (XT_CB_HANDLED
);
5075 struct settings_args
{
5081 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5084 struct settings_args
*sa
= cb_args
;
5089 if (s
->flags
& XT_SF_RUNTIME
)
5095 *sa
->body
= g_strdup_printf(
5097 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5098 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5110 set_show(struct tab
*t
, struct karg
*args
)
5112 char *body
, *page
, *tmp
;
5114 struct settings_args sa
;
5116 bzero(&sa
, sizeof sa
);
5120 body
= g_strdup_printf("<div align='center'><table><tr>"
5121 "<th align='left'>Setting</th>"
5122 "<th align='left'>Value</th></tr>\n");
5124 settings_walk(print_setting
, &sa
);
5127 /* small message if there are none */
5130 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5131 "colspan='2'>No settings</td></tr>\n", body
);
5136 body
= g_strdup_printf("%s</table></div>", body
);
5139 page
= get_html_page("Settings", body
, "", 0);
5143 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5147 return (XT_CB_PASSTHROUGH
);
5151 set(struct tab
*t
, struct karg
*args
)
5156 if (args
== NULL
|| args
->s
== NULL
)
5157 return (set_show(t
, args
));
5160 p
= g_strstrip(args
->s
);
5163 return (set_show(t
, args
));
5165 /* we got some sort of string */
5166 val
= g_strrstr(p
, "=");
5169 val
= g_strchomp(val
);
5172 for (i
= 0; i
< LENGTH(rs
); i
++) {
5173 if (strcmp(rs
[i
].name
, p
))
5176 if (rs
[i
].activate
) {
5177 if (rs
[i
].activate(val
))
5178 show_oops(t
, "%s invalid value %s",
5181 show_oops(t
, ":set %s = %s", p
, val
);
5184 show_oops(t
, "not a runtime option: %s", p
);
5188 show_oops(t
, "unknown option: %s", p
);
5192 for (i
= 0; i
< LENGTH(rs
); i
++) {
5193 if (strcmp(rs
[i
].name
, p
))
5196 /* XXX this could use some cleanup */
5197 switch (rs
[i
].type
) {
5200 show_oops(t
, "%s = %d",
5201 rs
[i
].name
, *rs
[i
].ival
);
5202 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5203 show_oops(t
, "%s = %s",
5205 rs
[i
].s
->get(&rs
[i
]));
5206 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5207 show_oops(t
, "%s = ...", rs
[i
].name
);
5209 show_oops(t
, "%s = ", rs
[i
].name
);
5213 show_oops(t
, "%s = %f",
5214 rs
[i
].name
, *rs
[i
].fval
);
5215 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5216 show_oops(t
, "%s = %s",
5218 rs
[i
].s
->get(&rs
[i
]));
5219 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5220 show_oops(t
, "%s = ...", rs
[i
].name
);
5222 show_oops(t
, "%s = ", rs
[i
].name
);
5225 if (rs
[i
].sval
&& *rs
[i
].sval
)
5226 show_oops(t
, "%s = %s",
5227 rs
[i
].name
, *rs
[i
].sval
);
5228 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5229 show_oops(t
, "%s = %s",
5231 rs
[i
].s
->get(&rs
[i
]));
5232 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5233 show_oops(t
, "%s = ...", rs
[i
].name
);
5235 show_oops(t
, "%s = ", rs
[i
].name
);
5238 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5244 show_oops(t
, "unknown option: %s", p
);
5247 return (XT_CB_PASSTHROUGH
);
5251 session_save(struct tab
*t
, char *filename
)
5257 if (strlen(filename
) == 0)
5260 if (filename
[0] == '.' || filename
[0] == '/')
5264 if (save_tabs(t
, &a
))
5266 strlcpy(named_session
, filename
, sizeof named_session
);
5268 /* add the new session to the list of sessions */
5269 s
= g_malloc(sizeof(struct session
));
5270 s
->name
= g_strdup(filename
);
5271 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5279 session_open(struct tab
*t
, char *filename
)
5284 if (strlen(filename
) == 0)
5287 if (filename
[0] == '.' || filename
[0] == '/')
5291 a
.i
= XT_SES_CLOSETABS
;
5292 if (open_tabs(t
, &a
))
5295 strlcpy(named_session
, filename
, sizeof named_session
);
5303 session_delete(struct tab
*t
, char *filename
)
5305 char file
[PATH_MAX
];
5309 if (strlen(filename
) == 0)
5312 if (filename
[0] == '.' || filename
[0] == '/')
5315 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5319 if (!strcmp(filename
, named_session
))
5320 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5321 sizeof named_session
);
5323 /* remove session from sessions list */
5324 TAILQ_FOREACH(s
, &sessions
, entry
) {
5325 if (!strcmp(s
->name
, filename
))
5328 TAILQ_REMOVE(&sessions
, s
, entry
);
5329 g_free((gpointer
) s
->name
);
5338 session_cmd(struct tab
*t
, struct karg
*args
)
5340 char *filename
= args
->s
;
5345 if (args
->i
& XT_SHOW
)
5346 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5347 XT_SAVED_TABS_FILE
: named_session
);
5348 else if (args
->i
& XT_SAVE
) {
5349 if (session_save(t
, filename
)) {
5350 show_oops(t
, "Can't save session: %s",
5351 filename
? filename
: "INVALID");
5354 } else if (args
->i
& XT_OPEN
) {
5355 if (session_open(t
, filename
)) {
5356 show_oops(t
, "Can't open session: %s",
5357 filename
? filename
: "INVALID");
5360 } else if (args
->i
& XT_DELETE
) {
5361 if (session_delete(t
, filename
)) {
5362 show_oops(t
, "Can't delete session: %s",
5363 filename
? filename
: "INVALID");
5368 return (XT_CB_PASSTHROUGH
);
5372 * Make a hardcopy of the page
5375 print_page(struct tab
*t
, struct karg
*args
)
5377 WebKitWebFrame
*frame
;
5379 GtkPrintOperation
*op
;
5380 GtkPrintOperationAction action
;
5381 GtkPrintOperationResult print_res
;
5382 GError
*g_err
= NULL
;
5383 int marg_l
, marg_r
, marg_t
, marg_b
;
5385 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5387 ps
= gtk_page_setup_new();
5388 op
= gtk_print_operation_new();
5389 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5390 frame
= webkit_web_view_get_main_frame(t
->wv
);
5392 /* the default margins are too small, so we will bump them */
5393 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5394 XT_PRINT_EXTRA_MARGIN
;
5395 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5396 XT_PRINT_EXTRA_MARGIN
;
5397 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5398 XT_PRINT_EXTRA_MARGIN
;
5399 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5400 XT_PRINT_EXTRA_MARGIN
;
5403 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5404 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5405 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5406 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5408 gtk_print_operation_set_default_page_setup(op
, ps
);
5410 /* this appears to free 'op' and 'ps' */
5411 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5413 /* check it worked */
5414 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5415 show_oops(NULL
, "can't print: %s", g_err
->message
);
5416 g_error_free (g_err
);
5424 go_home(struct tab
*t
, struct karg
*args
)
5431 restart(struct tab
*t
, struct karg
*args
)
5435 a
.s
= XT_RESTART_TABS_FILE
;
5437 execvp(start_argv
[0], start_argv
);
5443 #define CTRL GDK_CONTROL_MASK
5444 #define MOD1 GDK_MOD1_MASK
5445 #define SHFT GDK_SHIFT_MASK
5447 /* inherent to GTK not all keys will be caught at all times */
5448 /* XXX sort key bindings */
5449 struct key_binding
{
5454 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5456 { "cookiejar", MOD1
, 0, GDK_j
},
5457 { "downloadmgr", MOD1
, 0, GDK_d
},
5458 { "history", MOD1
, 0, GDK_h
},
5459 { "print", CTRL
, 0, GDK_p
},
5460 { "search", 0, 0, GDK_slash
},
5461 { "searchb", 0, 0, GDK_question
},
5462 { "statustoggle", CTRL
, 0, GDK_n
},
5463 { "command", 0, 0, GDK_colon
},
5464 { "qa", CTRL
, 0, GDK_q
},
5465 { "restart", MOD1
, 0, GDK_q
},
5466 { "js toggle", CTRL
, 0, GDK_j
},
5467 { "cookie toggle", MOD1
, 0, GDK_c
},
5468 { "togglesrc", CTRL
, 0, GDK_s
},
5469 { "yankuri", 0, 0, GDK_y
},
5470 { "pasteuricur", 0, 0, GDK_p
},
5471 { "pasteurinew", 0, 0, GDK_P
},
5472 { "toplevel toggle", 0, 0, GDK_F4
},
5473 { "help", 0, 0, GDK_F1
},
5474 { "run_script", MOD1
, 0, GDK_r
},
5477 { "searchnext", 0, 0, GDK_n
},
5478 { "searchprevious", 0, 0, GDK_N
},
5481 { "focusaddress", 0, 0, GDK_F6
},
5482 { "focussearch", 0, 0, GDK_F7
},
5485 { "hinting", 0, 0, GDK_f
},
5487 /* custom stylesheet */
5488 { "userstyle", 0, 0, GDK_i
},
5491 { "goback", 0, 0, GDK_BackSpace
},
5492 { "goback", MOD1
, 0, GDK_Left
},
5493 { "goforward", SHFT
, 0, GDK_BackSpace
},
5494 { "goforward", MOD1
, 0, GDK_Right
},
5495 { "reload", 0, 0, GDK_F5
},
5496 { "reload", CTRL
, 0, GDK_r
},
5497 { "reload", CTRL
, 0, GDK_l
},
5498 { "favorites", MOD1
, 1, GDK_f
},
5500 /* vertical movement */
5501 { "scrolldown", 0, 0, GDK_j
},
5502 { "scrolldown", 0, 0, GDK_Down
},
5503 { "scrollup", 0, 0, GDK_Up
},
5504 { "scrollup", 0, 0, GDK_k
},
5505 { "scrollbottom", 0, 0, GDK_G
},
5506 { "scrollbottom", 0, 0, GDK_End
},
5507 { "scrolltop", 0, 0, GDK_Home
},
5508 { "scrollpagedown", 0, 0, GDK_space
},
5509 { "scrollpagedown", CTRL
, 0, GDK_f
},
5510 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5511 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5512 { "scrollpageup", 0, 0, GDK_Page_Up
},
5513 { "scrollpageup", CTRL
, 0, GDK_b
},
5514 { "scrollhalfup", CTRL
, 0, GDK_u
},
5515 /* horizontal movement */
5516 { "scrollright", 0, 0, GDK_l
},
5517 { "scrollright", 0, 0, GDK_Right
},
5518 { "scrollleft", 0, 0, GDK_Left
},
5519 { "scrollleft", 0, 0, GDK_h
},
5520 { "scrollfarright", 0, 0, GDK_dollar
},
5521 { "scrollfarleft", 0, 0, GDK_0
},
5524 { "tabnew", CTRL
, 0, GDK_t
},
5525 { "999tabnew", CTRL
, 0, GDK_T
},
5526 { "tabclose", CTRL
, 1, GDK_w
},
5527 { "tabundoclose", 0, 0, GDK_U
},
5528 { "tabnext 1", CTRL
, 0, GDK_1
},
5529 { "tabnext 2", CTRL
, 0, GDK_2
},
5530 { "tabnext 3", CTRL
, 0, GDK_3
},
5531 { "tabnext 4", CTRL
, 0, GDK_4
},
5532 { "tabnext 5", CTRL
, 0, GDK_5
},
5533 { "tabnext 6", CTRL
, 0, GDK_6
},
5534 { "tabnext 7", CTRL
, 0, GDK_7
},
5535 { "tabnext 8", CTRL
, 0, GDK_8
},
5536 { "tabnext 9", CTRL
, 0, GDK_9
},
5537 { "tabfirst", CTRL
, 0, GDK_less
},
5538 { "tablast", CTRL
, 0, GDK_greater
},
5539 { "tabprevious", CTRL
, 0, GDK_Left
},
5540 { "tabnext", CTRL
, 0, GDK_Right
},
5541 { "focusout", CTRL
, 0, GDK_minus
},
5542 { "focusin", CTRL
, 0, GDK_plus
},
5543 { "focusin", CTRL
, 0, GDK_equal
},
5544 { "focusreset", CTRL
, 0, GDK_0
},
5546 /* command aliases (handy when -S flag is used) */
5547 { "promptopen", 0, 0, GDK_F9
},
5548 { "promptopencurrent", 0, 0, GDK_F10
},
5549 { "prompttabnew", 0, 0, GDK_F11
},
5550 { "prompttabnewcurrent",0, 0, GDK_F12
},
5552 TAILQ_HEAD(keybinding_list
, key_binding
);
5555 walk_kb(struct settings
*s
,
5556 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5558 struct key_binding
*k
;
5561 if (s
== NULL
|| cb
== NULL
) {
5562 show_oops(NULL
, "walk_kb invalid parameters");
5566 TAILQ_FOREACH(k
, &kbl
, entry
) {
5572 if (gdk_keyval_name(k
->key
) == NULL
)
5575 strlcat(str
, k
->cmd
, sizeof str
);
5576 strlcat(str
, ",", sizeof str
);
5578 if (k
->mask
& GDK_SHIFT_MASK
)
5579 strlcat(str
, "S-", sizeof str
);
5580 if (k
->mask
& GDK_CONTROL_MASK
)
5581 strlcat(str
, "C-", sizeof str
);
5582 if (k
->mask
& GDK_MOD1_MASK
)
5583 strlcat(str
, "M1-", sizeof str
);
5584 if (k
->mask
& GDK_MOD2_MASK
)
5585 strlcat(str
, "M2-", sizeof str
);
5586 if (k
->mask
& GDK_MOD3_MASK
)
5587 strlcat(str
, "M3-", sizeof str
);
5588 if (k
->mask
& GDK_MOD4_MASK
)
5589 strlcat(str
, "M4-", sizeof str
);
5590 if (k
->mask
& GDK_MOD5_MASK
)
5591 strlcat(str
, "M5-", sizeof str
);
5593 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5594 cb(s
, str
, cb_args
);
5599 init_keybindings(void)
5602 struct key_binding
*k
;
5604 for (i
= 0; i
< LENGTH(keys
); i
++) {
5605 k
= g_malloc0(sizeof *k
);
5606 k
->cmd
= keys
[i
].cmd
;
5607 k
->mask
= keys
[i
].mask
;
5608 k
->use_in_entry
= keys
[i
].use_in_entry
;
5609 k
->key
= keys
[i
].key
;
5610 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5612 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5613 k
->cmd
? k
->cmd
: "unnamed key");
5618 keybinding_clearall(void)
5620 struct key_binding
*k
, *next
;
5622 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5623 next
= TAILQ_NEXT(k
, entry
);
5627 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5628 k
->cmd
? k
->cmd
: "unnamed key");
5629 TAILQ_REMOVE(&kbl
, k
, entry
);
5635 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5637 struct key_binding
*k
;
5638 guint keyval
, mask
= 0;
5641 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5643 /* Keys which are to be used in entry have been prefixed with an
5644 * exclamation mark. */
5648 /* find modifier keys */
5649 if (strstr(key
, "S-"))
5650 mask
|= GDK_SHIFT_MASK
;
5651 if (strstr(key
, "C-"))
5652 mask
|= GDK_CONTROL_MASK
;
5653 if (strstr(key
, "M1-"))
5654 mask
|= GDK_MOD1_MASK
;
5655 if (strstr(key
, "M2-"))
5656 mask
|= GDK_MOD2_MASK
;
5657 if (strstr(key
, "M3-"))
5658 mask
|= GDK_MOD3_MASK
;
5659 if (strstr(key
, "M4-"))
5660 mask
|= GDK_MOD4_MASK
;
5661 if (strstr(key
, "M5-"))
5662 mask
|= GDK_MOD5_MASK
;
5665 for (i
= strlen(key
) - 1; i
> 0; i
--)
5669 /* validate keyname */
5670 keyval
= gdk_keyval_from_name(key
);
5671 if (keyval
== GDK_VoidSymbol
) {
5672 warnx("invalid keybinding name %s", key
);
5675 /* must run this test too, gtk+ doesn't handle 10 for example */
5676 if (gdk_keyval_name(keyval
) == NULL
) {
5677 warnx("invalid keybinding name %s", key
);
5681 /* Remove eventual dupes. */
5682 TAILQ_FOREACH(k
, &kbl
, entry
)
5683 if (k
->key
== keyval
&& k
->mask
== mask
) {
5684 TAILQ_REMOVE(&kbl
, k
, entry
);
5690 k
= g_malloc0(sizeof *k
);
5691 k
->cmd
= g_strdup(cmd
);
5693 k
->use_in_entry
= use_in_entry
;
5696 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5701 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5702 k
->cmd
, gdk_keyval_name(keyval
));
5704 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5710 add_kb(struct settings
*s
, char *entry
)
5714 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5716 /* clearall is special */
5717 if (!strcmp(entry
, "clearall")) {
5718 keybinding_clearall();
5722 kb
= strstr(entry
, ",");
5728 return (keybinding_add(entry
, key
, key
[0] == '!'));
5734 int (*func
)(struct tab
*, struct karg
*);
5738 { "command", 0, command
, ':', 0 },
5739 { "search", 0, command
, '/', 0 },
5740 { "searchb", 0, command
, '?', 0 },
5741 { "togglesrc", 0, toggle_src
, 0, 0 },
5743 /* yanking and pasting */
5744 { "yankuri", 0, yank_uri
, 0, 0 },
5745 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5746 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5747 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5750 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5751 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5754 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5755 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5758 { "hinting", 0, hint
, 0, 0 },
5760 /* custom stylesheet */
5761 { "userstyle", 0, userstyle
, 0, 0 },
5764 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5765 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5766 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5768 /* vertical movement */
5769 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5770 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5771 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5772 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5773 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5774 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5775 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5776 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5777 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5778 /* horizontal movement */
5779 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5780 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5781 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5782 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5784 { "favorites", 0, xtp_page_fl
, 0, 0 },
5785 { "fav", 0, xtp_page_fl
, 0, 0 },
5786 { "favadd", 0, add_favorite
, 0, 0 },
5788 { "qall", 0, quit
, 0, 0 },
5789 { "quitall", 0, quit
, 0, 0 },
5790 { "w", 0, save_tabs
, 0, 0 },
5791 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5792 { "help", 0, help
, 0, 0 },
5793 { "about", 0, about
, 0, 0 },
5794 { "stats", 0, stats
, 0, 0 },
5795 { "version", 0, about
, 0, 0 },
5798 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5799 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5800 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5801 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5802 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5803 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5804 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5805 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5806 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5807 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5808 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5810 /* cookie command */
5811 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5812 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5813 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5814 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5815 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5816 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5817 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5818 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5819 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5820 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5821 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5823 /* toplevel (domain) command */
5824 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5825 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5828 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5831 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5832 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5833 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5835 { "ca", 0, ca_cmd
, 0, 0 },
5836 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5837 { "dl", 0, xtp_page_dl
, 0, 0 },
5838 { "h", 0, xtp_page_hl
, 0, 0 },
5839 { "history", 0, xtp_page_hl
, 0, 0 },
5840 { "home", 0, go_home
, 0, 0 },
5841 { "restart", 0, restart
, 0, 0 },
5842 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5843 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5844 { "statustoggle", 0, statustoggle
, 0, 0 },
5845 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5847 { "print", 0, print_page
, 0, 0 },
5850 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5851 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5852 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5853 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5854 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5855 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5856 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5857 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5858 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5859 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5860 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5861 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5862 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5863 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5864 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5865 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5866 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5867 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5868 { "buffers", 0, buffers
, 0, 0 },
5869 { "ls", 0, buffers
, 0, 0 },
5870 { "tabs", 0, buffers
, 0, 0 },
5872 /* command aliases (handy when -S flag is used) */
5873 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5874 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5875 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5876 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5879 { "set", 0, set
, 0, XT_SETARG
},
5881 { "fullscreen", 0, fullscreen
, 0, 0 },
5882 { "f", 0, fullscreen
, 0, 0 },
5885 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5886 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
5887 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
5888 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5889 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5896 } cmd_status
= {-1, 0};
5899 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5902 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5909 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5916 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5918 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5924 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5926 a
.i
= XT_NAV_FORWARD
;
5936 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5938 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5940 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5947 * cancel, remove, etc. downloads
5950 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5952 struct download find
, *d
= NULL
;
5954 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5956 /* some commands require a valid download id */
5957 if (cmd
!= XT_XTP_DL_LIST
) {
5958 /* lookup download in question */
5960 d
= RB_FIND(download_list
, &downloads
, &find
);
5963 show_oops(t
, "%s: no such download", __func__
);
5968 /* decide what to do */
5970 case XT_XTP_DL_CANCEL
:
5971 webkit_download_cancel(d
->download
);
5973 case XT_XTP_DL_REMOVE
:
5974 webkit_download_cancel(d
->download
); /* just incase */
5975 g_object_unref(d
->download
);
5976 RB_REMOVE(download_list
, &downloads
, d
);
5978 case XT_XTP_DL_LIST
:
5982 show_oops(t
, "%s: unknown command", __func__
);
5985 xtp_page_dl(t
, NULL
);
5989 * Actions on history, only does one thing for now, but
5990 * we provide the function for future actions
5993 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5995 struct history
*h
, *next
;
5999 case XT_XTP_HL_REMOVE
:
6000 /* walk backwards, as listed in reverse */
6001 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6002 next
= RB_PREV(history_list
, &hl
, h
);
6004 RB_REMOVE(history_list
, &hl
, h
);
6005 g_free((gpointer
) h
->title
);
6006 g_free((gpointer
) h
->uri
);
6013 case XT_XTP_HL_LIST
:
6014 /* Nothing - just xtp_page_hl() below */
6017 show_oops(t
, "%s: unknown command", __func__
);
6021 xtp_page_hl(t
, NULL
);
6024 /* remove a favorite */
6026 remove_favorite(struct tab
*t
, int index
)
6028 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6029 char *new_favs
, *tmp
;
6034 /* open favorites */
6035 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6037 if ((f
= fopen(file
, "r")) == NULL
) {
6038 show_oops(t
, "%s: can't open favorites: %s",
6039 __func__
, strerror(errno
));
6043 /* build a string which will become the new favroites file */
6044 new_favs
= g_strdup("");
6047 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6048 if (feof(f
) || ferror(f
))
6050 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6057 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6058 if (feof(f
) || ferror(f
)) {
6059 show_oops(t
, "%s: can't parse favorites %s",
6060 __func__
, strerror(errno
));
6065 /* as long as this isn't the one we are deleting add to file */
6068 new_favs
= g_strdup_printf("%s%s\n%s\n",
6069 new_favs
, title
, uri
);
6081 /* write back new favorites file */
6082 if ((f
= fopen(file
, "w")) == NULL
) {
6083 show_oops(t
, "%s: can't open favorites: %s",
6084 __func__
, strerror(errno
));
6088 fwrite(new_favs
, strlen(new_favs
), 1, f
);
6101 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6104 case XT_XTP_FL_LIST
:
6105 /* nothing, just the below call to xtp_page_fl() */
6107 case XT_XTP_FL_REMOVE
:
6108 remove_favorite(t
, arg
);
6111 show_oops(t
, "%s: invalid favorites command", __func__
);
6115 xtp_page_fl(t
, NULL
);
6119 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6122 case XT_XTP_CL_LIST
:
6123 /* nothing, just xtp_page_cl() */
6125 case XT_XTP_CL_REMOVE
:
6129 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6133 xtp_page_cl(t
, NULL
);
6136 /* link an XTP class to it's session key and handler function */
6137 struct xtp_despatch
{
6140 void (*handle_func
)(struct tab
*, uint8_t, int);
6143 struct xtp_despatch xtp_despatches
[] = {
6144 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6145 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6146 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6147 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6148 { XT_XTP_INVALID
, NULL
, NULL
}
6152 * is the url xtp protocol? (xxxt://)
6153 * if so, parse and despatch correct bahvior
6156 parse_xtp_url(struct tab
*t
, const char *url
)
6158 char *dup
= NULL
, *p
, *last
;
6159 uint8_t n_tokens
= 0;
6160 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6161 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6166 * tokens array meaning:
6168 * tokens[1] = session key
6169 * tokens[2] = action
6170 * tokens[3] = optional argument
6173 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6175 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6178 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6180 /* split out the url */
6181 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6182 (p
= strtok_r(NULL
, "/", &last
))) {
6184 tokens
[n_tokens
++] = p
;
6187 /* should be atleast three fields 'class/seskey/command/arg' */
6191 dsp
= xtp_despatches
;
6192 req_class
= atoi(tokens
[0]);
6193 while (dsp
->xtp_class
) {
6194 if (dsp
->xtp_class
== req_class
) {
6201 /* did we find one atall? */
6202 if (dsp_match
== NULL
) {
6203 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6207 /* check session key and call despatch function */
6208 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6209 ret
= TRUE
; /* all is well, this was a valid xtp request */
6210 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6223 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6225 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6227 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6230 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6235 show_oops(t
, "activate_uri_entry_cb no uri");
6239 uri
+= strspn(uri
, "\t ");
6241 /* if xxxt:// treat specially */
6242 if (parse_xtp_url(t
, uri
))
6245 /* otherwise continue to load page normally */
6246 load_uri(t
, (gchar
*)uri
);
6251 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6253 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6254 char *newuri
= NULL
;
6257 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6260 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6264 if (search_string
== NULL
) {
6265 show_oops(t
, "no search_string");
6269 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6271 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6272 newuri
= g_strdup_printf(search_string
, enc_search
);
6276 webkit_web_view_load_uri(t
->wv
, newuri
);
6284 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6286 struct domain
*d
= NULL
;
6289 if (uri
== NULL
|| t
== NULL
)
6292 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6297 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6298 es
? "enable" : "disable", uri
);
6300 g_object_set(G_OBJECT(t
->settings
),
6301 "enable-html5-local-storage", es
, (char *)NULL
);
6302 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6306 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6308 struct domain
*d
= NULL
;
6311 if (uri
== NULL
|| t
== NULL
)
6314 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6319 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6320 es
? "enable" : "disable", uri
);
6322 g_object_set(G_OBJECT(t
->settings
),
6323 "enable-scripts", es
, (char *)NULL
);
6324 g_object_set(G_OBJECT(t
->settings
),
6325 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6326 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6328 button_set_stockid(t
->js_toggle
,
6329 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6333 color_address_bar(gpointer p
)
6336 struct tab
*tt
, *t
= p
;
6337 gchar
*col_str
= XT_COLOR_YELLOW
;
6339 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6341 /* make sure t still exists */
6344 TAILQ_FOREACH(tt
, &tabs
, entry
)
6350 switch (load_compare_cert(t
, NULL
)) {
6352 col_str
= XT_COLOR_BLUE
;
6355 col_str
= XT_COLOR_GREEN
;
6357 case CERT_UNTRUSTED
:
6358 col_str
= XT_COLOR_YELLOW
;
6361 col_str
= XT_COLOR_RED
;
6365 gdk_color_parse(col_str
, &color
);
6366 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6368 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6369 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6371 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6375 return (FALSE
/* kill thread */);
6379 show_ca_status(struct tab
*t
, const char *uri
)
6382 gchar
*col_str
= XT_COLOR_WHITE
;
6384 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6385 ssl_strict_certs
, ssl_ca_file
, uri
);
6392 if (ssl_ca_file
== NULL
) {
6393 if (g_str_has_prefix(uri
, "http://"))
6395 if (g_str_has_prefix(uri
, "https://")) {
6396 col_str
= XT_COLOR_RED
;
6401 if (g_str_has_prefix(uri
, "http://") ||
6402 !g_str_has_prefix(uri
, "https://"))
6405 /* thread the coloring of the address bar */
6406 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
,
6407 color_address_bar
, t
, NULL
);
6412 gdk_color_parse(col_str
, &color
);
6413 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6415 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6416 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6418 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6423 free_favicon(struct tab
*t
)
6425 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6426 __func__
, t
->icon_download
, t
->icon_request
);
6428 if (t
->icon_request
)
6429 g_object_unref(t
->icon_request
);
6430 if (t
->icon_dest_uri
)
6431 g_free(t
->icon_dest_uri
);
6433 t
->icon_request
= NULL
;
6434 t
->icon_dest_uri
= NULL
;
6438 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6440 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6441 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6443 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6444 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6446 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6447 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6451 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6453 GdkPixbuf
*pb_scaled
;
6455 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6456 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6457 GDK_INTERP_BILINEAR
);
6461 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6462 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6464 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6465 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6467 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6468 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6470 if (pb_scaled
!= pb
)
6471 g_object_unref(pb_scaled
);
6475 xt_icon_from_file(struct tab
*t
, char *file
)
6479 if (g_str_has_prefix(file
, "file://"))
6480 file
+= strlen("file://");
6482 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6484 xt_icon_from_pixbuf(t
, pb
);
6487 xt_icon_from_name(t
, "text-html");
6491 is_valid_icon(char *file
)
6494 const char *mime_type
;
6498 gf
= g_file_new_for_path(file
);
6499 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6501 mime_type
= g_file_info_get_content_type(fi
);
6502 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6503 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6504 g_strcmp0(mime_type
, "image/png") == 0 ||
6505 g_strcmp0(mime_type
, "image/gif") == 0 ||
6506 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6514 set_favicon_from_file(struct tab
*t
, char *file
)
6518 if (t
== NULL
|| file
== NULL
)
6521 if (g_str_has_prefix(file
, "file://"))
6522 file
+= strlen("file://");
6523 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6525 if (!stat(file
, &sb
)) {
6526 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6527 /* corrupt icon so trash it */
6528 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6531 /* no need to set icon to default here */
6535 xt_icon_from_file(t
, file
);
6539 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6542 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6543 struct tab
*tt
= NULL
, *t
= NULL
;
6546 * find the webview instead of passing in the tab as it could have been
6547 * deleted from underneath us.
6549 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6558 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6559 __func__
, t
->tab_id
, status
);
6562 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6564 t
->icon_download
= NULL
;
6567 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6570 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6573 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6575 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6576 __func__
, t
->tab_id
);
6577 t
->icon_download
= NULL
;
6580 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6583 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6584 __func__
, t
->icon_dest_uri
);
6585 set_favicon_from_file(t
, t
->icon_dest_uri
);
6586 /* these will be freed post callback */
6587 t
->icon_request
= NULL
;
6588 t
->icon_download
= NULL
;
6596 abort_favicon_download(struct tab
*t
)
6598 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6600 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6601 if (t
->icon_download
) {
6602 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6603 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6604 webkit_download_cancel(t
->icon_download
);
6605 t
->icon_download
= NULL
;
6610 xt_icon_from_name(t
, "text-html");
6614 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6616 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6618 if (uri
== NULL
|| t
== NULL
)
6621 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6622 /* take icon from WebKitIconDatabase */
6625 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6627 xt_icon_from_pixbuf(t
, pb
);
6630 xt_icon_from_name(t
, "text-html");
6631 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6632 /* download icon to cache dir */
6633 gchar
*name_hash
, file
[PATH_MAX
];
6636 if (t
->icon_request
) {
6637 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6641 /* check to see if we got the icon in cache */
6642 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6643 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6646 if (!stat(file
, &sb
)) {
6647 if (sb
.st_size
> 0) {
6648 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6650 set_favicon_from_file(t
, file
);
6654 /* corrupt icon so trash it */
6655 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6660 /* create download for icon */
6661 t
->icon_request
= webkit_network_request_new(uri
);
6662 if (t
->icon_request
== NULL
) {
6663 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6668 t
->icon_download
= webkit_download_new(t
->icon_request
);
6669 if (t
->icon_download
== NULL
)
6672 /* we have to free icon_dest_uri later */
6673 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6674 webkit_download_set_destination_uri(t
->icon_download
,
6677 if (webkit_download_get_status(t
->icon_download
) ==
6678 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6679 g_object_unref(t
->icon_request
);
6680 g_free(t
->icon_dest_uri
);
6681 t
->icon_request
= NULL
;
6682 t
->icon_dest_uri
= NULL
;
6686 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6687 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6689 webkit_download_start(t
->icon_download
);
6694 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6696 const gchar
*uri
= NULL
, *title
= NULL
;
6697 struct history
*h
, find
;
6701 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6702 webkit_web_view_get_load_status(wview
),
6703 get_uri(t
) ? get_uri(t
) : "NOTHING");
6706 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6710 switch (webkit_web_view_get_load_status(wview
)) {
6711 case WEBKIT_LOAD_PROVISIONAL
:
6713 abort_favicon_download(t
);
6714 #if GTK_CHECK_VERSION(2, 20, 0)
6715 gtk_widget_show(t
->spinner
);
6716 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6718 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6720 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6722 /* assume we are a new address */
6723 gdk_color_parse("white", &color
);
6724 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6725 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6727 /* take focus if we are visible */
6733 case WEBKIT_LOAD_COMMITTED
:
6738 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6744 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6746 /* check if js white listing is enabled */
6747 if (enable_cookie_whitelist
)
6748 check_and_set_cookie(uri
, t
);
6749 if (enable_js_whitelist
)
6750 check_and_set_js(uri
, t
);
6756 /* we know enough to autosave the session */
6757 if (session_autosave
) {
6762 show_ca_status(t
, uri
);
6765 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6769 case WEBKIT_LOAD_FINISHED
:
6775 if (!strncmp(uri
, "http://", strlen("http://")) ||
6776 !strncmp(uri
, "https://", strlen("https://")) ||
6777 !strncmp(uri
, "file://", strlen("file://"))) {
6779 h
= RB_FIND(history_list
, &hl
, &find
);
6781 title
= get_title(t
, FALSE
);
6782 h
= g_malloc(sizeof *h
);
6783 h
->uri
= g_strdup(uri
);
6784 h
->title
= g_strdup(title
);
6785 RB_INSERT(history_list
, &hl
, h
);
6786 completion_add_uri(h
->uri
);
6787 update_history_tabs(NULL
);
6791 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6792 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6793 case WEBKIT_LOAD_FAILED
:
6796 #if GTK_CHECK_VERSION(2, 20, 0)
6797 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6798 gtk_widget_hide(t
->spinner
);
6801 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6806 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6808 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6809 can_go_back_for_real(t
));
6811 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6812 can_go_forward_for_real(t
));
6817 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6818 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6821 * XXX this function is wrong
6822 * it overwrites perfectly good urls with garbage on load errors
6823 * those happen often when popups fail to resolve dns
6827 t
->tmp_uri
= g_strdup(uri
);
6828 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6829 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6830 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6831 set_status(t
, uri
, XT_STATUS_NOTHING
);
6838 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6840 const gchar
*title
= NULL
, *win_title
= NULL
;
6842 title
= get_title(t
, FALSE
);
6843 win_title
= get_title(t
, TRUE
);
6844 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6845 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6846 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6847 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6851 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6853 run_script(t
, JS_HINTING
);
6857 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6859 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6860 progress
== 100 ? 0 : (double)progress
/ 100);
6861 if (show_url
== 0) {
6862 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6863 progress
== 100 ? 0 : (double)progress
/ 100);
6866 update_statusbar_position(NULL
, NULL
);
6870 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6871 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6872 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6875 WebKitWebNavigationReason reason
;
6876 struct domain
*d
= NULL
;
6879 show_oops(NULL
, "webview_npd_cb invalid parameters");
6883 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6885 webkit_network_request_get_uri(request
));
6887 uri
= (char *)webkit_network_request_get_uri(request
);
6889 /* if this is an xtp url, we don't load anything else */
6890 if (parse_xtp_url(t
, uri
))
6893 if (t
->ctrl_click
) {
6895 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6896 webkit_web_policy_decision_ignore(pd
);
6897 return (TRUE
); /* we made the decission */
6901 * This is a little hairy but it comes down to this:
6902 * when we run in whitelist mode we have to assist the browser in
6903 * opening the URL that it would have opened in a new tab.
6905 reason
= webkit_web_navigation_action_get_reason(na
);
6906 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6907 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6908 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6909 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6911 webkit_web_policy_decision_use(pd
);
6912 return (TRUE
); /* we made the decision */
6919 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6922 struct domain
*d
= NULL
;
6924 WebKitWebView
*webview
= NULL
;
6926 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6927 webkit_web_view_get_uri(wv
));
6930 /* open in current tab */
6932 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6933 uri
= webkit_web_view_get_uri(wv
);
6934 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6937 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6939 } else if (enable_scripts
== 1) {
6940 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6948 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6951 struct domain
*d
= NULL
;
6953 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6955 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6956 uri
= webkit_web_view_get_uri(wv
);
6957 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6961 } else if (enable_scripts
== 1)
6968 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6970 /* we can not eat the event without throwing gtk off so defer it */
6972 /* catch middle click */
6973 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6978 /* catch ctrl click */
6979 if (e
->type
== GDK_BUTTON_RELEASE
&&
6980 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6985 return (XT_CB_PASSTHROUGH
);
6989 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6991 struct mime_type
*m
;
6993 m
= find_mime_type(mime_type
);
7001 show_oops(t
, "can't fork mime handler");
7011 execlp(m
->mt_action
, m
->mt_action
,
7012 webkit_network_request_get_uri(request
), (void *)NULL
);
7021 get_mime_type(char *file
)
7023 const char *mime_type
;
7027 if (g_str_has_prefix(file
, "file://"))
7028 file
+= strlen("file://");
7030 gf
= g_file_new_for_path(file
);
7031 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7033 mime_type
= g_file_info_get_content_type(fi
);
7041 run_download_mimehandler(char *mime_type
, char *file
)
7043 struct mime_type
*m
;
7045 m
= find_mime_type(mime_type
);
7051 show_oops(NULL
, "can't fork download mime handler");
7061 if (g_str_has_prefix(file
, "file://"))
7062 file
+= strlen("file://");
7063 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7072 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7075 WebKitDownloadStatus status
;
7076 const gchar
*file
= NULL
, *mime
= NULL
;
7078 if (download
== NULL
)
7080 status
= webkit_download_get_status(download
);
7081 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7084 file
= webkit_download_get_destination_uri(download
);
7087 mime
= get_mime_type((char *)file
);
7091 run_download_mimehandler((char *)mime
, (char *)file
);
7095 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7096 WebKitNetworkRequest
*request
, char *mime_type
,
7097 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7100 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7104 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7105 t
->tab_id
, mime_type
);
7107 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7108 webkit_web_policy_decision_ignore(decision
);
7113 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7114 webkit_web_policy_decision_download(decision
);
7122 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7126 const gchar
*suggested_name
;
7127 gchar
*filename
= NULL
;
7129 struct download
*download_entry
;
7132 if (wk_download
== NULL
|| t
== NULL
) {
7133 show_oops(NULL
, "%s invalid parameters", __func__
);
7137 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7138 if (suggested_name
== NULL
)
7139 return (FALSE
); /* abort download */
7150 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7152 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7153 filename
: suggested_name
);
7155 } while (!stat(uri
+ strlen("file://"), &sb
));
7157 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7158 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7160 webkit_download_set_destination_uri(wk_download
, uri
);
7162 if (webkit_download_get_status(wk_download
) ==
7163 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7164 show_oops(t
, "%s: download failed to start", __func__
);
7166 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7168 /* connect "download first" mime handler */
7169 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7170 G_CALLBACK(download_status_changed_cb
), NULL
);
7172 download_entry
= g_malloc(sizeof(struct download
));
7173 download_entry
->download
= wk_download
;
7174 download_entry
->tab
= t
;
7175 download_entry
->id
= next_download_id
++;
7176 RB_INSERT(download_list
, &downloads
, download_entry
);
7177 /* get from history */
7178 g_object_ref(wk_download
);
7179 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7180 show_oops(t
, "Download of '%s' started...",
7181 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7190 /* sync other download manager tabs */
7191 update_download_tabs(NULL
);
7194 * NOTE: never redirect/render the current tab before this
7195 * function returns. This will cause the download to never start.
7197 return (ret
); /* start download */
7201 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7203 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7206 show_oops(NULL
, "webview_hover_cb");
7211 set_status(t
, uri
, XT_STATUS_LINK
);
7214 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7219 mark(struct tab
*t
, struct karg
*arg
)
7225 if ((index
= marktoindex(mark
)) == -1)
7228 if (arg
->i
== XT_MARK_SET
)
7229 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7230 else if (arg
->i
== XT_MARK_GOTO
) {
7231 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7232 show_oops(t
, "mark '%c' does not exist", mark
);
7235 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7236 something changes the document size */
7237 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7244 marks_clear(struct tab
*t
)
7248 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7249 t
->mark
[i
] = XT_INVALID_MARK
;
7255 char file
[PATH_MAX
];
7256 char *line
= NULL
, *p
;
7261 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7262 if ((f
= fopen(file
, "r+")) == NULL
) {
7263 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7267 for (i
= 1; ; i
++) {
7268 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7269 if (feof(f
) || ferror(f
))
7271 if (strlen(line
) == 0 || line
[0] == '#') {
7277 p
= strtok(line
, " \t");
7279 if (p
== NULL
|| strlen(p
) != 1 ||
7280 (index
= marktoindex(*p
)) == -1) {
7281 warnx("corrupt quickmarks file, line %d", i
);
7285 p
= strtok(NULL
, " \t");
7286 if (qmarks
[index
] != NULL
)
7287 g_free(qmarks
[index
]);
7288 qmarks
[index
] = g_strdup(p
);
7299 char file
[PATH_MAX
];
7303 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7304 if ((f
= fopen(file
, "r+")) == NULL
) {
7305 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7309 for (i
= 0; i
< XT_NOMARKS
; i
++)
7310 if (qmarks
[i
] != NULL
)
7311 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7319 qmark(struct tab
*t
, struct karg
*arg
)
7324 mark
= arg
->s
[strlen(arg
->s
)-1];
7325 index
= marktoindex(mark
);
7331 if (qmarks
[index
] != NULL
)
7332 g_free(qmarks
[index
]);
7334 qmarks_load(); /* sync if multiple instances */
7335 qmarks
[index
] = g_strdup(get_uri(t
));
7339 if (qmarks
[index
] != NULL
)
7340 load_uri(t
, qmarks
[index
]);
7342 show_oops(t
, "quickmark \"%c\" does not exist",
7348 if (qmarks
[index
] != NULL
)
7349 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7351 show_oops(t
, "quickmark \"%c\" does not exist",
7362 go_up(struct tab
*t
, struct karg
*args
)
7368 levels
= atoi(args
->s
);
7372 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7373 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7375 tmp
+= strlen(XT_PROTO_DELIM
);
7377 /* if an uri starts with a slash, leave it alone (for file:///) */
7384 p
= strrchr(tmp
, '/');
7398 gototab(struct tab
*t
, struct karg
*args
)
7401 struct karg arg
= {0, NULL
, -1};
7403 tab
= atoi(args
->s
);
7405 arg
.i
= XT_TAB_NEXT
;
7414 zoom_amount(struct tab
*t
, struct karg
*arg
)
7416 struct karg narg
= {0, NULL
, -1};
7418 narg
.i
= atoi(arg
->s
);
7419 resizetab(t
, &narg
);
7425 flip_colon(struct tab
*t
, struct karg
*arg
)
7427 struct karg narg
= {0, NULL
, -1};
7430 if (t
== NULL
|| arg
== NULL
)
7433 p
= strstr(arg
->s
, ":");
7445 /* buffer commands receive the regex that triggered them in arg.s */
7446 char bcmd
[XT_BUFCMD_SZ
];
7450 #define XT_PRE_NO (0)
7451 #define XT_PRE_YES (1)
7452 #define XT_PRE_MAYBE (2)
7454 int (*func
)(struct tab
*, struct karg
*);
7458 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7459 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7460 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7461 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7462 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7463 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7464 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7465 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7466 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7467 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7468 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7469 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7470 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7471 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7472 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7473 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7474 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7475 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7479 buffercmd_init(void)
7483 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7484 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7485 REG_EXTENDED
| REG_NOSUB
))
7486 startpage_add("invalid buffercmd regex %s",
7487 buffercmds
[i
].regex
);
7491 buffercmd_abort(struct tab
*t
)
7495 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7496 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7499 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7500 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7504 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7506 struct karg arg
= {0, NULL
, -1};
7509 arg
.s
= g_strdup(bcmd
);
7511 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7512 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7522 buffercmd_addkey(struct tab
*t
, guint keyval
)
7525 char s
[XT_BUFCMD_SZ
];
7527 if (keyval
== GDK_Escape
) {
7529 return (XT_CB_HANDLED
);
7532 /* key with modifier or non-ascii character */
7533 if (!isascii(keyval
))
7534 return (XT_CB_PASSTHROUGH
);
7536 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7537 "to buffer \"%s\"\n", keyval
, bcmd
);
7539 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7540 if (bcmd
[i
] == '\0') {
7545 /* buffer full, ignore input */
7546 if (i
>= LENGTH(bcmd
) -1) {
7547 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7549 return (XT_CB_HANDLED
);
7552 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7554 /* find exact match */
7555 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7556 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7557 (size_t) 0, NULL
, 0) == 0) {
7558 buffercmd_execute(t
, &buffercmds
[i
]);
7562 /* find non exact matches to see if we need to abort ot not */
7563 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7564 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7567 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7568 if (isdigit(bcmd
[0])) {
7569 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7573 if (sscanf(bcmd
, "%s", s
) == 0)
7576 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7577 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7580 if (sscanf(bcmd
, "%s", s
) == 0)
7583 if (c
== -1 && buffercmds
[i
].precount
)
7585 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7588 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7589 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7592 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7597 return (XT_CB_HANDLED
);
7601 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7603 struct key_binding
*k
;
7605 /* handle keybindings if buffercmd is empty.
7606 if not empty, allow commands like C-n */
7607 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7608 TAILQ_FOREACH(k
, &kbl
, entry
)
7609 if (e
->keyval
== k
->key
7610 && (entry
? k
->use_in_entry
: 1)) {
7612 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7613 return (cmd_execute(t
, k
->cmd
));
7614 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7615 return (cmd_execute(t
, k
->cmd
));
7619 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7620 return buffercmd_addkey(t
, e
->keyval
);
7622 return (XT_CB_PASSTHROUGH
);
7626 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7628 char s
[2], buf
[128];
7629 const char *errstr
= NULL
;
7631 /* don't use w directly; use t->whatever instead */
7634 show_oops(NULL
, "wv_keypress_after_cb");
7635 return (XT_CB_PASSTHROUGH
);
7638 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7639 e
->keyval
, e
->state
, t
);
7643 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7645 return (XT_CB_HANDLED
);
7649 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7651 /* we have a string */
7653 /* we have a number */
7654 snprintf(buf
, sizeof buf
,
7655 "vimprobable_fire(%s)", t
->hint_num
);
7662 /* XXX unfuck this */
7663 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7664 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7665 /* last input was numerical */
7667 l
= strlen(t
->hint_num
);
7674 t
->hint_num
[l
] = '\0';
7678 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7679 /* last input was alphanumerical */
7681 l
= strlen(t
->hint_buf
);
7688 t
->hint_buf
[l
] = '\0';
7698 /* numerical input */
7699 if (CLEAN(e
->state
) == 0 &&
7700 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7701 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7702 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7703 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7704 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7708 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7709 "invalid link number\n");
7712 snprintf(buf
, sizeof buf
,
7713 "vimprobable_update_hints(%s)",
7715 t
->hint_mode
= XT_HINT_NUMERICAL
;
7719 /* empty the counter buffer */
7720 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7721 return (XT_CB_HANDLED
);
7724 /* alphanumerical input */
7725 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7726 e
->keyval
<= GDK_z
) ||
7727 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7728 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7729 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7730 e
->keyval
<= GDK_9
) ||
7731 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7732 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7733 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7734 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7735 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7736 " %s\n", t
->hint_buf
);
7738 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7741 snprintf(buf
, sizeof buf
,
7742 "vimprobable_show_hints('%s')", t
->hint_buf
);
7743 t
->hint_mode
= XT_HINT_ALPHANUM
;
7746 /* empty the counter buffer */
7747 bzero(t
->hint_num
, sizeof t
->hint_num
);
7748 return (XT_CB_HANDLED
);
7751 return (XT_CB_HANDLED
);
7754 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7755 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7756 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7759 return (handle_keypress(t
, e
, 0));
7763 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7767 /* Hide buffers, if they are visible, with escape. */
7768 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7769 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7770 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7772 return (XT_CB_HANDLED
);
7775 return (XT_CB_PASSTHROUGH
);
7779 search_continue(struct tab
*t
)
7781 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7782 gboolean rv
= FALSE
;
7786 if (strlen(c
) == 1) {
7787 webkit_web_view_unmark_text_matches(t
->wv
);
7792 t
->search_forward
= TRUE
;
7793 else if (c
[0] == '?')
7794 t
->search_forward
= FALSE
;
7804 search_cb(struct tab
*t
)
7806 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7809 if (search_continue(t
) == FALSE
)
7813 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7815 /* not found, mark red */
7816 gdk_color_parse(XT_COLOR_RED
, &color
);
7817 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7818 /* unmark and remove selection */
7819 webkit_web_view_unmark_text_matches(t
->wv
);
7820 /* my kingdom for a way to unselect text in webview */
7822 /* found, highlight all */
7823 webkit_web_view_unmark_text_matches(t
->wv
);
7824 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7825 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7826 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7827 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7835 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7837 const gchar
*c
= gtk_entry_get_text(w
);
7840 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7841 return (XT_CB_PASSTHROUGH
);
7844 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7845 e
->keyval
, e
->state
, t
);
7847 if (search_continue(t
) == FALSE
)
7850 /* if search length is > 4 then no longer play timeout games */
7851 if (strlen(c
) > 4) {
7853 g_source_remove(t
->search_id
);
7860 /* reestablish a new timer if the user types fast */
7862 g_source_remove(t
->search_id
);
7863 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7866 return (XT_CB_PASSTHROUGH
);
7870 match_uri(const gchar
*uri
, const gchar
*key
) {
7873 gboolean match
= FALSE
;
7877 if (!strncmp(key
, uri
, len
))
7880 voffset
= strstr(uri
, "/") + 2;
7881 if (!strncmp(key
, voffset
, len
))
7883 else if (g_str_has_prefix(voffset
, "www.")) {
7884 voffset
= voffset
+ strlen("www.");
7885 if (!strncmp(key
, voffset
, len
))
7894 match_session(const gchar
*name
, const gchar
*key
) {
7897 sub
= strcasestr(name
, key
);
7903 cmd_getlist(int id
, char *key
)
7910 if (cmds
[id
].type
& XT_URLARG
) {
7911 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7912 if (match_uri(h
->uri
, key
)) {
7913 cmd_status
.list
[c
] = (char *)h
->uri
;
7919 } else if (cmds
[id
].type
& XT_SESSARG
) {
7920 TAILQ_FOREACH(s
, &sessions
, entry
)
7921 if (match_session(s
->name
, key
)) {
7922 cmd_status
.list
[c
] = (char *)s
->name
;
7928 } else if (cmds
[id
].type
& XT_SETARG
) {
7929 for (i
= 0; i
< LENGTH(rs
); i
++)
7930 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
7931 cmd_status
.list
[c
++] = rs
[i
].name
;
7937 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7939 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7940 if (cmds
[i
].level
< dep
)
7942 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7944 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7952 cmd_getnext(int dir
)
7954 cmd_status
.index
+= dir
;
7956 if (cmd_status
.index
< 0)
7957 cmd_status
.index
= cmd_status
.len
- 1;
7958 else if (cmd_status
.index
>= cmd_status
.len
)
7959 cmd_status
.index
= 0;
7961 return cmd_status
.list
[cmd_status
.index
];
7965 cmd_tokenize(char *s
, char *tokens
[])
7969 size_t len
= strlen(s
);
7972 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7973 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7974 tok
= strtok_r(NULL
, " ", &last
), i
++)
7984 cmd_complete(struct tab
*t
, char *str
, int dir
)
7986 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7987 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7989 char *tok
, *match
, *s
= g_strdup(str
);
7991 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7994 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7997 for (i
= 0; isdigit(s
[i
]); i
++)
8000 for (; isspace(s
[i
]); i
++)
8005 levels
= cmd_tokenize(s
, tokens
);
8007 for (i
= 0; i
< levels
- 1; i
++) {
8010 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8011 if (cmds
[j
].level
< dep
)
8013 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8017 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8024 if (matchcount
== 1) {
8025 strlcat(res
, tok
, sizeof res
);
8026 strlcat(res
, " ", sizeof res
);
8036 if (cmd_status
.index
== -1)
8037 cmd_getlist(parent
, tokens
[i
]);
8039 if (cmd_status
.len
> 0) {
8040 match
= cmd_getnext(dir
);
8041 strlcat(res
, match
, sizeof res
);
8042 gtk_entry_set_text(w
, res
);
8043 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8050 cmd_execute(struct tab
*t
, char *str
)
8052 struct cmd
*cmd
= NULL
;
8053 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
8055 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8056 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8057 struct karg arg
= {0, NULL
, -1};
8062 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8068 while (isspace(s
[0]))
8071 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8072 prefix
= atoi(prefixstr
);
8076 for (tok
= strtok_r(s
, " ", &last
); tok
;
8077 tok
= strtok_r(NULL
, " ", &last
)) {
8079 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8080 if (cmds
[j
].level
< dep
)
8082 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8084 if (cmds
[j
].level
== dep
&&
8085 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8089 if (len
== strlen(cmds
[j
].cmd
)) {
8095 if (matchcount
== 1) {
8100 show_oops(t
, "Invalid command: %s", str
);
8108 arg
.precount
= prefix
;
8109 else if (cmd_prefix
> 0)
8110 arg
.precount
= cmd_prefix
;
8112 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8113 show_oops(t
, "No prefix allowed: %s", str
);
8117 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8118 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8119 arg
.precount
= atoi(arg
.s
);
8120 if (arg
.precount
<= 0) {
8121 if (arg
.s
[0] == '0')
8122 show_oops(t
, "Zero count");
8124 show_oops(t
, "Trailing characters");
8129 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8130 __func__
, arg
.precount
, arg
.s
);
8146 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8149 show_oops(NULL
, "entry_key_cb invalid parameters");
8150 return (XT_CB_PASSTHROUGH
);
8153 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8154 e
->keyval
, e
->state
, t
);
8158 if (e
->keyval
== GDK_Escape
) {
8159 /* don't use focus_webview(t) because we want to type :cmds */
8160 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8163 return (handle_keypress(t
, e
, 1));
8166 struct command_entry
*
8167 history_prev(struct command_list
*l
, struct command_entry
*at
)
8170 at
= TAILQ_LAST(l
, command_list
);
8172 at
= TAILQ_PREV(at
, command_list
, entry
);
8174 at
= TAILQ_LAST(l
, command_list
);
8180 struct command_entry
*
8181 history_next(struct command_list
*l
, struct command_entry
*at
)
8184 at
= TAILQ_FIRST(l
);
8186 at
= TAILQ_NEXT(at
, entry
);
8188 at
= TAILQ_FIRST(l
);
8195 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8197 int rv
= XT_CB_HANDLED
;
8198 const gchar
*c
= gtk_entry_get_text(w
);
8201 show_oops(NULL
, "cmd_keypress_cb parameters");
8202 return (XT_CB_PASSTHROUGH
);
8205 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8206 e
->keyval
, e
->state
, t
);
8210 e
->keyval
= GDK_Escape
;
8211 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8212 e
->keyval
= GDK_Escape
;
8214 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8215 e
->keyval
!= GDK_ISO_Left_Tab
)
8216 cmd_status
.index
= -1;
8218 switch (e
->keyval
) {
8221 cmd_complete(t
, (char *)&c
[1], 1);
8223 case GDK_ISO_Left_Tab
:
8225 cmd_complete(t
, (char *)&c
[1], -1);
8230 if ((search_at
= history_next(&shl
, search_at
))) {
8231 search_at
->line
[0] = c
[0];
8232 gtk_entry_set_text(w
, search_at
->line
);
8233 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8236 if ((history_at
= history_prev(&chl
, history_at
))) {
8237 history_at
->line
[0] = c
[0];
8238 gtk_entry_set_text(w
, history_at
->line
);
8239 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8246 if ((search_at
= history_next(&shl
, search_at
))) {
8247 search_at
->line
[0] = c
[0];
8248 gtk_entry_set_text(w
, search_at
->line
);
8249 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8252 if ((history_at
= history_next(&chl
, history_at
))) {
8253 history_at
->line
[0] = c
[0];
8254 gtk_entry_set_text(w
, history_at
->line
);
8255 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8261 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8269 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8270 webkit_web_view_unmark_text_matches(t
->wv
);
8274 rv
= XT_CB_PASSTHROUGH
;
8280 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8282 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8286 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8288 /* popup menu enabled */
8293 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8296 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8297 return (XT_CB_PASSTHROUGH
);
8300 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8301 t
->tab_id
, t
->popup
);
8303 /* if popup is enabled don't lose focus */
8306 return (XT_CB_PASSTHROUGH
);
8312 if (show_url
== 0 || t
->focus_wv
)
8315 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8317 return (XT_CB_PASSTHROUGH
);
8321 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8324 const gchar
*c
= gtk_entry_get_text(entry
);
8327 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8331 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8338 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8344 if (c
[0] == '/' || c
[0] == '?') {
8345 /* see if there is a timer pending */
8347 g_source_remove(t
->search_id
);
8352 if (t
->search_text
) {
8353 g_free(t
->search_text
);
8354 t
->search_text
= NULL
;
8357 t
->search_text
= g_strdup(s
);
8359 g_free(global_search
);
8360 global_search
= g_strdup(s
);
8361 t
->search_forward
= c
[0] == '/';
8363 history_add(&shl
, search_file
, s
, &search_history_count
);
8367 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8374 backward_cb(GtkWidget
*w
, struct tab
*t
)
8379 show_oops(NULL
, "backward_cb invalid parameters");
8383 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8390 forward_cb(GtkWidget
*w
, struct tab
*t
)
8395 show_oops(NULL
, "forward_cb invalid parameters");
8399 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8401 a
.i
= XT_NAV_FORWARD
;
8406 home_cb(GtkWidget
*w
, struct tab
*t
)
8409 show_oops(NULL
, "home_cb invalid parameters");
8413 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8419 stop_cb(GtkWidget
*w
, struct tab
*t
)
8421 WebKitWebFrame
*frame
;
8424 show_oops(NULL
, "stop_cb invalid parameters");
8428 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8430 frame
= webkit_web_view_get_main_frame(t
->wv
);
8431 if (frame
== NULL
) {
8432 show_oops(t
, "stop_cb: no frame");
8436 webkit_web_frame_stop_loading(frame
);
8437 abort_favicon_download(t
);
8441 setup_webkit(struct tab
*t
)
8443 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8444 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8445 FALSE
, (char *)NULL
);
8447 warnx("webkit does not have \"enable-dns-prefetching\" property");
8448 g_object_set(G_OBJECT(t
->settings
),
8449 "user-agent", t
->user_agent
, (char *)NULL
);
8450 g_object_set(G_OBJECT(t
->settings
),
8451 "enable-scripts", enable_scripts
, (char *)NULL
);
8452 g_object_set(G_OBJECT(t
->settings
),
8453 "enable-plugins", enable_plugins
, (char *)NULL
);
8454 g_object_set(G_OBJECT(t
->settings
),
8455 "javascript-can-open-windows-automatically", enable_scripts
,
8457 g_object_set(G_OBJECT(t
->settings
),
8458 "enable-html5-database", FALSE
, (char *)NULL
);
8459 g_object_set(G_OBJECT(t
->settings
),
8460 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8461 g_object_set(G_OBJECT(t
->settings
),
8462 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8463 g_object_set(G_OBJECT(t
->settings
),
8464 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8465 g_object_set(G_OBJECT(t
->wv
),
8466 "full-content-zoom", TRUE
, (char *)NULL
);
8468 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8472 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8474 struct tab
*ti
, *t
= NULL
;
8475 gdouble view_size
, value
, max
;
8478 TAILQ_FOREACH(ti
, &tabs
, entry
)
8479 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8487 if (adjustment
== NULL
)
8488 adjustment
= gtk_scrolled_window_get_vadjustment(
8489 GTK_SCROLLED_WINDOW(t
->browser_win
));
8491 view_size
= gtk_adjustment_get_page_size(adjustment
);
8492 value
= gtk_adjustment_get_value(adjustment
);
8493 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8496 position
= g_strdup("All");
8497 else if (value
== max
)
8498 position
= g_strdup("Bot");
8499 else if (value
== 0)
8500 position
= g_strdup("Top");
8502 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8504 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8511 create_browser(struct tab
*t
)
8515 GtkAdjustment
*adjustment
;
8518 show_oops(NULL
, "create_browser invalid parameters");
8522 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8523 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8524 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8525 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8527 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8528 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8529 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8531 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8532 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8535 t
->settings
= webkit_web_settings_new();
8537 if (user_agent
== NULL
) {
8538 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8540 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8543 t
->user_agent
= g_strdup(user_agent
);
8545 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8548 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8549 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8550 G_CALLBACK(update_statusbar_position
), NULL
);
8562 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8563 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8564 gtk_widget_set_name(w
, "xxxterm");
8565 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8566 g_signal_connect(G_OBJECT(w
), "delete_event",
8567 G_CALLBACK (gtk_main_quit
), NULL
);
8573 create_kiosk_toolbar(struct tab
*t
)
8575 GtkWidget
*toolbar
= NULL
, *b
;
8577 b
= gtk_hbox_new(FALSE
, 0);
8579 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8581 /* backward button */
8582 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8583 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8584 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8585 G_CALLBACK(backward_cb
), t
);
8586 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8588 /* forward button */
8589 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8590 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8591 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8592 G_CALLBACK(forward_cb
), t
);
8593 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8596 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8597 gtk_widget_set_sensitive(t
->gohome
, true);
8598 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8599 G_CALLBACK(home_cb
), t
);
8600 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8602 /* create widgets but don't use them */
8603 t
->uri_entry
= gtk_entry_new();
8604 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8605 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8606 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8612 create_toolbar(struct tab
*t
)
8614 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8616 b
= gtk_hbox_new(FALSE
, 0);
8618 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8620 /* backward button */
8621 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8622 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8623 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8624 G_CALLBACK(backward_cb
), t
);
8625 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8627 /* forward button */
8628 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8629 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8630 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8631 G_CALLBACK(forward_cb
), t
);
8632 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8636 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8637 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8638 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8639 G_CALLBACK(stop_cb
), t
);
8640 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8644 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8645 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8646 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8647 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8648 G_CALLBACK(js_toggle_cb
), t
);
8649 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8651 t
->uri_entry
= gtk_entry_new();
8652 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8653 G_CALLBACK(activate_uri_entry_cb
), t
);
8654 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8655 G_CALLBACK(entry_key_cb
), t
);
8657 eb1
= gtk_hbox_new(FALSE
, 0);
8658 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8659 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8660 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8663 if (search_string
) {
8665 t
->search_entry
= gtk_entry_new();
8666 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8667 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8668 G_CALLBACK(activate_search_entry_cb
), t
);
8669 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8670 G_CALLBACK(entry_key_cb
), t
);
8671 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8672 eb2
= gtk_hbox_new(FALSE
, 0);
8673 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8674 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8676 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8683 create_buffers(struct tab
*t
)
8685 GtkCellRenderer
*renderer
;
8688 view
= gtk_tree_view_new();
8690 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8692 renderer
= gtk_cell_renderer_text_new();
8693 gtk_tree_view_insert_column_with_attributes
8694 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8696 renderer
= gtk_cell_renderer_text_new();
8697 gtk_tree_view_insert_column_with_attributes
8698 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8701 gtk_tree_view_set_model
8702 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8708 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8709 GtkTreeViewColumn
*col
, struct tab
*t
)
8714 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8716 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8719 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8720 set_current_tab(id
- 1);
8726 /* after tab reordering/creation/removal */
8733 TAILQ_FOREACH(t
, &tabs
, entry
) {
8734 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8735 if (t
->tab_id
> maxid
)
8738 gtk_widget_show(t
->tab_elems
.sep
);
8741 TAILQ_FOREACH(t
, &tabs
, entry
) {
8742 if (t
->tab_id
== maxid
) {
8743 gtk_widget_hide(t
->tab_elems
.sep
);
8749 /* after active tab change */
8751 recolor_compact_tabs(void)
8757 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8758 TAILQ_FOREACH(t
, &tabs
, entry
)
8759 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8762 curid
= gtk_notebook_get_current_page(notebook
);
8763 TAILQ_FOREACH(t
, &tabs
, entry
)
8764 if (t
->tab_id
== curid
) {
8765 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8766 gtk_widget_modify_fg(t
->tab_elems
.label
,
8767 GTK_STATE_NORMAL
, &color
);
8773 set_current_tab(int page_num
)
8775 buffercmd_abort(get_current_tab());
8776 gtk_notebook_set_current_page(notebook
, page_num
);
8777 recolor_compact_tabs();
8781 undo_close_tab_save(struct tab
*t
)
8785 struct undo
*u1
, *u2
;
8787 WebKitWebHistoryItem
*item
;
8789 if ((uri
= get_uri(t
)) == NULL
)
8792 u1
= g_malloc0(sizeof(struct undo
));
8793 u1
->uri
= g_strdup(uri
);
8795 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8797 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8798 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8801 /* forward history */
8802 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8806 u1
->history
= g_list_prepend(u1
->history
,
8807 webkit_web_history_item_copy(item
));
8808 items
= g_list_next(items
);
8813 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8814 u1
->history
= g_list_prepend(u1
->history
,
8815 webkit_web_history_item_copy(item
));
8819 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8823 u1
->history
= g_list_prepend(u1
->history
,
8824 webkit_web_history_item_copy(item
));
8825 items
= g_list_next(items
);
8828 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8830 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8831 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8832 TAILQ_REMOVE(&undos
, u2
, entry
);
8834 g_list_free(u2
->history
);
8843 delete_tab(struct tab
*t
)
8847 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8853 TAILQ_REMOVE(&tabs
, t
, entry
);
8855 /* Halt all webkit activity. */
8856 abort_favicon_download(t
);
8857 webkit_web_view_stop_loading(t
->wv
);
8859 /* Save the tab, so we can undo the close. */
8860 undo_close_tab_save(t
);
8862 if (browser_mode
== XT_BM_KIOSK
) {
8863 gtk_widget_destroy(t
->uri_entry
);
8864 gtk_widget_destroy(t
->stop
);
8865 gtk_widget_destroy(t
->js_toggle
);
8868 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8869 gtk_widget_destroy(t
->vbox
);
8873 g_source_remove(t
->search_id
);
8875 g_free(t
->user_agent
);
8876 g_free(t
->stylesheet
);
8880 if (TAILQ_EMPTY(&tabs
)) {
8881 if (browser_mode
== XT_BM_KIOSK
)
8882 create_new_tab(home
, NULL
, 1, -1);
8884 create_new_tab(NULL
, NULL
, 1, -1);
8887 /* recreate session */
8888 if (session_autosave
) {
8894 recolor_compact_tabs();
8898 update_statusbar_zoom(struct tab
*t
)
8901 char s
[16] = { '\0' };
8903 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8904 if ((zoom
<= 0.99 || zoom
>= 1.01))
8905 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8906 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8910 setzoom_webkit(struct tab
*t
, int adjust
)
8912 #define XT_ZOOMPERCENT 0.04
8917 show_oops(NULL
, "setzoom_webkit invalid parameters");
8921 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8922 if (adjust
== XT_ZOOM_IN
)
8923 zoom
+= XT_ZOOMPERCENT
;
8924 else if (adjust
== XT_ZOOM_OUT
)
8925 zoom
-= XT_ZOOMPERCENT
;
8926 else if (adjust
> 0)
8927 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8929 show_oops(t
, "setzoom_webkit invalid zoom value");
8933 if (zoom
< XT_ZOOMPERCENT
)
8934 zoom
= XT_ZOOMPERCENT
;
8935 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8936 update_statusbar_zoom(t
);
8940 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8942 struct tab
*t
= (struct tab
*) data
;
8944 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8946 switch (event
->button
) {
8948 set_current_tab(t
->tab_id
);
8959 append_tab(struct tab
*t
)
8964 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8965 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8969 create_sbe(int width
)
8973 sbe
= gtk_entry_new();
8974 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8975 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8976 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8977 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8978 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8979 gtk_widget_set_size_request(sbe
, width
, -1);
8985 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8990 WebKitWebHistoryItem
*item
;
8994 int sbe_p
= 0, sbe_b
= 0,
8997 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8999 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9000 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9004 t
= g_malloc0(sizeof *t
);
9006 if (title
== NULL
) {
9007 title
= "(untitled)";
9011 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9013 /* label + button for tab */
9014 b
= gtk_hbox_new(FALSE
, 0);
9017 #if GTK_CHECK_VERSION(2, 20, 0)
9018 t
->spinner
= gtk_spinner_new();
9020 t
->label
= gtk_label_new(title
);
9021 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9022 gtk_widget_set_size_request(t
->label
, 100, 0);
9023 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9024 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9025 gtk_widget_set_size_request(b
, 130, 0);
9027 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9028 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9029 #if GTK_CHECK_VERSION(2, 20, 0)
9030 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9034 if (browser_mode
== XT_BM_KIOSK
) {
9035 t
->toolbar
= create_kiosk_toolbar(t
);
9036 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9039 t
->toolbar
= create_toolbar(t
);
9041 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9049 t
->browser_win
= create_browser(t
);
9050 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9052 /* oops message for user feedback */
9053 t
->oops
= gtk_entry_new();
9054 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9055 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9056 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9057 gdk_color_parse(XT_COLOR_RED
, &color
);
9058 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9059 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9060 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9063 t
->cmd
= gtk_entry_new();
9064 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9065 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9066 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9067 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9070 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9072 t
->sbe
.statusbar
= gtk_entry_new();
9073 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9074 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9075 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9076 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9078 /* create these widgets only if specified in statusbar_elems */
9080 t
->sbe
.position
= create_sbe(40);
9081 t
->sbe
.zoom
= create_sbe(40);
9082 t
->sbe
.buffercmd
= create_sbe(60);
9084 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9086 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9089 /* gtk widgets cannot be added to a box twice. sbe_* variables
9090 make sure of this */
9091 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9095 GtkWidget
*sep
= gtk_vseparator_new();
9097 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9098 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9099 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9100 FALSE
, FALSE
, FALSE
);
9105 warnx("flag \"%c\" specified more than "
9106 "once in statusbar_elems\n", *p
);
9110 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9111 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9115 warnx("flag \"%c\" specified more than "
9116 "once in statusbar_elems\n", *p
);
9120 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9121 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9125 warnx("flag \"%c\" specified more than "
9126 "once in statusbar_elems\n", *p
);
9130 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9131 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9134 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9139 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9142 t
->buffers
= create_buffers(t
);
9143 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9145 /* xtp meaning is normal by default */
9146 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9148 /* set empty favicon */
9149 xt_icon_from_name(t
, "text-html");
9151 /* and show it all */
9152 gtk_widget_show_all(b
);
9153 gtk_widget_show_all(t
->vbox
);
9155 /* compact tab bar */
9156 t
->tab_elems
.label
= gtk_label_new(title
);
9157 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9158 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9159 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9160 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9162 t
->tab_elems
.eventbox
= gtk_event_box_new();
9163 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9164 t
->tab_elems
.sep
= gtk_vseparator_new();
9166 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9167 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9168 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9169 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9170 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9171 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9173 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9175 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9177 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9180 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9182 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9184 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9187 id
= position
>= 0 ? position
:
9188 gtk_notebook_get_current_page(notebook
) + 1;
9189 if (id
> gtk_notebook_get_n_pages(notebook
))
9192 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9193 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9194 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9195 t
->tab_elems
.eventbox
, id
);
9200 #if GTK_CHECK_VERSION(2, 20, 0)
9201 /* turn spinner off if we are a new tab without uri */
9203 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9204 gtk_widget_hide(t
->spinner
);
9207 /* make notebook tabs reorderable */
9208 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9210 /* compact tabs clickable */
9211 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9212 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9214 g_object_connect(G_OBJECT(t
->cmd
),
9215 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9216 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9217 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9218 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9219 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9222 /* reuse wv_button_cb to hide oops */
9223 g_object_connect(G_OBJECT(t
->oops
),
9224 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9227 g_signal_connect(t
->buffers
,
9228 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9229 g_object_connect(G_OBJECT(t
->buffers
),
9230 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9232 g_object_connect(G_OBJECT(t
->wv
),
9233 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9234 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9235 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9236 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9237 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9238 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9239 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9240 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9241 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9242 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9243 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9244 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9245 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9246 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9247 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9248 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9250 g_signal_connect(t
->wv
,
9251 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9253 * XXX this puts invalid url in uri_entry and that is undesirable
9256 g_signal_connect(t
->wv
,
9257 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
9259 g_signal_connect(t
->wv
,
9260 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9262 /* hijack the unused keys as if we were the browser */
9263 g_object_connect(G_OBJECT(t
->toolbar
),
9264 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9267 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9268 G_CALLBACK(tab_close_cb
), t
);
9271 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9272 /* restore the tab's history */
9273 if (u
&& u
->history
) {
9277 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9278 items
= g_list_next(items
);
9281 item
= g_list_nth_data(u
->history
, u
->back
);
9283 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9286 g_list_free(u
->history
);
9288 webkit_web_back_forward_list_clear(t
->bfl
);
9294 url_set_visibility();
9295 statusbar_set_visibility();
9298 set_current_tab(t
->tab_id
);
9299 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9304 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9308 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9313 recolor_compact_tabs();
9314 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9319 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9325 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9327 if (gtk_notebook_get_current_page(notebook
) == -1)
9330 TAILQ_FOREACH(t
, &tabs
, entry
) {
9331 if (t
->tab_id
== pn
) {
9332 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9335 uri
= get_title(t
, TRUE
);
9336 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9342 /* can't use focus_webview here */
9343 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9350 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9353 struct tab
*t
= NULL
, *tt
;
9357 TAILQ_FOREACH(tt
, &tabs
, entry
)
9358 if (tt
->tab_id
== pn
) {
9363 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9365 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9370 menuitem_response(struct tab
*t
)
9372 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9376 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9378 GtkWidget
*menu
, *menu_items
;
9379 GdkEventButton
*bevent
;
9383 if (event
->type
== GDK_BUTTON_PRESS
) {
9384 bevent
= (GdkEventButton
*) event
;
9385 menu
= gtk_menu_new();
9387 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9388 if ((uri
= get_uri(ti
)) == NULL
)
9389 /* XXX make sure there is something to print */
9390 /* XXX add gui pages in here to look purdy */
9392 menu_items
= gtk_menu_item_new_with_label(uri
);
9393 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9394 gtk_widget_show(menu_items
);
9396 g_signal_connect_swapped((menu_items
),
9397 "activate", G_CALLBACK(menuitem_response
),
9401 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9402 bevent
->button
, bevent
->time
);
9404 /* unref object so it'll free itself when popped down */
9405 #if !GTK_CHECK_VERSION(3, 0, 0)
9406 /* XXX does not need unref with gtk+3? */
9407 g_object_ref_sink(menu
);
9408 g_object_unref(menu
);
9411 return (TRUE
/* eat event */);
9414 return (FALSE
/* propagate */);
9418 icon_size_map(int icon_size
)
9420 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9421 icon_size
> GTK_ICON_SIZE_DIALOG
)
9422 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9428 create_button(char *name
, char *stockid
, int size
)
9430 GtkWidget
*button
, *image
;
9434 rcstring
= g_strdup_printf(
9435 "style \"%s-style\"\n"
9437 " GtkWidget::focus-padding = 0\n"
9438 " GtkWidget::focus-line-width = 0\n"
9442 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9443 gtk_rc_parse_string(rcstring
);
9445 button
= gtk_button_new();
9446 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9447 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9449 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9450 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9451 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9452 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9453 gtk_widget_set_name(button
, name
);
9454 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9460 button_set_stockid(GtkWidget
*button
, char *stockid
)
9464 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9465 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9466 gtk_button_set_image(GTK_BUTTON(button
), image
);
9470 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9473 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9476 if (xterm_workaround
== 0)
9480 * xterm doesn't play nice with clipboards because it clears the
9481 * primary when clicked. We rely on primary being set to properly
9482 * handle middle mouse button clicks (paste). So when someone clears
9483 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9484 * other application behavior (as in DON'T clear primary).
9487 p
= gtk_clipboard_wait_for_text(primary
);
9489 if (gdk_property_get(gdk_get_default_root_window(),
9491 gdk_atom_intern("STRING", FALSE
),
9493 1024 * 1024 /* picked out of my butt */,
9499 /* yes sir, we need to NUL the string */
9501 gtk_clipboard_set_text(primary
, p
, -1);
9515 char file
[PATH_MAX
];
9518 vbox
= gtk_vbox_new(FALSE
, 0);
9519 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9520 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9521 #if !GTK_CHECK_VERSION(3, 0, 0)
9522 /* XXX seems to be needed with gtk+2 */
9523 gtk_notebook_set_tab_hborder(notebook
, 0);
9524 gtk_notebook_set_tab_vborder(notebook
, 0);
9526 gtk_notebook_set_scrollable(notebook
, TRUE
);
9527 gtk_notebook_set_show_border(notebook
, FALSE
);
9528 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9530 abtn
= gtk_button_new();
9531 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9532 gtk_widget_set_size_request(arrow
, -1, -1);
9533 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9534 gtk_widget_set_size_request(abtn
, -1, 20);
9536 #if GTK_CHECK_VERSION(2, 20, 0)
9537 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9539 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9541 /* compact tab bar */
9542 tab_bar
= gtk_hbox_new(TRUE
, 0);
9544 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9545 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9546 gtk_widget_set_size_request(vbox
, -1, -1);
9548 g_object_connect(G_OBJECT(notebook
),
9549 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9551 g_object_connect(G_OBJECT(notebook
),
9552 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9553 NULL
, (char *)NULL
);
9554 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9555 G_CALLBACK(arrow_cb
), NULL
);
9557 main_window
= create_window();
9558 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9561 for (i
= 0; i
< LENGTH(icons
); i
++) {
9562 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9563 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9564 l
= g_list_append(l
, pb
);
9566 gtk_window_set_default_icon_list(l
);
9568 /* clipboard work around */
9569 if (xterm_workaround
)
9571 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9572 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9574 gtk_widget_show_all(abtn
);
9575 gtk_widget_show_all(main_window
);
9576 notebook_tab_set_visibility();
9580 set_hook(void **hook
, char *name
)
9583 errx(1, "set_hook");
9585 if (*hook
== NULL
) {
9586 *hook
= dlsym(RTLD_NEXT
, name
);
9588 errx(1, "can't hook %s", name
);
9592 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9594 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9596 g_return_val_if_fail(cookie1
, FALSE
);
9597 g_return_val_if_fail(cookie2
, FALSE
);
9599 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9600 !strcmp (cookie1
->value
, cookie2
->value
) &&
9601 !strcmp (cookie1
->path
, cookie2
->path
) &&
9602 !strcmp (cookie1
->domain
, cookie2
->domain
));
9606 transfer_cookies(void)
9609 SoupCookie
*sc
, *pc
;
9611 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9613 for (;cf
; cf
= cf
->next
) {
9615 sc
= soup_cookie_copy(pc
);
9616 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9619 soup_cookies_free(cf
);
9623 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9628 print_cookie("soup_cookie_jar_delete_cookie", c
);
9630 if (cookies_enabled
== 0)
9633 if (jar
== NULL
|| c
== NULL
)
9636 /* find and remove from persistent jar */
9637 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9639 for (;cf
; cf
= cf
->next
) {
9641 if (soup_cookie_equal(ci
, c
)) {
9642 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9647 soup_cookies_free(cf
);
9649 /* delete from session jar */
9650 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9654 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9656 struct domain
*d
= NULL
;
9660 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9661 jar
, p_cookiejar
, s_cookiejar
);
9663 if (cookies_enabled
== 0)
9666 /* see if we are up and running */
9667 if (p_cookiejar
== NULL
) {
9668 _soup_cookie_jar_add_cookie(jar
, cookie
);
9671 /* disallow p_cookiejar adds, shouldn't happen */
9672 if (jar
== p_cookiejar
)
9676 if (jar
== NULL
|| cookie
== NULL
)
9679 if (enable_cookie_whitelist
&&
9680 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9682 DNPRINTF(XT_D_COOKIE
,
9683 "soup_cookie_jar_add_cookie: reject %s\n",
9685 if (save_rejected_cookies
) {
9686 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9687 show_oops(NULL
, "can't open reject cookie file");
9690 fseek(r_cookie_f
, 0, SEEK_END
);
9691 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9692 cookie
->http_only
? "#HttpOnly_" : "",
9694 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9696 cookie
->secure
? "TRUE" : "FALSE",
9698 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9705 if (!allow_volatile_cookies
)
9709 if (cookie
->expires
== NULL
&& session_timeout
) {
9710 soup_cookie_set_expires(cookie
,
9711 soup_date_new_from_now(session_timeout
));
9712 print_cookie("modified add cookie", cookie
);
9715 /* see if we are white listed for persistence */
9716 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9717 /* add to persistent jar */
9718 c
= soup_cookie_copy(cookie
);
9719 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9720 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9723 /* add to session jar */
9724 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9725 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9731 char file
[PATH_MAX
];
9733 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9734 "soup_cookie_jar_add_cookie");
9735 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9736 "soup_cookie_jar_delete_cookie");
9738 if (cookies_enabled
== 0)
9742 * the following code is intricate due to overriding several libsoup
9744 * do not alter order of these operations.
9747 /* rejected cookies */
9748 if (save_rejected_cookies
)
9749 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9752 /* persistent cookies */
9753 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9754 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9756 /* session cookies */
9757 s_cookiejar
= soup_cookie_jar_new();
9758 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9759 cookie_policy
, (void *)NULL
);
9762 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9766 setup_proxy(char *uri
)
9769 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9770 soup_uri_free(proxy_uri
);
9774 if (http_proxy
!= uri
) {
9781 http_proxy
= g_strdup(uri
);
9782 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9783 proxy_uri
= soup_uri_new(http_proxy
);
9784 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
9785 g_object_set(session
, "proxy-uri", proxy_uri
,
9791 set_http_proxy(char *proxy
)
9798 /* see if we need to clear it instead */
9799 if (strlen(proxy
) == 0) {
9804 uri
= soup_uri_new(proxy
);
9805 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9816 send_cmd_to_socket(char *cmd
)
9819 struct sockaddr_un sa
;
9821 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9822 warnx("%s: socket", __func__
);
9826 sa
.sun_family
= AF_UNIX
;
9827 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9828 work_dir
, XT_SOCKET_FILE
);
9831 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9832 warnx("%s: connect", __func__
);
9836 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9837 warnx("%s: send", __func__
);
9848 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9851 char str
[XT_MAX_URL_LENGTH
];
9852 socklen_t t
= sizeof(struct sockaddr_un
);
9853 struct sockaddr_un sa
;
9858 gint fd
= g_io_channel_unix_get_fd(source
);
9860 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9865 if (getpeereid(s
, &uid
, &gid
) == -1) {
9869 if (uid
!= getuid() || gid
!= getgid()) {
9870 warnx("unauthorized user");
9876 warnx("not a valid user");
9880 n
= recv(s
, str
, sizeof(str
), 0);
9884 tt
= TAILQ_LAST(&tabs
, tab_list
);
9885 cmd_execute(tt
, str
);
9893 struct sockaddr_un sa
;
9895 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9896 warn("is_running: socket");
9900 sa
.sun_family
= AF_UNIX
;
9901 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9902 work_dir
, XT_SOCKET_FILE
);
9905 /* connect to see if there is a listener */
9906 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9907 rv
= 0; /* not running */
9909 rv
= 1; /* already running */
9920 struct sockaddr_un sa
;
9922 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9923 warn("build_socket: socket");
9927 sa
.sun_family
= AF_UNIX
;
9928 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9929 work_dir
, XT_SOCKET_FILE
);
9932 /* connect to see if there is a listener */
9933 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9934 /* no listener so we will */
9935 unlink(sa
.sun_path
);
9937 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9938 warn("build_socket: bind");
9942 if (listen(s
, 1) == -1) {
9943 warn("build_socket: listen");
9956 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9957 GtkTreeIter
*iter
, struct tab
*t
)
9961 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9969 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9970 GtkTreeIter
*iter
, struct tab
*t
)
9974 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9975 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9976 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9983 completion_add_uri(const gchar
*uri
)
9987 /* add uri to list_store */
9988 gtk_list_store_append(completion_model
, &iter
);
9989 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9993 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9994 GtkTreeIter
*iter
, gpointer user_data
)
9997 gboolean match
= FALSE
;
9999 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10005 match
= match_uri(value
, key
);
10012 completion_add(struct tab
*t
)
10014 /* enable completion for tab */
10015 t
->completion
= gtk_entry_completion_new();
10016 gtk_entry_completion_set_text_column(t
->completion
, 0);
10017 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10018 gtk_entry_completion_set_model(t
->completion
,
10019 GTK_TREE_MODEL(completion_model
));
10020 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10022 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10023 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10024 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10025 G_CALLBACK(completion_select_cb
), t
);
10026 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10027 G_CALLBACK(completion_hover_cb
), t
);
10035 if (stat(dir
, &sb
)) {
10036 if (mkdir(dir
, S_IRWXU
) == -1)
10037 err(1, "mkdir %s", dir
);
10038 if (stat(dir
, &sb
))
10039 err(1, "stat %s", dir
);
10041 if (S_ISDIR(sb
.st_mode
) == 0)
10042 errx(1, "%s not a dir", dir
);
10043 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10044 warnx("fixing invalid permissions on %s", dir
);
10045 if (chmod(dir
, S_IRWXU
) == -1)
10046 err(1, "chmod %s", dir
);
10054 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10060 main(int argc
, char *argv
[])
10063 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10064 char conf
[PATH_MAX
] = { '\0' };
10065 char file
[PATH_MAX
];
10066 char *env_proxy
= NULL
;
10070 struct sigaction sact
;
10071 GIOChannel
*channel
;
10077 gtk_init(&argc
, &argv
);
10078 g_thread_init(NULL
);
10080 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10084 RB_INIT(&downloads
);
10086 TAILQ_INIT(&sessions
);
10089 TAILQ_INIT(&aliases
);
10090 TAILQ_INIT(&undos
);
10096 /* fiddle with ulimits */
10097 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10100 /* just use them all */
10101 rlp
.rlim_cur
= rlp
.rlim_max
;
10102 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10104 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10106 else if (rlp
.rlim_cur
<= 256)
10107 startpage_add("%s requires at least 256 file "
10108 "descriptors, currently it has up to %d available",
10109 __progname
, rlp
.rlim_cur
);
10112 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10121 errx(0 , "Version: %s", version
);
10124 strlcpy(conf
, optarg
, sizeof(conf
));
10127 strlcpy(named_session
, optarg
, sizeof(named_session
));
10146 init_keybindings();
10148 gnutls_global_init();
10150 /* generate session keys for xtp pages */
10151 generate_xtp_session_key(&dl_session_key
);
10152 generate_xtp_session_key(&hl_session_key
);
10153 generate_xtp_session_key(&cl_session_key
);
10154 generate_xtp_session_key(&fl_session_key
);
10157 bzero(&sact
, sizeof(sact
));
10158 sigemptyset(&sact
.sa_mask
);
10159 sact
.sa_handler
= sigchild
;
10160 sact
.sa_flags
= SA_NOCLDSTOP
;
10161 sigaction(SIGCHLD
, &sact
, NULL
);
10163 /* set download dir */
10164 pwd
= getpwuid(getuid());
10166 errx(1, "invalid user %d", getuid());
10167 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10169 /* compile buffer command regexes */
10172 /* set default string settings */
10173 home
= g_strdup("https://www.cyphertite.com");
10174 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10175 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10176 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10177 cmd_font_name
= g_strdup("monospace normal 9");
10178 oops_font_name
= g_strdup("monospace normal 9");
10179 statusbar_font_name
= g_strdup("monospace normal 9");
10180 tabbar_font_name
= g_strdup("monospace normal 9");
10181 statusbar_elems
= g_strdup("BP");
10183 /* read config file */
10184 if (strlen(conf
) == 0)
10185 snprintf(conf
, sizeof conf
, "%s/.%s",
10186 pwd
->pw_dir
, XT_CONF_FILE
);
10187 config_parse(conf
, 0);
10190 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10191 oops_font
= pango_font_description_from_string(oops_font_name
);
10192 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10193 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10195 /* working directory */
10196 if (strlen(work_dir
) == 0)
10197 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10198 pwd
->pw_dir
, XT_DIR
);
10201 /* icon cache dir */
10202 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10203 xxx_dir(cache_dir
);
10206 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10207 xxx_dir(certs_dir
);
10210 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10211 work_dir
, XT_SESSIONS_DIR
);
10212 xxx_dir(sessions_dir
);
10214 /* runtime settings that can override config file */
10215 if (runtime_settings
[0] != '\0')
10216 config_parse(runtime_settings
, 1);
10219 if (!strcmp(download_dir
, pwd
->pw_dir
))
10220 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10221 xxx_dir(download_dir
);
10223 /* favorites file */
10224 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10225 if (stat(file
, &sb
)) {
10226 warnx("favorites file doesn't exist, creating it");
10227 if ((f
= fopen(file
, "w")) == NULL
)
10228 err(1, "favorites");
10232 /* quickmarks file */
10233 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10234 if (stat(file
, &sb
)) {
10235 warnx("quickmarks file doesn't exist, creating it");
10236 if ((f
= fopen(file
, "w")) == NULL
)
10237 err(1, "quickmarks");
10241 /* search history */
10242 if (history_autosave
) {
10243 snprintf(search_file
, sizeof search_file
, "%s/%s",
10244 work_dir
, XT_SEARCH_FILE
);
10245 if (stat(search_file
, &sb
)) {
10246 warnx("search history file doesn't exist, creating it");
10247 if ((f
= fopen(search_file
, "w")) == NULL
)
10248 err(1, "search_history");
10251 history_read(&shl
, search_file
, &search_history_count
);
10254 /* command history */
10255 if (history_autosave
) {
10256 snprintf(command_file
, sizeof command_file
, "%s/%s",
10257 work_dir
, XT_COMMAND_FILE
);
10258 if (stat(command_file
, &sb
)) {
10259 warnx("command history file doesn't exist, creating it");
10260 if ((f
= fopen(command_file
, "w")) == NULL
)
10261 err(1, "command_history");
10264 history_read(&chl
, command_file
, &cmd_history_count
);
10268 session
= webkit_get_default_session();
10273 if (stat(ssl_ca_file
, &sb
)) {
10274 warnx("no CA file: %s", ssl_ca_file
);
10275 g_free(ssl_ca_file
);
10276 ssl_ca_file
= NULL
;
10278 g_object_set(session
,
10279 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10280 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10284 /* guess_search regex */
10285 if (url_regex
== NULL
)
10286 url_regex
= g_strdup(XT_URL_REGEX
);
10288 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10289 startpage_add("invalid url regex %s", url_regex
);
10292 env_proxy
= getenv("http_proxy");
10294 setup_proxy(env_proxy
);
10296 setup_proxy(http_proxy
);
10299 send_cmd_to_socket(argv
[0]);
10303 /* set some connection parameters */
10304 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10305 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10308 /* see if there is already an xxxterm running */
10309 if (single_instance
&& is_running()) {
10311 warnx("already running");
10316 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10317 send_cmd_to_socket(cmd
);
10327 /* uri completion */
10328 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10331 buffers_store
= gtk_list_store_new
10332 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10338 notebook_tab_set_visibility();
10340 if (save_global_history
)
10341 restore_global_history();
10343 /* restore session list */
10344 restore_sessions_list();
10346 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10347 restore_saved_tabs();
10349 a
.s
= named_session
;
10350 a
.i
= XT_SES_DONOTHING
;
10351 open_tabs(NULL
, &a
);
10354 /* see if we have an exception */
10355 if (!TAILQ_EMPTY(&spl
)) {
10356 create_new_tab("about:startpage", NULL
, focus
, -1);
10361 create_new_tab(argv
[0], NULL
, focus
, -1);
10368 if (TAILQ_EMPTY(&tabs
))
10369 create_new_tab(home
, NULL
, 1, -1);
10372 if ((s
= build_socket()) != -1) {
10373 channel
= g_io_channel_unix_new(s
);
10374 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10379 gnutls_global_deinit();