3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
8 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * create privacy browsing
26 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #elif defined(__FreeBSD__)
50 #include "freebsd/util.h"
56 #include <sys/queue.h>
57 #include <sys/resource.h>
58 #include <sys/socket.h>
64 #include <gdk/gdkkeysyms.h>
66 #if GTK_CHECK_VERSION(3,0,0)
67 /* we still use GDK_* instead of GDK_KEY_* */
68 #include <gdk/gdkkeysyms-compat.h>
71 #include <webkit/webkit.h>
72 #include <libsoup/soup.h>
73 #include <gnutls/gnutls.h>
74 #include <JavaScriptCore/JavaScript.h>
75 #include <gnutls/x509.h>
77 #include "javascript.h"
80 javascript.h borrowed from vimprobable2 under the following license:
82 Copyright (c) 2009 Leon Winter
83 Copyright (c) 2009 Hannes Schueller
84 Copyright (c) 2009 Matto Fransen
86 Permission is hereby granted, free of charge, to any person obtaining a copy
87 of this software and associated documentation files (the "Software"), to deal
88 in the Software without restriction, including without limitation the rights
89 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
90 copies of the Software, and to permit persons to whom the Software is
91 furnished to do so, subject to the following conditions:
93 The above copyright notice and this permission notice shall be included in
94 all copies or substantial portions of the Software.
96 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
97 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
98 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
99 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
100 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
101 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
105 static char *version
= "$xxxterm$";
107 /* hooked functions */
108 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
109 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
114 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
115 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
116 #define XT_D_MOVE 0x0001
117 #define XT_D_KEY 0x0002
118 #define XT_D_TAB 0x0004
119 #define XT_D_URL 0x0008
120 #define XT_D_CMD 0x0010
121 #define XT_D_NAV 0x0020
122 #define XT_D_DOWNLOAD 0x0040
123 #define XT_D_CONFIG 0x0080
124 #define XT_D_JS 0x0100
125 #define XT_D_FAVORITE 0x0200
126 #define XT_D_PRINTING 0x0400
127 #define XT_D_COOKIE 0x0800
128 #define XT_D_KEYBINDING 0x1000
129 #define XT_D_CLIP 0x2000
130 #define XT_D_BUFFERCMD 0x4000
131 u_int32_t swm_debug
= 0
149 #define DPRINTF(x...)
150 #define DNPRINTF(n,x...)
153 #define LENGTH(x) (sizeof x / sizeof x[0])
154 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
155 ~(GDK_BUTTON1_MASK) & \
156 ~(GDK_BUTTON2_MASK) & \
157 ~(GDK_BUTTON3_MASK) & \
158 ~(GDK_BUTTON4_MASK) & \
161 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
172 TAILQ_ENTRY(tab
) entry
;
174 GtkWidget
*tab_content
;
183 GtkWidget
*uri_entry
;
184 GtkWidget
*search_entry
;
186 GtkWidget
*browser_win
;
187 GtkWidget
*statusbar_box
;
189 GtkWidget
*statusbar
;
190 GtkWidget
*buffercmd
;
201 GtkWidget
*js_toggle
;
202 GtkEntryCompletion
*completion
;
206 WebKitWebHistoryItem
*item
;
207 WebKitWebBackForwardList
*bfl
;
210 WebKitNetworkRequest
*icon_request
;
211 WebKitDownload
*icon_download
;
212 gchar
*icon_dest_uri
;
214 /* adjustments for browser */
217 GtkAdjustment
*adjust_h
;
218 GtkAdjustment
*adjust_v
;
224 int xtp_meaning
; /* identifies dls/favorites */
226 int popup
; /* 1 if cmd_entry has popup visible */
231 #define XT_HINT_NONE (0)
232 #define XT_HINT_NUMERICAL (1)
233 #define XT_HINT_ALPHANUM (2)
237 /* custom stylesheet */
247 WebKitWebSettings
*settings
;
251 double mark
[XT_NOMARKS
];
253 TAILQ_HEAD(tab_list
, tab
);
256 RB_ENTRY(history
) entry
;
260 RB_HEAD(history_list
, history
);
263 RB_ENTRY(download
) entry
;
265 WebKitDownload
*download
;
268 RB_HEAD(download_list
, download
);
271 RB_ENTRY(domain
) entry
;
273 int handy
; /* app use */
275 RB_HEAD(domain_list
, domain
);
278 TAILQ_ENTRY(undo
) entry
;
281 int back
; /* Keeps track of how many back
282 * history items there are. */
284 TAILQ_HEAD(undo_tailq
, undo
);
288 TAILQ_ENTRY(sp
) entry
;
290 TAILQ_HEAD(sp_list
, sp
);
292 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
293 int next_download_id
= 1;
302 #define XT_NAME ("XXXTerm")
303 #define XT_DIR (".xxxterm")
304 #define XT_CACHE_DIR ("cache")
305 #define XT_CERT_DIR ("certs/")
306 #define XT_SESSIONS_DIR ("sessions/")
307 #define XT_CONF_FILE ("xxxterm.conf")
308 #define XT_FAVS_FILE ("favorites")
309 #define XT_QMARKS_FILE ("quickmarks")
310 #define XT_SAVED_TABS_FILE ("main_session")
311 #define XT_RESTART_TABS_FILE ("restart_tabs")
312 #define XT_SOCKET_FILE ("socket")
313 #define XT_HISTORY_FILE ("history")
314 #define XT_REJECT_FILE ("rejected.txt")
315 #define XT_COOKIE_FILE ("cookies.txt")
316 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
317 #define XT_CB_HANDLED (TRUE)
318 #define XT_CB_PASSTHROUGH (FALSE)
319 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
320 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
321 #define XT_DLMAN_REFRESH "10"
322 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
323 "td{overflow: hidden;" \
324 " padding: 2px 2px 2px 2px;" \
325 " border: 1px solid black;" \
326 " vertical-align:top;" \
327 " word-wrap: break-word}\n" \
328 "tr:hover{background: #ffff99}\n" \
329 "th{background-color: #cccccc;" \
330 " border: 1px solid black}\n" \
331 "table{width: 100%%;" \
332 " border: 1px black solid;" \
333 " border-collapse:collapse}\n" \
335 "border: 1px solid black;" \
338 ".progress-inner{float: left;" \
340 " background: green}\n" \
341 ".dlstatus{font-size: small;" \
342 " text-align: center}\n" \
344 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
345 #define XT_MAX_UNDO_CLOSE_TAB (32)
346 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
347 #define XT_PRINT_EXTRA_MARGIN 10
348 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
349 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
352 #define XT_COLOR_RED "#cc0000"
353 #define XT_COLOR_YELLOW "#ffff66"
354 #define XT_COLOR_BLUE "lightblue"
355 #define XT_COLOR_GREEN "#99ff66"
356 #define XT_COLOR_WHITE "white"
357 #define XT_COLOR_BLACK "black"
359 #define XT_COLOR_CT_BACKGROUND "#000000"
360 #define XT_COLOR_CT_INACTIVE "#dddddd"
361 #define XT_COLOR_CT_ACTIVE "#bbbb00"
362 #define XT_COLOR_CT_SEPARATOR "#555555"
364 #define XT_COLOR_SB_SEPARATOR "#555555"
366 #define XT_PROTO_DELIM "://"
369 * xxxterm "protocol" (xtp)
370 * We use this for managing stuff like downloads and favorites. They
371 * make magical HTML pages in memory which have xxxt:// links in order
372 * to communicate with xxxterm's internals. These links take the format:
373 * xxxt://class/session_key/action/arg
375 * Don't begin xtp class/actions as 0. atoi returns that on error.
377 * Typically we have not put addition of items in this framework, as
378 * adding items is either done via an ex-command or via a keybinding instead.
381 #define XT_XTP_STR "xxxt://"
383 /* XTP classes (xxxt://<class>) */
384 #define XT_XTP_INVALID 0 /* invalid */
385 #define XT_XTP_DL 1 /* downloads */
386 #define XT_XTP_HL 2 /* history */
387 #define XT_XTP_CL 3 /* cookies */
388 #define XT_XTP_FL 4 /* favorites */
390 /* XTP download actions */
391 #define XT_XTP_DL_LIST 1
392 #define XT_XTP_DL_CANCEL 2
393 #define XT_XTP_DL_REMOVE 3
395 /* XTP history actions */
396 #define XT_XTP_HL_LIST 1
397 #define XT_XTP_HL_REMOVE 2
399 /* XTP cookie actions */
400 #define XT_XTP_CL_LIST 1
401 #define XT_XTP_CL_REMOVE 2
403 /* XTP cookie actions */
404 #define XT_XTP_FL_LIST 1
405 #define XT_XTP_FL_REMOVE 2
408 #define XT_MOVE_INVALID (0)
409 #define XT_MOVE_DOWN (1)
410 #define XT_MOVE_UP (2)
411 #define XT_MOVE_BOTTOM (3)
412 #define XT_MOVE_TOP (4)
413 #define XT_MOVE_PAGEDOWN (5)
414 #define XT_MOVE_PAGEUP (6)
415 #define XT_MOVE_HALFDOWN (7)
416 #define XT_MOVE_HALFUP (8)
417 #define XT_MOVE_LEFT (9)
418 #define XT_MOVE_FARLEFT (10)
419 #define XT_MOVE_RIGHT (11)
420 #define XT_MOVE_FARRIGHT (12)
421 #define XT_MOVE_PERCENT (13)
423 #define XT_QMARK_SET (0)
424 #define XT_QMARK_OPEN (1)
425 #define XT_QMARK_TAB (2)
427 #define XT_MARK_SET (0)
428 #define XT_MARK_GOTO (1)
430 #define XT_TAB_LAST (-4)
431 #define XT_TAB_FIRST (-3)
432 #define XT_TAB_PREV (-2)
433 #define XT_TAB_NEXT (-1)
434 #define XT_TAB_INVALID (0)
435 #define XT_TAB_NEW (1)
436 #define XT_TAB_DELETE (2)
437 #define XT_TAB_DELQUIT (3)
438 #define XT_TAB_OPEN (4)
439 #define XT_TAB_UNDO_CLOSE (5)
440 #define XT_TAB_SHOW (6)
441 #define XT_TAB_HIDE (7)
442 #define XT_TAB_NEXTSTYLE (8)
444 #define XT_NAV_INVALID (0)
445 #define XT_NAV_BACK (1)
446 #define XT_NAV_FORWARD (2)
447 #define XT_NAV_RELOAD (3)
449 #define XT_FOCUS_INVALID (0)
450 #define XT_FOCUS_URI (1)
451 #define XT_FOCUS_SEARCH (2)
453 #define XT_SEARCH_INVALID (0)
454 #define XT_SEARCH_NEXT (1)
455 #define XT_SEARCH_PREV (2)
457 #define XT_PASTE_CURRENT_TAB (0)
458 #define XT_PASTE_NEW_TAB (1)
460 #define XT_ZOOM_IN (-1)
461 #define XT_ZOOM_OUT (-2)
462 #define XT_ZOOM_NORMAL (100)
464 #define XT_URL_SHOW (1)
465 #define XT_URL_HIDE (2)
467 #define XT_WL_TOGGLE (1<<0)
468 #define XT_WL_ENABLE (1<<1)
469 #define XT_WL_DISABLE (1<<2)
470 #define XT_WL_FQDN (1<<3) /* default */
471 #define XT_WL_TOPLEVEL (1<<4)
472 #define XT_WL_PERSISTENT (1<<5)
473 #define XT_WL_SESSION (1<<6)
474 #define XT_WL_RELOAD (1<<7)
476 #define XT_SHOW (1<<7)
477 #define XT_DELETE (1<<8)
478 #define XT_SAVE (1<<9)
479 #define XT_OPEN (1<<10)
481 #define XT_CMD_OPEN (0)
482 #define XT_CMD_OPEN_CURRENT (1)
483 #define XT_CMD_TABNEW (2)
484 #define XT_CMD_TABNEW_CURRENT (3)
486 #define XT_STATUS_NOTHING (0)
487 #define XT_STATUS_LINK (1)
488 #define XT_STATUS_URI (2)
489 #define XT_STATUS_LOADING (3)
491 #define XT_SES_DONOTHING (0)
492 #define XT_SES_CLOSETABS (1)
494 #define XT_BM_NORMAL (0)
495 #define XT_BM_WHITELIST (1)
496 #define XT_BM_KIOSK (2)
498 #define XT_PREFIX (1<<0)
499 #define XT_USERARG (1<<1)
500 #define XT_URLARG (1<<2)
501 #define XT_INTARG (1<<3)
503 #define XT_TABS_NORMAL 0
504 #define XT_TABS_COMPACT 1
506 #define XT_BUFCMD_SZ (8)
514 TAILQ_ENTRY(mime_type
) entry
;
516 TAILQ_HEAD(mime_type_list
, mime_type
);
522 TAILQ_ENTRY(alias
) entry
;
524 TAILQ_HEAD(alias_list
, alias
);
526 /* settings that require restart */
527 int tabless
= 0; /* allow only 1 tab */
528 int enable_socket
= 0;
529 int single_instance
= 0; /* only allow one xxxterm to run */
530 int fancy_bar
= 1; /* fancy toolbar */
531 int browser_mode
= XT_BM_NORMAL
;
532 int enable_localstorage
= 0;
533 char *statusbar_elems
= NULL
;
535 /* runtime settings */
536 int show_tabs
= 1; /* show tabs on notebook */
537 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
538 int show_url
= 1; /* show url toolbar on notebook */
539 int show_statusbar
= 0; /* vimperator style status bar */
540 int ctrl_click_focus
= 0; /* ctrl click gets focus */
541 int cookies_enabled
= 1; /* enable cookies */
542 int read_only_cookies
= 0; /* enable to not write cookies */
543 int enable_scripts
= 1;
544 int enable_plugins
= 0;
545 gfloat default_zoom_level
= 1.0;
546 char default_script
[PATH_MAX
];
547 int window_height
= 768;
548 int window_width
= 1024;
549 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
550 int refresh_interval
= 10; /* download refresh interval */
551 int enable_cookie_whitelist
= 0;
552 int enable_js_whitelist
= 0;
553 int session_timeout
= 3600; /* cookie session timeout */
554 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
555 char *ssl_ca_file
= NULL
;
556 char *resource_dir
= NULL
;
557 gboolean ssl_strict_certs
= FALSE
;
558 int append_next
= 1; /* append tab after current tab */
560 char *search_string
= NULL
;
561 char *http_proxy
= NULL
;
562 char download_dir
[PATH_MAX
];
563 char runtime_settings
[PATH_MAX
]; /* override of settings */
564 int allow_volatile_cookies
= 0;
565 int save_global_history
= 0; /* save global history to disk */
566 char *user_agent
= NULL
;
567 int save_rejected_cookies
= 0;
568 int session_autosave
= 0;
569 int guess_search
= 0;
570 int dns_prefetch
= FALSE
;
571 gint max_connections
= 25;
572 gint max_host_connections
= 5;
573 gint enable_spell_checking
= 0;
574 char *spell_check_languages
= NULL
;
575 int xterm_workaround
= 0;
576 char *url_regex
= NULL
;
578 char *cmd_font_name
= NULL
;
579 char *oops_font_name
= NULL
;
580 char *statusbar_font_name
= NULL
;
581 char *tabbar_font_name
= NULL
;
582 PangoFontDescription
*cmd_font
;
583 PangoFontDescription
*oops_font
;
584 PangoFontDescription
*statusbar_font
;
585 PangoFontDescription
*tabbar_font
;
586 char *qmarks
[XT_NOMARKS
];
588 int btn_down
; /* M1 down in any wv */
589 regex_t url_re
; /* guess_search regex */
593 int set_browser_mode(struct settings
*, char *);
594 int set_cookie_policy(struct settings
*, char *);
595 int set_download_dir(struct settings
*, char *);
596 int set_default_script(struct settings
*, char *);
597 int set_runtime_dir(struct settings
*, char *);
598 int set_tab_style(struct settings
*, char *);
599 int set_work_dir(struct settings
*, char *);
600 int add_alias(struct settings
*, char *);
601 int add_mime_type(struct settings
*, char *);
602 int add_cookie_wl(struct settings
*, char *);
603 int add_js_wl(struct settings
*, char *);
604 int add_kb(struct settings
*, char *);
605 void button_set_stockid(GtkWidget
*, char *);
606 GtkWidget
* create_button(char *, char *, int);
608 char *get_browser_mode(struct settings
*);
609 char *get_cookie_policy(struct settings
*);
610 char *get_download_dir(struct settings
*);
611 char *get_default_script(struct settings
*);
612 char *get_runtime_dir(struct settings
*);
613 char *get_tab_style(struct settings
*);
614 char *get_work_dir(struct settings
*);
615 void startpage_add(const char *, ...);
617 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
618 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
619 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
620 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
621 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
623 void recalc_tabs(void);
624 void recolor_compact_tabs(void);
625 void set_current_tab(int page_num
);
626 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
627 void marks_clear(struct tab
*t
);
629 int set_http_proxy(char *);
632 int (*set
)(struct settings
*, char *);
633 char *(*get
)(struct settings
*);
634 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
637 struct special s_browser_mode
= {
643 struct special s_cookie
= {
649 struct special s_alias
= {
655 struct special s_mime
= {
661 struct special s_js
= {
667 struct special s_kb
= {
673 struct special s_cookie_wl
= {
679 struct special s_default_script
= {
685 struct special s_download_dir
= {
691 struct special s_work_dir
= {
697 struct special s_tab_style
= {
706 #define XT_S_INVALID (0)
709 #define XT_S_FLOAT (3)
711 #define XT_SF_RESTART (1<<0)
712 #define XT_SF_RUNTIME (1<<1)
717 int (*activate
)(char *);
719 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
720 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
721 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
722 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
723 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
724 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
725 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
726 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
727 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
728 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
729 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
730 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
731 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
732 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
733 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
734 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
735 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
736 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
737 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
738 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
739 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
740 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
741 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
742 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
743 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
744 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
745 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
746 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
747 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
748 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
749 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
750 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
751 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
752 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
753 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
754 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
755 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
756 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
757 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
758 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
759 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
760 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
761 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
762 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
763 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
764 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
767 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
768 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
769 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
770 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
772 /* runtime settings */
773 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
774 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
775 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
776 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
777 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
780 int about(struct tab
*, struct karg
*);
781 int blank(struct tab
*, struct karg
*);
782 int ca_cmd(struct tab
*, struct karg
*);
783 int cookie_show_wl(struct tab
*, struct karg
*);
784 int js_show_wl(struct tab
*, struct karg
*);
785 int help(struct tab
*, struct karg
*);
786 int set(struct tab
*, struct karg
*);
787 int stats(struct tab
*, struct karg
*);
788 int marco(struct tab
*, struct karg
*);
789 int startpage(struct tab
*, struct karg
*);
790 const char * marco_message(int *);
791 int xtp_page_cl(struct tab
*, struct karg
*);
792 int xtp_page_dl(struct tab
*, struct karg
*);
793 int xtp_page_fl(struct tab
*, struct karg
*);
794 int xtp_page_hl(struct tab
*, struct karg
*);
795 void xt_icon_from_file(struct tab
*, char *);
796 const gchar
*get_uri(struct tab
*);
797 const gchar
*get_title(struct tab
*, bool);
799 #define XT_URI_ABOUT ("about:")
800 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
801 #define XT_URI_ABOUT_ABOUT ("about")
802 #define XT_URI_ABOUT_BLANK ("blank")
803 #define XT_URI_ABOUT_CERTS ("certs")
804 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
805 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
806 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
807 #define XT_URI_ABOUT_FAVORITES ("favorites")
808 #define XT_URI_ABOUT_HELP ("help")
809 #define XT_URI_ABOUT_HISTORY ("history")
810 #define XT_URI_ABOUT_JSWL ("jswl")
811 #define XT_URI_ABOUT_SET ("set")
812 #define XT_URI_ABOUT_STATS ("stats")
813 #define XT_URI_ABOUT_MARCO ("marco")
814 #define XT_URI_ABOUT_STARTPAGE ("startpage")
818 int (*func
)(struct tab
*, struct karg
*);
820 { XT_URI_ABOUT_ABOUT
, about
},
821 { XT_URI_ABOUT_BLANK
, blank
},
822 { XT_URI_ABOUT_CERTS
, ca_cmd
},
823 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
824 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
825 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
826 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
827 { XT_URI_ABOUT_HELP
, help
},
828 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
829 { XT_URI_ABOUT_JSWL
, js_show_wl
},
830 { XT_URI_ABOUT_SET
, set
},
831 { XT_URI_ABOUT_STATS
, stats
},
832 { XT_URI_ABOUT_MARCO
, marco
},
833 { XT_URI_ABOUT_STARTPAGE
, startpage
},
836 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
837 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
838 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
839 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
840 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
841 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
842 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
845 extern char *__progname
;
848 GtkWidget
*main_window
;
849 GtkNotebook
*notebook
;
851 GtkWidget
*arrow
, *abtn
;
852 struct tab_list tabs
;
853 struct history_list hl
;
854 struct download_list downloads
;
855 struct domain_list c_wl
;
856 struct domain_list js_wl
;
857 struct undo_tailq undos
;
858 struct keybinding_list kbl
;
861 int updating_dl_tabs
= 0;
862 int updating_hl_tabs
= 0;
863 int updating_cl_tabs
= 0;
864 int updating_fl_tabs
= 0;
866 uint64_t blocked_cookies
= 0;
867 char named_session
[PATH_MAX
];
868 int icon_size_map(int);
870 GtkListStore
*completion_model
;
871 void completion_add(struct tab
*);
872 void completion_add_uri(const gchar
*);
873 GtkListStore
*buffers_store
;
874 void xxx_dir(char *);
876 /* marks and quickmarks array storage.
877 * first a-z, then A-Z, then 0-9 */
884 if (i
>= 0 && i
<= 'z' - 'a')
888 if (i
>= 0 && i
<= 'Z' - 'A')
903 if (m
>= 'a' && m
<= 'z')
904 return ret
+ m
- 'a';
906 ret
+= 'z' - 'a' + 1;
907 if (m
>= 'A' && m
<= 'Z')
908 return ret
+ m
- 'A';
910 ret
+= 'Z' - 'A' + 1;
911 if (m
>= '0' && m
<= '9')
912 return ret
+ m
- '0';
921 int saved_errno
, status
;
926 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
930 if (errno
!= ECHILD
) {
932 clog_warn("sigchild: waitpid:");
938 if (WIFEXITED(status
)) {
939 if (WEXITSTATUS(status
) != 0) {
941 clog_warnx("sigchild: child exit status: %d",
942 WEXITSTATUS(status));
947 clog_warnx("sigchild: child is terminated abnormally");
956 is_g_object_setting(GObject
*o
, char *str
)
958 guint n_props
= 0, i
;
959 GParamSpec
**proplist
;
961 if (! G_IS_OBJECT(o
))
964 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
967 for (i
=0; i
< n_props
; i
++) {
968 if (! strcmp(proplist
[i
]->name
, str
))
975 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
979 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
981 "<title>%s</title>\n"
990 addstyles
? XT_PAGE_STYLE
: "",
999 * Display a web page from a HTML string in memory, rather than from a URL
1002 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1004 char file
[PATH_MAX
];
1007 /* we set this to indicate we want to manually do navaction */
1009 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1011 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1013 /* set t->xtp_meaning */
1014 for (i
= 0; i
< LENGTH(about_list
); i
++)
1015 if (!strcmp(title
, about_list
[i
].name
)) {
1020 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1021 #if GTK_CHECK_VERSION(2, 20, 0)
1022 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1023 gtk_widget_hide(t
->spinner
);
1025 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1026 xt_icon_from_file(t
, file
);
1031 get_current_tab(void)
1035 TAILQ_FOREACH(t
, &tabs
, entry
) {
1036 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1040 warnx("%s: no current tab", __func__
);
1046 set_status(struct tab
*t
, gchar
*s
, int status
)
1054 case XT_STATUS_LOADING
:
1055 type
= g_strdup_printf("Loading: %s", s
);
1058 case XT_STATUS_LINK
:
1059 type
= g_strdup_printf("Link: %s", s
);
1061 t
->status
= g_strdup(gtk_entry_get_text(
1062 GTK_ENTRY(t
->sbe
.statusbar
)));
1066 type
= g_strdup_printf("%s", s
);
1068 t
->status
= g_strdup(type
);
1072 t
->status
= g_strdup(s
);
1074 case XT_STATUS_NOTHING
:
1079 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1085 hide_cmd(struct tab
*t
)
1087 gtk_widget_hide(t
->cmd
);
1091 show_cmd(struct tab
*t
)
1093 gtk_widget_hide(t
->oops
);
1094 gtk_widget_show(t
->cmd
);
1098 hide_buffers(struct tab
*t
)
1100 gtk_widget_hide(t
->buffers
);
1101 gtk_list_store_clear(buffers_store
);
1111 sort_tabs_by_page_num(struct tab
***stabs
)
1116 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1118 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1120 TAILQ_FOREACH(t
, &tabs
, entry
)
1121 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1127 buffers_make_list(void)
1130 const gchar
*title
= NULL
;
1132 struct tab
**stabs
= NULL
;
1134 num_tabs
= sort_tabs_by_page_num(&stabs
);
1136 for (i
= 0; i
< num_tabs
; i
++)
1138 gtk_list_store_append(buffers_store
, &iter
);
1139 title
= get_title(stabs
[i
], FALSE
);
1140 gtk_list_store_set(buffers_store
, &iter
,
1141 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1151 show_buffers(struct tab
*t
)
1153 buffers_make_list();
1154 gtk_widget_show(t
->buffers
);
1155 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1159 toggle_buffers(struct tab
*t
)
1161 if (gtk_widget_get_visible(t
->buffers
))
1168 buffers(struct tab
*t
, struct karg
*args
)
1176 hide_oops(struct tab
*t
)
1178 gtk_widget_hide(t
->oops
);
1182 show_oops(struct tab
*at
, const char *fmt
, ...)
1186 struct tab
*t
= NULL
;
1192 if ((t
= get_current_tab()) == NULL
)
1198 if (vasprintf(&msg
, fmt
, ap
) == -1)
1199 errx(1, "show_oops failed");
1202 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1203 gtk_widget_hide(t
->cmd
);
1204 gtk_widget_show(t
->oops
);
1211 get_as_string(struct settings
*s
)
1222 warnx("get_as_string skip %s\n", s
->name
);
1223 } else if (s
->type
== XT_S_INT
)
1224 r
= g_strdup_printf("%d", *s
->ival
);
1225 else if (s
->type
== XT_S_STR
)
1226 r
= g_strdup(*s
->sval
);
1227 else if (s
->type
== XT_S_FLOAT
)
1228 r
= g_strdup_printf("%f", *s
->fval
);
1230 r
= g_strdup_printf("INVALID TYPE");
1236 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1241 for (i
= 0; i
< LENGTH(rs
); i
++) {
1242 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1243 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1245 s
= get_as_string(&rs
[i
]);
1246 cb(&rs
[i
], s
, cb_args
);
1253 set_browser_mode(struct settings
*s
, char *val
)
1255 if (!strcmp(val
, "whitelist")) {
1256 browser_mode
= XT_BM_WHITELIST
;
1257 allow_volatile_cookies
= 0;
1258 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1259 cookies_enabled
= 1;
1260 enable_cookie_whitelist
= 1;
1261 read_only_cookies
= 0;
1262 save_rejected_cookies
= 0;
1263 session_timeout
= 3600;
1265 enable_js_whitelist
= 1;
1266 enable_localstorage
= 0;
1267 } else if (!strcmp(val
, "normal")) {
1268 browser_mode
= XT_BM_NORMAL
;
1269 allow_volatile_cookies
= 0;
1270 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1271 cookies_enabled
= 1;
1272 enable_cookie_whitelist
= 0;
1273 read_only_cookies
= 0;
1274 save_rejected_cookies
= 0;
1275 session_timeout
= 3600;
1277 enable_js_whitelist
= 0;
1278 enable_localstorage
= 1;
1279 } else if (!strcmp(val
, "kiosk")) {
1280 browser_mode
= XT_BM_KIOSK
;
1281 allow_volatile_cookies
= 0;
1282 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1283 cookies_enabled
= 1;
1284 enable_cookie_whitelist
= 0;
1285 read_only_cookies
= 0;
1286 save_rejected_cookies
= 0;
1287 session_timeout
= 3600;
1289 enable_js_whitelist
= 0;
1290 enable_localstorage
= 1;
1300 get_browser_mode(struct settings
*s
)
1304 if (browser_mode
== XT_BM_WHITELIST
)
1305 r
= g_strdup("whitelist");
1306 else if (browser_mode
== XT_BM_NORMAL
)
1307 r
= g_strdup("normal");
1308 else if (browser_mode
== XT_BM_KIOSK
)
1309 r
= g_strdup("kiosk");
1317 set_cookie_policy(struct settings
*s
, char *val
)
1319 if (!strcmp(val
, "no3rdparty"))
1320 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1321 else if (!strcmp(val
, "accept"))
1322 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1323 else if (!strcmp(val
, "reject"))
1324 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1332 get_cookie_policy(struct settings
*s
)
1336 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1337 r
= g_strdup("no3rdparty");
1338 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1339 r
= g_strdup("accept");
1340 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1341 r
= g_strdup("reject");
1349 get_default_script(struct settings
*s
)
1351 if (default_script
[0] == '\0')
1353 return (g_strdup(default_script
));
1357 set_default_script(struct settings
*s
, char *val
)
1360 snprintf(default_script
, sizeof default_script
, "%s/%s",
1361 pwd
->pw_dir
, &val
[1]);
1363 strlcpy(default_script
, val
, sizeof default_script
);
1369 get_download_dir(struct settings
*s
)
1371 if (download_dir
[0] == '\0')
1373 return (g_strdup(download_dir
));
1377 set_download_dir(struct settings
*s
, char *val
)
1380 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1381 pwd
->pw_dir
, &val
[1]);
1383 strlcpy(download_dir
, val
, sizeof download_dir
);
1390 * We use these to prevent people putting xxxt:// URLs on
1391 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1393 #define XT_XTP_SES_KEY_SZ 8
1394 #define XT_XTP_SES_KEY_HEX_FMT \
1395 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1396 char *dl_session_key
; /* downloads */
1397 char *hl_session_key
; /* history list */
1398 char *cl_session_key
; /* cookie list */
1399 char *fl_session_key
; /* favorites list */
1401 char work_dir
[PATH_MAX
];
1402 char certs_dir
[PATH_MAX
];
1403 char cache_dir
[PATH_MAX
];
1404 char sessions_dir
[PATH_MAX
];
1405 char cookie_file
[PATH_MAX
];
1406 SoupURI
*proxy_uri
= NULL
;
1407 SoupSession
*session
;
1408 SoupCookieJar
*s_cookiejar
;
1409 SoupCookieJar
*p_cookiejar
;
1410 char rc_fname
[PATH_MAX
];
1412 struct mime_type_list mtl
;
1413 struct alias_list aliases
;
1416 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1417 void delete_tab(struct tab
*);
1418 void setzoom_webkit(struct tab
*, int);
1419 int run_script(struct tab
*, char *);
1420 int download_rb_cmp(struct download
*, struct download
*);
1421 gboolean
cmd_execute(struct tab
*t
, char *str
);
1424 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1426 return (strcmp(h1
->uri
, h2
->uri
));
1428 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1431 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1433 return (strcmp(d1
->d
, d2
->d
));
1435 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1438 get_work_dir(struct settings
*s
)
1440 if (work_dir
[0] == '\0')
1442 return (g_strdup(work_dir
));
1446 set_work_dir(struct settings
*s
, char *val
)
1449 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1450 pwd
->pw_dir
, &val
[1]);
1452 strlcpy(work_dir
, val
, sizeof work_dir
);
1458 get_tab_style(struct settings
*s
)
1460 if (tab_style
== XT_TABS_NORMAL
)
1461 return (g_strdup("normal"));
1463 return (g_strdup("compact"));
1467 set_tab_style(struct settings
*s
, char *val
)
1469 if (!strcmp(val
, "normal"))
1470 tab_style
= XT_TABS_NORMAL
;
1471 else if (!strcmp(val
, "compact"))
1472 tab_style
= XT_TABS_COMPACT
;
1480 * generate a session key to secure xtp commands.
1481 * pass in a ptr to the key in question and it will
1482 * be modified in place.
1485 generate_xtp_session_key(char **key
)
1487 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1493 /* make a new one */
1494 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1495 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1496 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1497 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1499 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1503 * validate a xtp session key.
1507 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1509 if (strcmp(trusted
, untrusted
) != 0) {
1510 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1519 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1521 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1523 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1525 struct valid_url_types
{
1536 valid_url_type(char *url
)
1540 for (i
= 0; i
< LENGTH(vut
); i
++)
1541 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1548 print_cookie(char *msg
, SoupCookie
*c
)
1554 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1555 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1556 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1557 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1558 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1559 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1560 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1561 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1562 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1563 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1567 walk_alias(struct settings
*s
,
1568 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1573 if (s
== NULL
|| cb
== NULL
) {
1574 show_oops(NULL
, "walk_alias invalid parameters");
1578 TAILQ_FOREACH(a
, &aliases
, entry
) {
1579 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1580 cb(s
, str
, cb_args
);
1586 match_alias(char *url_in
)
1590 char *url_out
= NULL
, *search
, *enc_arg
;
1592 search
= g_strdup(url_in
);
1594 if (strsep(&arg
, " \t") == NULL
) {
1595 show_oops(NULL
, "match_alias: NULL URL");
1599 TAILQ_FOREACH(a
, &aliases
, entry
) {
1600 if (!strcmp(search
, a
->a_name
))
1605 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1608 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1609 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1612 url_out
= g_strdup_printf(a
->a_uri
, "");
1620 guess_url_type(char *url_in
)
1623 char *url_out
= NULL
, *enc_search
= NULL
;
1625 url_out
= match_alias(url_in
);
1626 if (url_out
!= NULL
)
1629 if (guess_search
&& url_regex
&&
1630 !(g_str_has_prefix(url_in
, "http://") ||
1631 g_str_has_prefix(url_in
, "https://"))) {
1632 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1633 /* invalid URI so search instead */
1634 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1635 url_out
= g_strdup_printf(search_string
, enc_search
);
1641 /* XXX not sure about this heuristic */
1642 if (stat(url_in
, &sb
) == 0)
1643 url_out
= g_strdup_printf("file://%s", url_in
);
1645 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1647 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1653 load_uri(struct tab
*t
, gchar
*uri
)
1656 gchar
*newuri
= NULL
;
1662 /* Strip leading spaces. */
1663 while (*uri
&& isspace(*uri
))
1666 if (strlen(uri
) == 0) {
1671 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1673 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1674 for (i
= 0; i
< LENGTH(about_list
); i
++)
1675 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1676 bzero(&args
, sizeof args
);
1677 about_list
[i
].func(t
, &args
);
1678 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1682 show_oops(t
, "invalid about page");
1686 if (valid_url_type(uri
)) {
1687 newuri
= guess_url_type(uri
);
1691 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1693 webkit_web_view_load_uri(t
->wv
, uri
);
1700 get_uri(struct tab
*t
)
1702 const gchar
*uri
= NULL
;
1704 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1706 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1707 uri
= webkit_web_view_get_uri(t
->wv
);
1709 /* use tmp_uri to make sure it is g_freed */
1712 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1713 about_list
[t
->xtp_meaning
].name
);
1720 get_title(struct tab
*t
, bool window
)
1722 const gchar
*set
= NULL
, *title
= NULL
;
1723 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1725 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1726 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1729 title
= webkit_web_view_get_title(t
->wv
);
1730 if ((set
= title
? title
: get_uri(t
)))
1734 set
= window
? XT_NAME
: "(untitled)";
1740 add_alias(struct settings
*s
, char *line
)
1743 struct alias
*a
= NULL
;
1745 if (s
== NULL
|| line
== NULL
) {
1746 show_oops(NULL
, "add_alias invalid parameters");
1751 a
= g_malloc(sizeof(*a
));
1753 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1754 show_oops(NULL
, "add_alias: incomplete alias definition");
1757 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1758 show_oops(NULL
, "add_alias: invalid alias definition");
1762 a
->a_name
= g_strdup(alias
);
1763 a
->a_uri
= g_strdup(l
);
1765 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1767 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1777 add_mime_type(struct settings
*s
, char *line
)
1781 struct mime_type
*m
= NULL
;
1782 int downloadfirst
= 0;
1784 /* XXX this could be smarter */
1786 if (line
== NULL
|| strlen(line
) == 0) {
1787 show_oops(NULL
, "add_mime_type invalid parameters");
1796 m
= g_malloc(sizeof(*m
));
1798 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1799 show_oops(NULL
, "add_mime_type: invalid mime_type");
1802 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1803 mime_type
[strlen(mime_type
) - 1] = '\0';
1808 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1809 show_oops(NULL
, "add_mime_type: invalid mime_type");
1813 m
->mt_type
= g_strdup(mime_type
);
1814 m
->mt_action
= g_strdup(l
);
1815 m
->mt_download
= downloadfirst
;
1817 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1818 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1820 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1830 find_mime_type(char *mime_type
)
1832 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1834 TAILQ_FOREACH(m
, &mtl
, entry
) {
1835 if (m
->mt_default
&&
1836 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1839 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1852 walk_mime_type(struct settings
*s
,
1853 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1855 struct mime_type
*m
;
1858 if (s
== NULL
|| cb
== NULL
) {
1859 show_oops(NULL
, "walk_mime_type invalid parameters");
1863 TAILQ_FOREACH(m
, &mtl
, entry
) {
1864 str
= g_strdup_printf("%s%s --> %s",
1866 m
->mt_default
? "*" : "",
1868 cb(s
, str
, cb_args
);
1874 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1880 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1883 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1885 /* treat *.moo.com the same as .moo.com */
1886 if (str
[0] == '*' && str
[1] == '.')
1888 else if (str
[0] == '.')
1893 /* slice off port number */
1894 p
= g_strrstr(str
, ":");
1898 d
= g_malloc(sizeof *d
);
1900 d
->d
= g_strdup_printf(".%s", str
);
1902 d
->d
= g_strdup(str
);
1905 if (RB_INSERT(domain_list
, wl
, d
))
1908 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1919 add_cookie_wl(struct settings
*s
, char *entry
)
1921 wl_add(entry
, &c_wl
, 1);
1926 walk_cookie_wl(struct settings
*s
,
1927 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1931 if (s
== NULL
|| cb
== NULL
) {
1932 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1936 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1937 cb(s
, d
->d
, cb_args
);
1941 walk_js_wl(struct settings
*s
,
1942 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1946 if (s
== NULL
|| cb
== NULL
) {
1947 show_oops(NULL
, "walk_js_wl invalid parameters");
1951 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1952 cb(s
, d
->d
, cb_args
);
1956 add_js_wl(struct settings
*s
, char *entry
)
1958 wl_add(entry
, &js_wl
, 1 /* persistent */);
1963 wl_find(const gchar
*search
, struct domain_list
*wl
)
1966 struct domain
*d
= NULL
, dfind
;
1969 if (search
== NULL
|| wl
== NULL
)
1971 if (strlen(search
) < 2)
1974 if (search
[0] != '.')
1975 s
= g_strdup_printf(".%s", search
);
1977 s
= g_strdup(search
);
1979 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1982 d
= RB_FIND(domain_list
, wl
, &dfind
);
1996 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2002 if (s
== NULL
|| wl
== NULL
)
2005 if (!strncmp(s
, "http://", strlen("http://")))
2006 s
= &s
[strlen("http://")];
2007 else if (!strncmp(s
, "https://", strlen("https://")))
2008 s
= &s
[strlen("https://")];
2013 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2014 /* chop string at first slash */
2015 if (s
[i
] == '/' || s
[i
] == '\0') {
2018 r
= wl_find(ss
, wl
);
2027 settings_add(char *var
, char *val
)
2034 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2035 if (strcmp(var
, rs
[i
].name
))
2039 if (rs
[i
].s
->set(&rs
[i
], val
))
2040 errx(1, "invalid value for %s: %s", var
, val
);
2044 switch (rs
[i
].type
) {
2053 errx(1, "invalid sval for %s",
2067 errx(1, "invalid type for %s", var
);
2076 config_parse(char *filename
, int runtime
)
2079 char *line
, *cp
, *var
, *val
;
2080 size_t len
, lineno
= 0;
2082 char file
[PATH_MAX
];
2085 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2087 if (filename
== NULL
)
2090 if (runtime
&& runtime_settings
[0] != '\0') {
2091 snprintf(file
, sizeof file
, "%s/%s",
2092 work_dir
, runtime_settings
);
2093 if (stat(file
, &sb
)) {
2094 warnx("runtime file doesn't exist, creating it");
2095 if ((f
= fopen(file
, "w")) == NULL
)
2097 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2101 strlcpy(file
, filename
, sizeof file
);
2103 if ((config
= fopen(file
, "r")) == NULL
) {
2104 warn("config_parse: cannot open %s", filename
);
2109 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2110 if (feof(config
) || ferror(config
))
2114 cp
+= (long)strspn(cp
, WS
);
2115 if (cp
[0] == '\0') {
2121 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2122 startpage_add("invalid configuration file entry: %s",
2125 cp
+= (long)strspn(cp
, WS
);
2127 if ((val
= strsep(&cp
, "\0")) == NULL
)
2130 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2131 handled
= settings_add(var
, val
);
2133 startpage_add("invalid configuration file entry: %s=%s",
2143 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2149 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2153 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2156 JSStringGetUTF8CString(jsref
, s
, l
);
2157 JSStringRelease(jsref
);
2163 disable_hints(struct tab
*t
)
2165 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2166 bzero(t
->hint_num
, sizeof t
->hint_num
);
2167 run_script(t
, "vimprobable_clear()");
2169 t
->hint_mode
= XT_HINT_NONE
;
2173 enable_hints(struct tab
*t
)
2175 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2176 run_script(t
, "vimprobable_show_hints()");
2178 t
->hint_mode
= XT_HINT_NONE
;
2181 #define XT_JS_OPEN ("open;")
2182 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2183 #define XT_JS_FIRE ("fire;")
2184 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2185 #define XT_JS_FOUND ("found;")
2186 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2189 run_script(struct tab
*t
, char *s
)
2191 JSGlobalContextRef ctx
;
2192 WebKitWebFrame
*frame
;
2194 JSValueRef val
, exception
;
2197 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2198 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2200 frame
= webkit_web_view_get_main_frame(t
->wv
);
2201 ctx
= webkit_web_frame_get_global_context(frame
);
2203 str
= JSStringCreateWithUTF8CString(s
);
2204 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2205 NULL
, 0, &exception
);
2206 JSStringRelease(str
);
2208 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2210 es
= js_ref_to_string(ctx
, exception
);
2211 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2215 es
= js_ref_to_string(ctx
, val
);
2216 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2218 /* handle return value right here */
2219 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2222 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2225 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2226 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2227 &es
[XT_JS_FIRE_LEN
]);
2232 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2233 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2244 hint(struct tab
*t
, struct karg
*args
)
2247 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2249 if (t
->hints_on
== 0)
2258 apply_style(struct tab
*t
)
2260 g_object_set(G_OBJECT(t
->settings
),
2261 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2265 userstyle(struct tab
*t
, struct karg
*args
)
2267 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2271 g_object_set(G_OBJECT(t
->settings
),
2272 "user-stylesheet-uri", NULL
, (char *)NULL
);
2281 * Doesn't work fully, due to the following bug:
2282 * https://bugs.webkit.org/show_bug.cgi?id=51747
2285 restore_global_history(void)
2287 char file
[PATH_MAX
];
2292 const char delim
[3] = {'\\', '\\', '\0'};
2294 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2296 if ((f
= fopen(file
, "r")) == NULL
) {
2297 warnx("%s: fopen", __func__
);
2302 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2303 if (feof(f
) || ferror(f
))
2306 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2307 if (feof(f
) || ferror(f
)) {
2309 warnx("%s: broken history file\n", __func__
);
2313 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2314 webkit_web_history_item_new_with_data(uri
, title
);
2315 h
= g_malloc(sizeof(struct history
));
2316 h
->uri
= g_strdup(uri
);
2317 h
->title
= g_strdup(title
);
2318 RB_INSERT(history_list
, &hl
, h
);
2319 completion_add_uri(h
->uri
);
2321 warnx("%s: failed to restore history\n", __func__
);
2337 save_global_history_to_disk(struct tab
*t
)
2339 char file
[PATH_MAX
];
2343 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2345 if ((f
= fopen(file
, "w")) == NULL
) {
2346 show_oops(t
, "%s: global history file: %s",
2347 __func__
, strerror(errno
));
2351 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2352 if (h
->uri
&& h
->title
)
2353 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2362 quit(struct tab
*t
, struct karg
*args
)
2364 if (save_global_history
)
2365 save_global_history_to_disk(t
);
2373 open_tabs(struct tab
*t
, struct karg
*a
)
2375 char file
[PATH_MAX
];
2379 struct tab
*ti
, *tt
;
2384 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2385 if ((f
= fopen(file
, "r")) == NULL
)
2388 ti
= TAILQ_LAST(&tabs
, tab_list
);
2391 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2392 if (feof(f
) || ferror(f
))
2395 /* retrieve session name */
2396 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2397 strlcpy(named_session
,
2398 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2399 sizeof named_session
);
2403 if (uri
&& strlen(uri
))
2404 create_new_tab(uri
, NULL
, 1, -1);
2410 /* close open tabs */
2411 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2413 tt
= TAILQ_FIRST(&tabs
);
2433 restore_saved_tabs(void)
2435 char file
[PATH_MAX
];
2436 int unlink_file
= 0;
2441 snprintf(file
, sizeof file
, "%s/%s",
2442 sessions_dir
, XT_RESTART_TABS_FILE
);
2443 if (stat(file
, &sb
) == -1)
2444 a
.s
= XT_SAVED_TABS_FILE
;
2447 a
.s
= XT_RESTART_TABS_FILE
;
2450 a
.i
= XT_SES_DONOTHING
;
2451 rv
= open_tabs(NULL
, &a
);
2460 save_tabs(struct tab
*t
, struct karg
*a
)
2462 char file
[PATH_MAX
];
2464 int num_tabs
= 0, i
;
2465 struct tab
**stabs
= NULL
;
2470 snprintf(file
, sizeof file
, "%s/%s",
2471 sessions_dir
, named_session
);
2473 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2475 if ((f
= fopen(file
, "w")) == NULL
) {
2476 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2480 /* save session name */
2481 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2483 /* Save tabs, in the order they are arranged in the notebook. */
2484 num_tabs
= sort_tabs_by_page_num(&stabs
);
2486 for (i
= 0; i
< num_tabs
; i
++)
2488 if (get_uri(stabs
[i
]) != NULL
)
2489 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2490 else if (gtk_entry_get_text(GTK_ENTRY(
2491 stabs
[i
]->uri_entry
)))
2492 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2493 stabs
[i
]->uri_entry
)));
2498 /* try and make sure this gets to disk NOW. XXX Backup first? */
2499 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2500 show_oops(t
, "May not have managed to save session: %s",
2510 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2522 run_page_script(struct tab
*t
, struct karg
*args
)
2525 char *tmp
, script
[PATH_MAX
];
2527 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2528 if (tmp
[0] == '\0') {
2529 show_oops(t
, "no script specified");
2533 if ((uri
= get_uri(t
)) == NULL
) {
2534 show_oops(t
, "tab is empty, not running script");
2539 snprintf(script
, sizeof script
, "%s/%s",
2540 pwd
->pw_dir
, &tmp
[1]);
2542 strlcpy(script
, tmp
, sizeof script
);
2546 show_oops(t
, "can't fork to run script");
2556 execlp(script
, script
, uri
, (void *)NULL
);
2566 yank_uri(struct tab
*t
, struct karg
*args
)
2569 GtkClipboard
*clipboard
;
2571 if ((uri
= get_uri(t
)) == NULL
)
2574 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2575 gtk_clipboard_set_text(clipboard
, uri
, -1);
2581 paste_uri(struct tab
*t
, struct karg
*args
)
2583 GtkClipboard
*clipboard
;
2584 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2586 gchar
*p
= NULL
, *uri
;
2588 /* try primary clipboard first */
2589 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2590 p
= gtk_clipboard_wait_for_text(clipboard
);
2592 /* if it failed get whatever text is in cut_buffer0 */
2593 if (p
== NULL
&& xterm_workaround
)
2594 if (gdk_property_get(gdk_get_default_root_window(),
2596 gdk_atom_intern("STRING", FALSE
),
2598 1024 * 1024 /* picked out of my butt */,
2604 /* yes sir, we need to NUL the string */
2610 while (*uri
&& isspace(*uri
))
2612 if (strlen(uri
) == 0) {
2613 show_oops(t
, "empty paste buffer");
2616 if (guess_search
== 0 && valid_url_type(uri
)) {
2617 /* we can be clever and paste this in search box */
2618 show_oops(t
, "not a valid URL");
2622 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2624 else if (args
->i
== XT_PASTE_NEW_TAB
)
2625 create_new_tab(uri
, NULL
, 1, -1);
2636 find_domain(const gchar
*s
, int toplevel
)
2644 uri
= soup_uri_new(s
);
2646 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2650 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2651 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2652 while(--p
>= uri
->host
&& *p
!= '.');
2659 if (uri
->port
== 80)
2660 ret
= g_strdup_printf(".%s", p
);
2662 ret
= g_strdup_printf(".%s:%d", p
, uri
->port
);
2670 toggle_cwl(struct tab
*t
, struct karg
*args
)
2681 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2683 if (uri
== NULL
|| dom
== NULL
||
2684 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2685 show_oops(t
, "Can't toggle domain in cookie white list");
2688 d
= wl_find(dom
, &c_wl
);
2695 if (args
->i
& XT_WL_TOGGLE
)
2697 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2699 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2703 /* enable cookies for domain */
2704 wl_add(dom
, &c_wl
, 0);
2706 /* disable cookies for domain */
2707 RB_REMOVE(domain_list
, &c_wl
, d
);
2709 if (args
->i
& XT_WL_RELOAD
)
2710 webkit_web_view_reload(t
->wv
);
2718 toggle_js(struct tab
*t
, struct karg
*args
)
2728 g_object_get(G_OBJECT(t
->settings
),
2729 "enable-scripts", &es
, (char *)NULL
);
2730 if (args
->i
& XT_WL_TOGGLE
)
2732 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2734 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2740 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2742 if (uri
== NULL
|| dom
== NULL
||
2743 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2744 show_oops(t
, "Can't toggle domain in JavaScript white list");
2749 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2750 wl_add(dom
, &js_wl
, 0 /* session */);
2752 d
= wl_find(dom
, &js_wl
);
2754 RB_REMOVE(domain_list
, &js_wl
, d
);
2755 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2757 g_object_set(G_OBJECT(t
->settings
),
2758 "enable-scripts", es
, (char *)NULL
);
2759 g_object_set(G_OBJECT(t
->settings
),
2760 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2761 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2763 if (args
->i
& XT_WL_RELOAD
)
2764 webkit_web_view_reload(t
->wv
);
2772 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2776 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2779 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2784 toggle_src(struct tab
*t
, struct karg
*args
)
2791 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2792 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2793 webkit_web_view_reload(t
->wv
);
2799 focus_webview(struct tab
*t
)
2804 /* only grab focus if we are visible */
2805 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2806 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2810 focus(struct tab
*t
, struct karg
*args
)
2812 if (t
== NULL
|| args
== NULL
)
2818 if (args
->i
== XT_FOCUS_URI
)
2819 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2820 else if (args
->i
== XT_FOCUS_SEARCH
)
2821 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2827 stats(struct tab
*t
, struct karg
*args
)
2829 char *page
, *body
, *s
, line
[64 * 1024];
2830 uint64_t line_count
= 0;
2834 show_oops(NULL
, "stats invalid parameters");
2837 if (save_rejected_cookies
) {
2838 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2840 s
= fgets(line
, sizeof line
, r_cookie_f
);
2841 if (s
== NULL
|| feof(r_cookie_f
) ||
2847 snprintf(line
, sizeof line
,
2848 "<br/>Cookies blocked(*) total: %llu", line_count
);
2850 show_oops(t
, "Can't open blocked cookies file: %s",
2854 body
= g_strdup_printf(
2855 "Cookies blocked(*) this session: %llu"
2857 "<p><small><b>*</b> results vary based on settings</small></p>",
2861 page
= get_html_page("Statistics", body
, "", 0);
2864 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2871 marco(struct tab
*t
, struct karg
*args
)
2873 char *page
, line
[64 * 1024];
2877 show_oops(NULL
, "marco invalid parameters");
2880 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2882 page
= get_html_page("Marco Sez...", line
, "", 0);
2884 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2891 blank(struct tab
*t
, struct karg
*args
)
2894 show_oops(NULL
, "blank invalid parameters");
2896 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2902 about(struct tab
*t
, struct karg
*args
)
2907 show_oops(NULL
, "about invalid parameters");
2909 body
= g_strdup_printf("<b>Version: %s</b><p>"
2912 "<li>Marco Peereboom <marco@peereboom.us></li>"
2913 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2914 "<li>Edd Barrett <vext01@gmail.com> </li>"
2915 "<li>Todd T. Fries <todd@fries.net> </li>"
2916 "<li>Raphael Graf <r@undefined.ch> </li>"
2918 "Copyrights and licenses can be found on the XXXTerm "
2919 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2923 page
= get_html_page("About", body
, "", 0);
2926 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2933 help(struct tab
*t
, struct karg
*args
)
2935 char *page
, *head
, *body
;
2938 show_oops(NULL
, "help invalid parameters");
2940 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2941 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2943 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
2944 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2945 "cgi-bin/man-cgi?xxxterm</a>";
2947 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
2949 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2956 startpage(struct tab
*t
, struct karg
*args
)
2958 char *page
, *body
, *b
;
2962 show_oops(NULL
, "startpage invalid parameters");
2964 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
2966 TAILQ_FOREACH(s
, &spl
, entry
) {
2968 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
2972 page
= get_html_page("Startup Exception", body
, "", 0);
2975 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
2982 startpage_add(const char *fmt
, ...)
2992 if (vasprintf(&msg
, fmt
, ap
) == -1)
2993 errx(1, "startpage_add failed");
2996 s
= g_malloc0(sizeof *s
);
2999 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3003 * update all favorite tabs apart from one. Pass NULL if
3004 * you want to update all.
3007 update_favorite_tabs(struct tab
*apart_from
)
3010 if (!updating_fl_tabs
) {
3011 updating_fl_tabs
= 1; /* stop infinite recursion */
3012 TAILQ_FOREACH(t
, &tabs
, entry
)
3013 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3014 && (t
!= apart_from
))
3015 xtp_page_fl(t
, NULL
);
3016 updating_fl_tabs
= 0;
3020 /* show a list of favorites (bookmarks) */
3022 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3024 char file
[PATH_MAX
];
3026 char *uri
= NULL
, *title
= NULL
;
3027 size_t len
, lineno
= 0;
3029 char *body
, *tmp
, *page
= NULL
;
3030 const char delim
[3] = {'\\', '\\', '\0'};
3032 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3035 warn("%s: bad param", __func__
);
3037 /* new session key */
3038 if (!updating_fl_tabs
)
3039 generate_xtp_session_key(&fl_session_key
);
3041 /* open favorites */
3042 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3043 if ((f
= fopen(file
, "r")) == NULL
) {
3044 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3049 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3050 "<th style='width: 40px'>#</th><th>Link</th>"
3051 "<th style='width: 40px'>Rm</th></tr>\n");
3054 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3055 if (feof(f
) || ferror(f
))
3057 if (strlen(title
) == 0 || title
[0] == '#') {
3063 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3064 if (feof(f
) || ferror(f
)) {
3065 show_oops(t
, "favorites file corrupt");
3071 body
= g_strdup_printf("%s<tr>"
3073 "<td><a href='%s'>%s</a></td>"
3074 "<td style='text-align: center'>"
3075 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3077 body
, i
, uri
, title
,
3078 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3090 /* if none, say so */
3093 body
= g_strdup_printf("%s<tr>"
3094 "<td colspan='3' style='text-align: center'>"
3095 "No favorites - To add one use the 'favadd' command."
3096 "</td></tr>", body
);
3101 body
= g_strdup_printf("%s</table>", body
);
3111 page
= get_html_page("Favorites", body
, "", 1);
3112 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3116 update_favorite_tabs(t
);
3125 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3126 size_t cert_count
, char *title
)
3128 gnutls_datum_t cinfo
;
3132 body
= g_strdup("");
3134 for (i
= 0; i
< cert_count
; i
++) {
3135 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3140 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3141 body
, i
, cinfo
.data
);
3142 gnutls_free(cinfo
.data
);
3146 tmp
= get_html_page(title
, body
, "", 0);
3149 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3154 ca_cmd(struct tab
*t
, struct karg
*args
)
3157 int rv
= 1, certs
= 0, certs_read
;
3160 gnutls_x509_crt_t
*c
= NULL
;
3161 char *certs_buf
= NULL
, *s
;
3163 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3164 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3168 if (fstat(fileno(f
), &sb
) == -1) {
3169 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3173 certs_buf
= g_malloc(sb
.st_size
+ 1);
3174 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3175 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3178 certs_buf
[sb
.st_size
] = '\0';
3181 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3183 s
+= strlen("BEGIN CERTIFICATE");
3186 bzero(&dt
, sizeof dt
);
3187 dt
.data
= (unsigned char *)certs_buf
;
3188 dt
.size
= sb
.st_size
;
3189 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3190 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3191 GNUTLS_X509_FMT_PEM
, 0);
3192 if (certs_read
<= 0) {
3193 show_oops(t
, "No cert(s) available");
3196 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3209 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3213 struct addrinfo hints
, *res
= NULL
, *ai
;
3214 int rv
= -1, s
= -1, on
, error
;
3217 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3218 show_oops(t
, "invalid URI");
3222 su
= soup_uri_new(uri
);
3224 show_oops(t
, "invalid soup URI");
3227 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3228 show_oops(t
, "invalid HTTPS URI");
3232 snprintf(port
, sizeof port
, "%d", su
->port
);
3233 bzero(&hints
, sizeof(struct addrinfo
));
3234 hints
.ai_flags
= AI_CANONNAME
;
3235 hints
.ai_family
= AF_UNSPEC
;
3236 hints
.ai_socktype
= SOCK_STREAM
;
3238 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3239 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3243 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3244 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3247 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3249 show_oops(t
, "socket failed: %s", strerror(errno
));
3252 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3253 sizeof(on
)) == -1) {
3254 show_oops(t
, "setsockopt failed: %s", strerror(errno
));
3257 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == -1) {
3258 show_oops(t
, "connect failed: %s", strerror(errno
));
3266 strlcpy(domain
, su
->host
, domain_sz
);
3273 if (rv
== -1 && s
!= -1)
3280 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3283 gnutls_deinit(gsession
);
3285 gnutls_certificate_free_credentials(xcred
);
3291 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3292 gnutls_certificate_credentials_t
*xc
)
3294 gnutls_certificate_credentials_t xcred
;
3295 gnutls_session_t gsession
;
3298 if (gs
== NULL
|| xc
== NULL
)
3304 gnutls_certificate_allocate_credentials(&xcred
);
3305 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3306 GNUTLS_X509_FMT_PEM
);
3308 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3309 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3310 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3311 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3312 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3313 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3315 gnutls_error_is_fatal(rv
),
3316 gnutls_strerror_name(rv
));
3317 stop_tls(gsession
, xcred
);
3321 gnutls_credentials_type_t cred
;
3322 cred
= gnutls_auth_get_type(gsession
);
3323 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3324 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3325 stop_tls(gsession
, xcred
);
3337 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3341 const gnutls_datum_t
*cl
;
3342 gnutls_x509_crt_t
*all_certs
;
3345 if (certs
== NULL
|| cert_count
== NULL
)
3347 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3349 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3353 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3354 for (i
= 0; i
< len
; i
++) {
3355 gnutls_x509_crt_init(&all_certs
[i
]);
3356 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3357 GNUTLS_X509_FMT_PEM
< 0)) {
3371 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3375 for (i
= 0; i
< cert_count
; i
++)
3376 gnutls_x509_crt_deinit(certs
[i
]);
3381 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3383 GdkColor c_text
, c_base
;
3385 gdk_color_parse(text
, &c_text
);
3386 gdk_color_parse(base
, &c_base
);
3388 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3389 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3390 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3391 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3393 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3394 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3395 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3396 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3400 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3401 size_t cert_count
, char *domain
)
3404 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3409 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3412 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3413 if ((f
= fopen(file
, "w")) == NULL
) {
3414 show_oops(t
, "Can't create cert file %s %s",
3415 file
, strerror(errno
));
3419 for (i
= 0; i
< cert_count
; i
++) {
3420 cert_buf_sz
= sizeof cert_buf
;
3421 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3422 cert_buf
, &cert_buf_sz
)) {
3423 show_oops(t
, "gnutls_x509_crt_export failed");
3426 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3427 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3432 /* not the best spot but oh well */
3433 gdk_color_parse("lightblue", &color
);
3434 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3435 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3448 load_compare_cert(struct tab
*t
, struct karg
*args
)
3451 char domain
[8182], file
[PATH_MAX
];
3452 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3453 int s
= -1, i
, error
;
3455 size_t cert_buf_sz
, cert_count
;
3456 enum cert_trust rv
= CERT_UNTRUSTED
;
3458 gnutls_session_t gsession
;
3459 gnutls_x509_crt_t
*certs
;
3460 gnutls_certificate_credentials_t xcred
;
3462 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3467 if ((uri
= get_uri(t
)) == NULL
)
3469 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3471 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3473 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3476 if (start_tls(t
, s
, &gsession
, &xcred
))
3478 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3480 /* verify certs in case cert file doesn't exist */
3481 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3483 show_oops(t
, "Invalid certificates");
3488 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3489 show_oops(t
, "Can't get connection certificates");
3493 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3494 if ((f
= fopen(file
, "r")) == NULL
) {
3500 for (i
= 0; i
< cert_count
; i
++) {
3501 cert_buf_sz
= sizeof cert_buf
;
3502 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3503 cert_buf
, &cert_buf_sz
)) {
3506 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3507 rv
= CERT_BAD
; /* critical */
3510 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3511 rv
= CERT_BAD
; /* critical */
3520 free_connection_certs(certs
, cert_count
);
3522 /* we close the socket first for speed */
3526 /* only complain if we didn't save it locally */
3527 if (error
&& rv
!= CERT_LOCAL
) {
3528 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3529 if (error
& GNUTLS_CERT_INVALID
)
3530 strlcat(serr
, "invalid, ", sizeof serr
);
3531 if (error
& GNUTLS_CERT_REVOKED
)
3532 strlcat(serr
, "revoked, ", sizeof serr
);
3533 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3534 strlcat(serr
, "signer not found, ", sizeof serr
);
3535 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3536 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3537 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3538 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3539 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3540 strlcat(serr
, "not activated, ", sizeof serr
);
3541 if (error
& GNUTLS_CERT_EXPIRED
)
3542 strlcat(serr
, "expired, ", sizeof serr
);
3543 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3544 if (serr
[i
] == ',') {
3551 stop_tls(gsession
, xcred
);
3557 cert_cmd(struct tab
*t
, struct karg
*args
)
3563 gnutls_session_t gsession
;
3564 gnutls_x509_crt_t
*certs
;
3565 gnutls_certificate_credentials_t xcred
;
3570 if (ssl_ca_file
== NULL
) {
3571 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3575 if ((uri
= get_uri(t
)) == NULL
) {
3576 show_oops(t
, "Invalid URI");
3580 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3581 show_oops(t
, "Invalid certificate URI: %s", uri
);
3586 if (start_tls(t
, s
, &gsession
, &xcred
))
3590 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3591 show_oops(t
, "get_connection_certs failed");
3595 if (args
->i
& XT_SHOW
)
3596 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3597 else if (args
->i
& XT_SAVE
)
3598 save_certs(t
, certs
, cert_count
, domain
);
3600 free_connection_certs(certs
, cert_count
);
3602 /* we close the socket first for speed */
3605 stop_tls(gsession
, xcred
);
3611 remove_cookie(int index
)
3617 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3619 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3621 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3625 print_cookie("remove cookie", c
);
3626 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3631 soup_cookies_free(cf
);
3637 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3642 body
= g_strdup("");
3645 if (args
->i
& XT_WL_PERSISTENT
) {
3647 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3649 RB_FOREACH(d
, domain_list
, wl
) {
3653 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3659 if (args
->i
& XT_WL_SESSION
) {
3661 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3663 RB_FOREACH(d
, domain_list
, wl
) {
3667 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3672 tmp
= get_html_page(title
, body
, "", 0);
3675 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3677 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3683 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3685 char file
[PATH_MAX
];
3687 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3696 if (t
== NULL
|| args
== NULL
)
3699 if (runtime_settings
[0] == '\0')
3702 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3703 if ((f
= fopen(file
, "r+")) == NULL
)
3707 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3708 if (uri
== NULL
|| dom
== NULL
||
3709 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3710 show_oops(t
, "Can't add domain to %s white list",
3711 js
? "JavaScript" : "cookie");
3715 /* we don't want to save :port number */
3716 p
= g_strrstr(dom
, ":");
3720 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3723 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3726 if (!strcmp(line
, lt
))
3732 fprintf(f
, "%s\n", lt
);
3737 d
= wl_find(dom
, &js_wl
);
3739 settings_add("js_wl", dom
);
3740 d
= wl_find(dom
, &js_wl
);
3744 d
= wl_find(dom
, &c_wl
);
3746 settings_add("cookie_wl", dom
);
3747 d
= wl_find(dom
, &c_wl
);
3751 /* find and add to persistent jar */
3752 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3753 for (;cf
; cf
= cf
->next
) {
3755 if (!strcmp(dom
, ci
->domain
) ||
3756 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3757 c
= soup_cookie_copy(ci
);
3758 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3761 soup_cookies_free(cf
);
3779 js_show_wl(struct tab
*t
, struct karg
*args
)
3781 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3782 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3788 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3790 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3791 wl_show(t
, args
, "Cookie White List", &c_wl
);
3797 cookie_cmd(struct tab
*t
, struct karg
*args
)
3799 if (args
->i
& XT_SHOW
)
3800 wl_show(t
, args
, "Cookie White List", &c_wl
);
3801 else if (args
->i
& XT_WL_TOGGLE
) {
3802 args
->i
|= XT_WL_RELOAD
;
3803 toggle_cwl(t
, args
);
3804 } else if (args
->i
& XT_SAVE
) {
3805 args
->i
|= XT_WL_RELOAD
;
3806 wl_save(t
, args
, 0);
3807 } else if (args
->i
& XT_DELETE
)
3808 show_oops(t
, "'cookie delete' currently unimplemented");
3814 js_cmd(struct tab
*t
, struct karg
*args
)
3816 if (args
->i
& XT_SHOW
)
3817 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3818 else if (args
->i
& XT_SAVE
) {
3819 args
->i
|= XT_WL_RELOAD
;
3820 wl_save(t
, args
, 1);
3821 } else if (args
->i
& XT_WL_TOGGLE
) {
3822 args
->i
|= XT_WL_RELOAD
;
3824 } else if (args
->i
& XT_DELETE
)
3825 show_oops(t
, "'js delete' currently unimplemented");
3831 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3833 js_toggle_cb(t
->js_toggle
, t
);
3839 add_favorite(struct tab
*t
, struct karg
*args
)
3841 char file
[PATH_MAX
];
3844 size_t urilen
, linelen
;
3845 const gchar
*uri
, *title
;
3850 /* don't allow adding of xtp pages to favorites */
3851 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3852 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3856 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3857 if ((f
= fopen(file
, "r+")) == NULL
) {
3858 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3862 title
= get_title(t
, FALSE
);
3865 if (title
== NULL
|| uri
== NULL
) {
3866 show_oops(t
, "can't add page to favorites");
3870 urilen
= strlen(uri
);
3873 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3874 if (feof(f
) || ferror(f
))
3877 if (linelen
== urilen
&& !strcmp(line
, uri
))
3884 fprintf(f
, "\n%s\n%s", title
, uri
);
3890 update_favorite_tabs(NULL
);
3896 navaction(struct tab
*t
, struct karg
*args
)
3898 WebKitWebHistoryItem
*item
;
3900 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3901 t
->tab_id
, args
->i
);
3903 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3906 if (args
->i
== XT_NAV_BACK
)
3907 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3909 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3911 return (XT_CB_PASSTHROUGH
);
3912 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3914 return (XT_CB_PASSTHROUGH
);
3920 item
= webkit_web_back_forward_list_get_back_item(t
->bfl
);
3922 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3924 case XT_NAV_FORWARD
:
3926 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3928 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3931 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3933 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3936 return (XT_CB_PASSTHROUGH
);
3940 move(struct tab
*t
, struct karg
*args
)
3942 GtkAdjustment
*adjust
;
3943 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3949 case XT_MOVE_BOTTOM
:
3951 case XT_MOVE_PAGEDOWN
:
3952 case XT_MOVE_PAGEUP
:
3953 case XT_MOVE_HALFDOWN
:
3954 case XT_MOVE_HALFUP
:
3955 case XT_MOVE_PERCENT
:
3956 adjust
= t
->adjust_v
;
3959 adjust
= t
->adjust_h
;
3963 pos
= gtk_adjustment_get_value(adjust
);
3964 ps
= gtk_adjustment_get_page_size(adjust
);
3965 upper
= gtk_adjustment_get_upper(adjust
);
3966 lower
= gtk_adjustment_get_lower(adjust
);
3967 si
= gtk_adjustment_get_step_increment(adjust
);
3968 pi
= gtk_adjustment_get_page_increment(adjust
);
3971 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3972 "max %f si %f pi %f\n",
3973 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3974 pos
, ps
, upper
, lower
, max
, si
, pi
);
3980 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3985 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3987 case XT_MOVE_BOTTOM
:
3988 case XT_MOVE_FARRIGHT
:
3989 gtk_adjustment_set_value(adjust
, max
);
3992 case XT_MOVE_FARLEFT
:
3993 gtk_adjustment_set_value(adjust
, lower
);
3995 case XT_MOVE_PAGEDOWN
:
3997 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3999 case XT_MOVE_PAGEUP
:
4001 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4003 case XT_MOVE_HALFDOWN
:
4005 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4007 case XT_MOVE_HALFUP
:
4009 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4011 case XT_MOVE_PERCENT
:
4012 percent
= atoi(args
->s
) / 100.0;
4013 pos
= max
* percent
;
4014 if (pos
< 0.0 || pos
> max
)
4016 gtk_adjustment_set_value(adjust
, pos
);
4019 return (XT_CB_PASSTHROUGH
);
4022 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4024 return (XT_CB_HANDLED
);
4028 url_set_visibility(void)
4032 TAILQ_FOREACH(t
, &tabs
, entry
)
4033 if (show_url
== 0) {
4034 gtk_widget_hide(t
->toolbar
);
4037 gtk_widget_show(t
->toolbar
);
4041 notebook_tab_set_visibility(void)
4043 if (show_tabs
== 0) {
4044 gtk_widget_hide(tab_bar
);
4045 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4047 if (tab_style
== XT_TABS_NORMAL
) {
4048 gtk_widget_hide(tab_bar
);
4049 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4050 } else if (tab_style
== XT_TABS_COMPACT
) {
4051 gtk_widget_show(tab_bar
);
4052 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4058 statusbar_set_visibility(void)
4062 TAILQ_FOREACH(t
, &tabs
, entry
)
4063 if (show_statusbar
== 0) {
4064 gtk_widget_hide(t
->statusbar_box
);
4067 gtk_widget_show(t
->statusbar_box
);
4071 url_set(struct tab
*t
, int enable_url_entry
)
4076 show_url
= enable_url_entry
;
4078 if (enable_url_entry
) {
4079 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4080 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4081 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4083 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4084 GTK_ENTRY_ICON_PRIMARY
);
4086 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4087 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4088 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4089 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4095 fullscreen(struct tab
*t
, struct karg
*args
)
4097 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4100 return (XT_CB_PASSTHROUGH
);
4102 if (show_url
== 0) {
4110 url_set_visibility();
4111 notebook_tab_set_visibility();
4113 return (XT_CB_HANDLED
);
4117 statustoggle(struct tab
*t
, struct karg
*args
)
4119 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4121 if (show_statusbar
== 1) {
4123 statusbar_set_visibility();
4124 } else if (show_statusbar
== 0) {
4126 statusbar_set_visibility();
4128 return (XT_CB_HANDLED
);
4132 urlaction(struct tab
*t
, struct karg
*args
)
4134 int rv
= XT_CB_HANDLED
;
4136 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4139 return (XT_CB_PASSTHROUGH
);
4143 if (show_url
== 0) {
4145 url_set_visibility();
4149 if (show_url
== 1) {
4151 url_set_visibility();
4159 tabaction(struct tab
*t
, struct karg
*args
)
4161 int rv
= XT_CB_HANDLED
;
4162 char *url
= args
->s
;
4166 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4169 return (XT_CB_PASSTHROUGH
);
4173 if (strlen(url
) > 0)
4174 create_new_tab(url
, NULL
, 1, args
->precount
);
4176 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4179 if (args
->precount
< 0)
4182 TAILQ_FOREACH(tt
, &tabs
, entry
)
4183 if (tt
->tab_id
== args
->precount
- 1) {
4188 case XT_TAB_DELQUIT
:
4189 if (gtk_notebook_get_n_pages(notebook
) > 1)
4195 if (strlen(url
) > 0)
4198 rv
= XT_CB_PASSTHROUGH
;
4204 if (show_tabs
== 0) {
4206 notebook_tab_set_visibility();
4210 if (show_tabs
== 1) {
4212 notebook_tab_set_visibility();
4215 case XT_TAB_NEXTSTYLE
:
4216 if (tab_style
== XT_TABS_NORMAL
) {
4217 tab_style
= XT_TABS_COMPACT
;
4218 recolor_compact_tabs();
4221 tab_style
= XT_TABS_NORMAL
;
4222 notebook_tab_set_visibility();
4224 case XT_TAB_UNDO_CLOSE
:
4225 if (undo_count
== 0) {
4226 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4231 u
= TAILQ_FIRST(&undos
);
4232 create_new_tab(u
->uri
, u
, 1, -1);
4234 TAILQ_REMOVE(&undos
, u
, entry
);
4236 /* u->history is freed in create_new_tab() */
4241 rv
= XT_CB_PASSTHROUGH
;
4255 resizetab(struct tab
*t
, struct karg
*args
)
4257 if (t
== NULL
|| args
== NULL
) {
4258 show_oops(NULL
, "resizetab invalid parameters");
4259 return (XT_CB_PASSTHROUGH
);
4262 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4263 t
->tab_id
, args
->i
);
4265 setzoom_webkit(t
, args
->i
);
4267 return (XT_CB_HANDLED
);
4271 movetab(struct tab
*t
, struct karg
*args
)
4275 if (t
== NULL
|| args
== NULL
) {
4276 show_oops(NULL
, "movetab invalid parameters");
4277 return (XT_CB_PASSTHROUGH
);
4280 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4281 t
->tab_id
, args
->i
);
4283 if (args
->i
>= XT_TAB_INVALID
)
4284 return (XT_CB_PASSTHROUGH
);
4286 if (TAILQ_EMPTY(&tabs
))
4287 return (XT_CB_PASSTHROUGH
);
4289 n
= gtk_notebook_get_n_pages(notebook
);
4290 dest
= gtk_notebook_get_current_page(notebook
);
4294 if (args
->precount
< 0)
4295 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4297 dest
= args
->precount
- 1;
4301 if (args
->precount
< 0)
4304 dest
-= args
->precount
% n
;
4317 return (XT_CB_PASSTHROUGH
);
4320 if (dest
< 0 || dest
>= n
)
4321 return (XT_CB_PASSTHROUGH
);
4322 if (t
->tab_id
== dest
) {
4323 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4324 return (XT_CB_HANDLED
);
4327 set_current_tab(dest
);
4329 return (XT_CB_HANDLED
);
4335 command(struct tab
*t
, struct karg
*args
)
4337 char *s
= NULL
, *ss
= NULL
;
4341 if (t
== NULL
|| args
== NULL
) {
4342 show_oops(NULL
, "command invalid parameters");
4343 return (XT_CB_PASSTHROUGH
);
4354 if (cmd_prefix
== 0)
4357 ss
= g_strdup_printf(":%d", cmd_prefix
);
4368 case XT_CMD_OPEN_CURRENT
:
4371 case XT_CMD_TABNEW_CURRENT
:
4372 if (!s
) /* FALL THROUGH? */
4374 if ((uri
= get_uri(t
)) != NULL
) {
4375 ss
= g_strdup_printf("%s%s", s
, uri
);
4380 show_oops(t
, "command: invalid opcode %d", args
->i
);
4381 return (XT_CB_PASSTHROUGH
);
4384 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4386 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4387 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4388 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4390 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4391 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4396 return (XT_CB_HANDLED
);
4400 * Return a new string with a download row (in html)
4401 * appended. Old string is freed.
4404 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4407 WebKitDownloadStatus stat
;
4408 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4410 char cur_sz
[FMT_SCALED_STRSIZE
];
4411 char tot_sz
[FMT_SCALED_STRSIZE
];
4414 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4416 /* All actions wil take this form:
4417 * xxxt://class/seskey
4419 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4420 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4422 stat
= webkit_download_get_status(dl
->download
);
4425 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4426 status_html
= g_strdup_printf("Finished");
4427 cmd_html
= g_strdup_printf(
4428 "<a href='%s%d/%d'>Remove</a>",
4429 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4431 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4432 /* gather size info */
4433 progress
= 100 * webkit_download_get_progress(dl
->download
);
4436 webkit_download_get_current_size(dl
->download
), cur_sz
);
4438 webkit_download_get_total_size(dl
->download
), tot_sz
);
4440 status_html
= g_strdup_printf(
4441 "<div style='width: 100%%' align='center'>"
4442 "<div class='progress-outer'>"
4443 "<div class='progress-inner' style='width: %.2f%%'>"
4444 "</div></div></div>"
4445 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4446 progress
, cur_sz
, tot_sz
, progress
);
4448 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4449 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4453 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4454 status_html
= g_strdup_printf("Cancelled");
4455 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4456 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4458 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4459 status_html
= g_strdup_printf("Error!");
4460 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4461 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4463 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4464 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4465 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4466 status_html
= g_strdup_printf("Starting");
4469 show_oops(t
, "%s: unknown download status", __func__
);
4472 new_html
= g_strdup_printf(
4473 "%s\n<tr><td>%s</td><td>%s</td>"
4474 "<td style='text-align:center'>%s</td></tr>\n",
4475 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4476 status_html
, cmd_html
);
4480 g_free(status_html
);
4491 * update all download tabs apart from one. Pass NULL if
4492 * you want to update all.
4495 update_download_tabs(struct tab
*apart_from
)
4498 if (!updating_dl_tabs
) {
4499 updating_dl_tabs
= 1; /* stop infinite recursion */
4500 TAILQ_FOREACH(t
, &tabs
, entry
)
4501 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4502 && (t
!= apart_from
))
4503 xtp_page_dl(t
, NULL
);
4504 updating_dl_tabs
= 0;
4509 * update all cookie tabs apart from one. Pass NULL if
4510 * you want to update all.
4513 update_cookie_tabs(struct tab
*apart_from
)
4516 if (!updating_cl_tabs
) {
4517 updating_cl_tabs
= 1; /* stop infinite recursion */
4518 TAILQ_FOREACH(t
, &tabs
, entry
)
4519 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4520 && (t
!= apart_from
))
4521 xtp_page_cl(t
, NULL
);
4522 updating_cl_tabs
= 0;
4527 * update all history tabs apart from one. Pass NULL if
4528 * you want to update all.
4531 update_history_tabs(struct tab
*apart_from
)
4535 if (!updating_hl_tabs
) {
4536 updating_hl_tabs
= 1; /* stop infinite recursion */
4537 TAILQ_FOREACH(t
, &tabs
, entry
)
4538 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4539 && (t
!= apart_from
))
4540 xtp_page_hl(t
, NULL
);
4541 updating_hl_tabs
= 0;
4545 /* cookie management XTP page */
4547 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4549 char *body
, *page
, *tmp
;
4550 int i
= 1; /* all ids start 1 */
4551 GSList
*sc
, *pc
, *pc_start
;
4553 char *type
, *table_headers
, *last_domain
;
4555 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4558 show_oops(NULL
, "%s invalid parameters", __func__
);
4562 /* Generate a new session key */
4563 if (!updating_cl_tabs
)
4564 generate_xtp_session_key(&cl_session_key
);
4567 table_headers
= g_strdup_printf("<table><tr>"
4570 "<th style='width:200px'>Value</th>"
4574 "<th>HTTP<br />only</th>"
4575 "<th style='width:40px'>Rm</th></tr>\n");
4577 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4578 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4582 last_domain
= strdup("");
4583 for (; sc
; sc
= sc
->next
) {
4586 if (strcmp(last_domain
, c
->domain
) != 0) {
4589 last_domain
= strdup(c
->domain
);
4593 body
= g_strdup_printf("%s</table>"
4595 body
, c
->domain
, table_headers
);
4599 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4600 c
->domain
, table_headers
);
4605 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4606 if (soup_cookie_equal(pc
->data
, c
)) {
4607 type
= "Session + Persistent";
4612 body
= g_strdup_printf(
4615 "<td style='word-wrap:normal'>%s</td>"
4617 " <textarea rows='4'>%s</textarea>"
4623 "<td style='text-align:center'>"
4624 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4631 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4646 soup_cookies_free(sc
);
4647 soup_cookies_free(pc
);
4649 /* small message if there are none */
4651 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4652 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4655 body
= g_strdup_printf("%s</table>", body
);
4658 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4660 g_free(table_headers
);
4661 g_free(last_domain
);
4663 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4664 update_cookie_tabs(t
);
4672 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4674 char *body
, *page
, *tmp
;
4676 int i
= 1; /* all ids start 1 */
4678 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4681 show_oops(NULL
, "%s invalid parameters", __func__
);
4685 /* Generate a new session key */
4686 if (!updating_hl_tabs
)
4687 generate_xtp_session_key(&hl_session_key
);
4690 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4691 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4693 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4695 body
= g_strdup_printf(
4697 "<td><a href='%s'>%s</a></td>"
4699 "<td style='text-align: center'>"
4700 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4701 body
, h
->uri
, h
->uri
, h
->title
,
4702 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4703 XT_XTP_HL_REMOVE
, i
);
4709 /* small message if there are none */
4712 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4713 "colspan='3'>No History</td></tr>\n", body
);
4718 body
= g_strdup_printf("%s</table>", body
);
4721 page
= get_html_page("History", body
, "", TRUE
);
4725 * update all history manager tabs as the xtp session
4726 * key has now changed. No need to update the current tab.
4727 * Already did that above.
4729 update_history_tabs(t
);
4731 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4738 * Generate a web page detailing the status of any downloads
4741 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4743 struct download
*dl
;
4744 char *body
, *page
, *tmp
;
4748 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4751 show_oops(NULL
, "%s invalid parameters", __func__
);
4756 * Generate a new session key for next page instance.
4757 * This only happens for the top level call to xtp_page_dl()
4758 * in which case updating_dl_tabs is 0.
4760 if (!updating_dl_tabs
)
4761 generate_xtp_session_key(&dl_session_key
);
4763 /* header - with refresh so as to update */
4764 if (refresh_interval
>= 1)
4765 ref
= g_strdup_printf(
4766 "<meta http-equiv='refresh' content='%u"
4767 ";url=%s%d/%s/%d' />\n",
4776 body
= g_strdup_printf("<div align='center'>"
4777 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4778 "</p><table><tr><th style='width: 60%%'>"
4779 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4780 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4782 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4783 body
= xtp_page_dl_row(t
, body
, dl
);
4787 /* message if no downloads in list */
4790 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4791 " style='text-align: center'>"
4792 "No downloads</td></tr>\n", body
);
4797 body
= g_strdup_printf("%s</table></div>", body
);
4800 page
= get_html_page("Downloads", body
, ref
, 1);
4805 * update all download manager tabs as the xtp session
4806 * key has now changed. No need to update the current tab.
4807 * Already did that above.
4809 update_download_tabs(t
);
4811 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4818 search(struct tab
*t
, struct karg
*args
)
4822 if (t
== NULL
|| args
== NULL
) {
4823 show_oops(NULL
, "search invalid parameters");
4826 if (t
->search_text
== NULL
) {
4827 if (global_search
== NULL
)
4828 return (XT_CB_PASSTHROUGH
);
4830 t
->search_text
= g_strdup(global_search
);
4831 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4832 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4836 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4837 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4840 case XT_SEARCH_NEXT
:
4841 d
= t
->search_forward
;
4843 case XT_SEARCH_PREV
:
4844 d
= !t
->search_forward
;
4847 return (XT_CB_PASSTHROUGH
);
4850 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4852 return (XT_CB_HANDLED
);
4855 struct settings_args
{
4861 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4864 struct settings_args
*sa
= cb_args
;
4869 if (s
->flags
& XT_SF_RUNTIME
)
4875 *sa
->body
= g_strdup_printf(
4877 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4878 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4890 set_show(struct tab
*t
, struct karg
*args
)
4892 char *body
, *page
, *tmp
;
4894 struct settings_args sa
;
4896 bzero(&sa
, sizeof sa
);
4900 body
= g_strdup_printf("<div align='center'><table><tr>"
4901 "<th align='left'>Setting</th>"
4902 "<th align='left'>Value</th></tr>\n");
4904 settings_walk(print_setting
, &sa
);
4907 /* small message if there are none */
4910 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4911 "colspan='2'>No settings</td></tr>\n", body
);
4916 body
= g_strdup_printf("%s</table></div>", body
);
4919 page
= get_html_page("Settings", body
, "", 0);
4923 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4927 return (XT_CB_PASSTHROUGH
);
4931 set(struct tab
*t
, struct karg
*args
)
4936 if (args
== NULL
|| args
->s
== NULL
)
4937 return (set_show(t
, args
));
4940 p
= g_strstrip(args
->s
);
4943 return (set_show(t
, args
));
4945 /* we got some sort of string */
4946 val
= g_strrstr(p
, "=");
4949 val
= g_strchomp(val
);
4952 for (i
= 0; i
< LENGTH(rs
); i
++) {
4953 if (strcmp(rs
[i
].name
, p
))
4956 if (rs
[i
].activate
) {
4957 if (rs
[i
].activate(val
))
4958 show_oops(t
, "%s invalid value %s",
4961 show_oops(t
, ":set %s = %s", p
, val
);
4964 show_oops(t
, "not a runtime option: %s", p
);
4968 show_oops(t
, "unknown option: %s", p
);
4972 for (i
= 0; i
< LENGTH(rs
); i
++) {
4973 if (strcmp(rs
[i
].name
, p
))
4976 /* XXX this could use some cleanup */
4977 switch (rs
[i
].type
) {
4980 show_oops(t
, "%s = %d",
4981 rs
[i
].name
, *rs
[i
].ival
);
4982 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4983 show_oops(t
, "%s = %s",
4985 rs
[i
].s
->get(&rs
[i
]));
4986 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4987 show_oops(t
, "%s = ...", rs
[i
].name
);
4989 show_oops(t
, "%s = ", rs
[i
].name
);
4993 show_oops(t
, "%s = %f",
4994 rs
[i
].name
, *rs
[i
].fval
);
4995 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4996 show_oops(t
, "%s = %s",
4998 rs
[i
].s
->get(&rs
[i
]));
4999 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5000 show_oops(t
, "%s = ...", rs
[i
].name
);
5002 show_oops(t
, "%s = ", rs
[i
].name
);
5005 if (rs
[i
].sval
&& *rs
[i
].sval
)
5006 show_oops(t
, "%s = %s",
5007 rs
[i
].name
, *rs
[i
].sval
);
5008 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5009 show_oops(t
, "%s = %s",
5011 rs
[i
].s
->get(&rs
[i
]));
5012 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5013 show_oops(t
, "%s = ...", rs
[i
].name
);
5015 show_oops(t
, "%s = ", rs
[i
].name
);
5018 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5024 show_oops(t
, "unknown option: %s", p
);
5027 return (XT_CB_PASSTHROUGH
);
5031 session_save(struct tab
*t
, char *filename
)
5036 if (strlen(filename
) == 0)
5039 if (filename
[0] == '.' || filename
[0] == '/')
5043 if (save_tabs(t
, &a
))
5045 strlcpy(named_session
, filename
, sizeof named_session
);
5053 session_open(struct tab
*t
, char *filename
)
5058 if (strlen(filename
) == 0)
5061 if (filename
[0] == '.' || filename
[0] == '/')
5065 a
.i
= XT_SES_CLOSETABS
;
5066 if (open_tabs(t
, &a
))
5069 strlcpy(named_session
, filename
, sizeof named_session
);
5077 session_delete(struct tab
*t
, char *filename
)
5079 char file
[PATH_MAX
];
5082 if (strlen(filename
) == 0)
5085 if (filename
[0] == '.' || filename
[0] == '/')
5088 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5092 if (!strcmp(filename
, named_session
))
5093 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5094 sizeof named_session
);
5102 session_cmd(struct tab
*t
, struct karg
*args
)
5104 char *filename
= args
->s
;
5109 if (args
->i
& XT_SHOW
)
5110 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5111 XT_SAVED_TABS_FILE
: named_session
);
5112 else if (args
->i
& XT_SAVE
) {
5113 if (session_save(t
, filename
)) {
5114 show_oops(t
, "Can't save session: %s",
5115 filename
? filename
: "INVALID");
5118 } else if (args
->i
& XT_OPEN
) {
5119 if (session_open(t
, filename
)) {
5120 show_oops(t
, "Can't open session: %s",
5121 filename
? filename
: "INVALID");
5124 } else if (args
->i
& XT_DELETE
) {
5125 if (session_delete(t
, filename
)) {
5126 show_oops(t
, "Can't delete session: %s",
5127 filename
? filename
: "INVALID");
5132 return (XT_CB_PASSTHROUGH
);
5136 * Make a hardcopy of the page
5139 print_page(struct tab
*t
, struct karg
*args
)
5141 WebKitWebFrame
*frame
;
5143 GtkPrintOperation
*op
;
5144 GtkPrintOperationAction action
;
5145 GtkPrintOperationResult print_res
;
5146 GError
*g_err
= NULL
;
5147 int marg_l
, marg_r
, marg_t
, marg_b
;
5149 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5151 ps
= gtk_page_setup_new();
5152 op
= gtk_print_operation_new();
5153 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5154 frame
= webkit_web_view_get_main_frame(t
->wv
);
5156 /* the default margins are too small, so we will bump them */
5157 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5158 XT_PRINT_EXTRA_MARGIN
;
5159 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5160 XT_PRINT_EXTRA_MARGIN
;
5161 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5162 XT_PRINT_EXTRA_MARGIN
;
5163 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5164 XT_PRINT_EXTRA_MARGIN
;
5167 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5168 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5169 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5170 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5172 gtk_print_operation_set_default_page_setup(op
, ps
);
5174 /* this appears to free 'op' and 'ps' */
5175 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5177 /* check it worked */
5178 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5179 show_oops(NULL
, "can't print: %s", g_err
->message
);
5180 g_error_free (g_err
);
5188 go_home(struct tab
*t
, struct karg
*args
)
5195 restart(struct tab
*t
, struct karg
*args
)
5199 a
.s
= XT_RESTART_TABS_FILE
;
5201 execvp(start_argv
[0], start_argv
);
5207 #define CTRL GDK_CONTROL_MASK
5208 #define MOD1 GDK_MOD1_MASK
5209 #define SHFT GDK_SHIFT_MASK
5211 /* inherent to GTK not all keys will be caught at all times */
5212 /* XXX sort key bindings */
5213 struct key_binding
{
5218 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5220 { "cookiejar", MOD1
, 0, GDK_j
},
5221 { "downloadmgr", MOD1
, 0, GDK_d
},
5222 { "history", MOD1
, 0, GDK_h
},
5223 { "print", CTRL
, 0, GDK_p
},
5224 { "search", 0, 0, GDK_slash
},
5225 { "searchb", 0, 0, GDK_question
},
5226 { "statustoggle", CTRL
, 0, GDK_n
},
5227 { "command", 0, 0, GDK_colon
},
5228 { "qa", CTRL
, 0, GDK_q
},
5229 { "restart", MOD1
, 0, GDK_q
},
5230 { "js toggle", CTRL
, 0, GDK_j
},
5231 { "cookie toggle", MOD1
, 0, GDK_c
},
5232 { "togglesrc", CTRL
, 0, GDK_s
},
5233 { "yankuri", 0, 0, GDK_y
},
5234 { "pasteuricur", 0, 0, GDK_p
},
5235 { "pasteurinew", 0, 0, GDK_P
},
5236 { "toplevel toggle", 0, 0, GDK_F4
},
5237 { "help", 0, 0, GDK_F1
},
5238 { "run_script", MOD1
, 0, GDK_r
},
5241 { "searchnext", 0, 0, GDK_n
},
5242 { "searchprevious", 0, 0, GDK_N
},
5245 { "focusaddress", 0, 0, GDK_F6
},
5246 { "focussearch", 0, 0, GDK_F7
},
5249 { "hinting", 0, 0, GDK_f
},
5251 /* custom stylesheet */
5252 { "userstyle", 0, 0, GDK_i
},
5255 { "goback", 0, 0, GDK_BackSpace
},
5256 { "goback", MOD1
, 0, GDK_Left
},
5257 { "goforward", SHFT
, 0, GDK_BackSpace
},
5258 { "goforward", MOD1
, 0, GDK_Right
},
5259 { "reload", 0, 0, GDK_F5
},
5260 { "reload", CTRL
, 0, GDK_r
},
5261 { "reload", CTRL
, 0, GDK_l
},
5262 { "favorites", MOD1
, 1, GDK_f
},
5264 /* vertical movement */
5265 { "scrolldown", 0, 0, GDK_j
},
5266 { "scrolldown", 0, 0, GDK_Down
},
5267 { "scrollup", 0, 0, GDK_Up
},
5268 { "scrollup", 0, 0, GDK_k
},
5269 { "scrollbottom", 0, 0, GDK_G
},
5270 { "scrollbottom", 0, 0, GDK_End
},
5271 { "scrolltop", 0, 0, GDK_Home
},
5272 { "scrollpagedown", 0, 0, GDK_space
},
5273 { "scrollpagedown", CTRL
, 0, GDK_f
},
5274 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5275 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5276 { "scrollpageup", 0, 0, GDK_Page_Up
},
5277 { "scrollpageup", CTRL
, 0, GDK_b
},
5278 { "scrollhalfup", CTRL
, 0, GDK_u
},
5279 /* horizontal movement */
5280 { "scrollright", 0, 0, GDK_l
},
5281 { "scrollright", 0, 0, GDK_Right
},
5282 { "scrollleft", 0, 0, GDK_Left
},
5283 { "scrollleft", 0, 0, GDK_h
},
5284 { "scrollfarright", 0, 0, GDK_dollar
},
5285 { "scrollfarleft", 0, 0, GDK_0
},
5288 { "tabnew", CTRL
, 0, GDK_t
},
5289 { "999tabnew", CTRL
, 0, GDK_T
},
5290 { "tabclose", CTRL
, 1, GDK_w
},
5291 { "tabundoclose", 0, 0, GDK_U
},
5292 { "tabnext 1", CTRL
, 0, GDK_1
},
5293 { "tabnext 2", CTRL
, 0, GDK_2
},
5294 { "tabnext 3", CTRL
, 0, GDK_3
},
5295 { "tabnext 4", CTRL
, 0, GDK_4
},
5296 { "tabnext 5", CTRL
, 0, GDK_5
},
5297 { "tabnext 6", CTRL
, 0, GDK_6
},
5298 { "tabnext 7", CTRL
, 0, GDK_7
},
5299 { "tabnext 8", CTRL
, 0, GDK_8
},
5300 { "tabnext 9", CTRL
, 0, GDK_9
},
5301 { "tabfirst", CTRL
, 0, GDK_less
},
5302 { "tablast", CTRL
, 0, GDK_greater
},
5303 { "tabprevious", CTRL
, 0, GDK_Left
},
5304 { "tabnext", CTRL
, 0, GDK_Right
},
5305 { "focusout", CTRL
, 0, GDK_minus
},
5306 { "focusin", CTRL
, 0, GDK_plus
},
5307 { "focusin", CTRL
, 0, GDK_equal
},
5308 { "focusreset", CTRL
, 0, GDK_0
},
5310 /* command aliases (handy when -S flag is used) */
5311 { "promptopen", 0, 0, GDK_F9
},
5312 { "promptopencurrent", 0, 0, GDK_F10
},
5313 { "prompttabnew", 0, 0, GDK_F11
},
5314 { "prompttabnewcurrent",0, 0, GDK_F12
},
5316 TAILQ_HEAD(keybinding_list
, key_binding
);
5319 walk_kb(struct settings
*s
,
5320 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5322 struct key_binding
*k
;
5325 if (s
== NULL
|| cb
== NULL
) {
5326 show_oops(NULL
, "walk_kb invalid parameters");
5330 TAILQ_FOREACH(k
, &kbl
, entry
) {
5336 if (gdk_keyval_name(k
->key
) == NULL
)
5339 strlcat(str
, k
->cmd
, sizeof str
);
5340 strlcat(str
, ",", sizeof str
);
5342 if (k
->mask
& GDK_SHIFT_MASK
)
5343 strlcat(str
, "S-", sizeof str
);
5344 if (k
->mask
& GDK_CONTROL_MASK
)
5345 strlcat(str
, "C-", sizeof str
);
5346 if (k
->mask
& GDK_MOD1_MASK
)
5347 strlcat(str
, "M1-", sizeof str
);
5348 if (k
->mask
& GDK_MOD2_MASK
)
5349 strlcat(str
, "M2-", sizeof str
);
5350 if (k
->mask
& GDK_MOD3_MASK
)
5351 strlcat(str
, "M3-", sizeof str
);
5352 if (k
->mask
& GDK_MOD4_MASK
)
5353 strlcat(str
, "M4-", sizeof str
);
5354 if (k
->mask
& GDK_MOD5_MASK
)
5355 strlcat(str
, "M5-", sizeof str
);
5357 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5358 cb(s
, str
, cb_args
);
5363 init_keybindings(void)
5366 struct key_binding
*k
;
5368 for (i
= 0; i
< LENGTH(keys
); i
++) {
5369 k
= g_malloc0(sizeof *k
);
5370 k
->cmd
= keys
[i
].cmd
;
5371 k
->mask
= keys
[i
].mask
;
5372 k
->use_in_entry
= keys
[i
].use_in_entry
;
5373 k
->key
= keys
[i
].key
;
5374 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5376 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5377 k
->cmd
? k
->cmd
: "unnamed key");
5382 keybinding_clearall(void)
5384 struct key_binding
*k
, *next
;
5386 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5387 next
= TAILQ_NEXT(k
, entry
);
5391 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5392 k
->cmd
? k
->cmd
: "unnamed key");
5393 TAILQ_REMOVE(&kbl
, k
, entry
);
5399 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5401 struct key_binding
*k
;
5402 guint keyval
, mask
= 0;
5405 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5407 /* Keys which are to be used in entry have been prefixed with an
5408 * exclamation mark. */
5412 /* find modifier keys */
5413 if (strstr(key
, "S-"))
5414 mask
|= GDK_SHIFT_MASK
;
5415 if (strstr(key
, "C-"))
5416 mask
|= GDK_CONTROL_MASK
;
5417 if (strstr(key
, "M1-"))
5418 mask
|= GDK_MOD1_MASK
;
5419 if (strstr(key
, "M2-"))
5420 mask
|= GDK_MOD2_MASK
;
5421 if (strstr(key
, "M3-"))
5422 mask
|= GDK_MOD3_MASK
;
5423 if (strstr(key
, "M4-"))
5424 mask
|= GDK_MOD4_MASK
;
5425 if (strstr(key
, "M5-"))
5426 mask
|= GDK_MOD5_MASK
;
5429 for (i
= strlen(key
) - 1; i
> 0; i
--)
5433 /* validate keyname */
5434 keyval
= gdk_keyval_from_name(key
);
5435 if (keyval
== GDK_VoidSymbol
) {
5436 warnx("invalid keybinding name %s", key
);
5439 /* must run this test too, gtk+ doesn't handle 10 for example */
5440 if (gdk_keyval_name(keyval
) == NULL
) {
5441 warnx("invalid keybinding name %s", key
);
5445 /* Remove eventual dupes. */
5446 TAILQ_FOREACH(k
, &kbl
, entry
)
5447 if (k
->key
== keyval
&& k
->mask
== mask
) {
5448 TAILQ_REMOVE(&kbl
, k
, entry
);
5454 k
= g_malloc0(sizeof *k
);
5455 k
->cmd
= g_strdup(cmd
);
5457 k
->use_in_entry
= use_in_entry
;
5460 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5465 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5466 k
->cmd
, gdk_keyval_name(keyval
));
5468 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5474 add_kb(struct settings
*s
, char *entry
)
5478 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5480 /* clearall is special */
5481 if (!strcmp(entry
, "clearall")) {
5482 keybinding_clearall();
5486 kb
= strstr(entry
, ",");
5492 return (keybinding_add(entry
, key
, key
[0] == '!'));
5498 int (*func
)(struct tab
*, struct karg
*);
5502 { "command", 0, command
, ':', 0 },
5503 { "search", 0, command
, '/', 0 },
5504 { "searchb", 0, command
, '?', 0 },
5505 { "togglesrc", 0, toggle_src
, 0, 0 },
5507 /* yanking and pasting */
5508 { "yankuri", 0, yank_uri
, 0, 0 },
5509 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5510 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5511 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5514 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5515 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5518 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5519 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5522 { "hinting", 0, hint
, 0, 0 },
5524 /* custom stylesheet */
5525 { "userstyle", 0, userstyle
, 0, 0 },
5528 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5529 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5530 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5532 /* vertical movement */
5533 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5534 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5535 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5536 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5537 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5538 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5539 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5540 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5541 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5542 /* horizontal movement */
5543 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5544 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5545 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5546 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5548 { "favorites", 0, xtp_page_fl
, 0, 0 },
5549 { "fav", 0, xtp_page_fl
, 0, 0 },
5550 { "favadd", 0, add_favorite
, 0, 0 },
5552 { "qall", 0, quit
, 0, 0 },
5553 { "quitall", 0, quit
, 0, 0 },
5554 { "w", 0, save_tabs
, 0, 0 },
5555 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5556 { "help", 0, help
, 0, 0 },
5557 { "about", 0, about
, 0, 0 },
5558 { "stats", 0, stats
, 0, 0 },
5559 { "version", 0, about
, 0, 0 },
5562 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5563 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5564 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5565 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5566 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5567 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5568 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5569 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5570 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5571 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5572 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5574 /* cookie command */
5575 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5576 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5577 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5578 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5579 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5580 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5581 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5582 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5583 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5584 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5585 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5587 /* toplevel (domain) command */
5588 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5589 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5592 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5595 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5596 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5597 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5599 { "ca", 0, ca_cmd
, 0, 0 },
5600 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5601 { "dl", 0, xtp_page_dl
, 0, 0 },
5602 { "h", 0, xtp_page_hl
, 0, 0 },
5603 { "history", 0, xtp_page_hl
, 0, 0 },
5604 { "home", 0, go_home
, 0, 0 },
5605 { "restart", 0, restart
, 0, 0 },
5606 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5607 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5608 { "statustoggle", 0, statustoggle
, 0, 0 },
5609 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5611 { "print", 0, print_page
, 0, 0 },
5614 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5615 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5616 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5617 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5618 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5619 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5620 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5621 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5622 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5623 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5624 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5625 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5626 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5627 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5628 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5629 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5630 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5631 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5632 { "buffers", 0, buffers
, 0, 0 },
5633 { "ls", 0, buffers
, 0, 0 },
5634 { "tabs", 0, buffers
, 0, 0 },
5636 /* command aliases (handy when -S flag is used) */
5637 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5638 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5639 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5640 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5643 { "set", 0, set
, 0, XT_USERARG
},
5645 { "fullscreen", 0, fullscreen
, 0, 0 },
5646 { "f", 0, fullscreen
, 0, 0 },
5649 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5650 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5651 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5652 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5653 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5660 } cmd_status
= {-1, 0};
5663 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5666 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5673 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5680 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5682 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5688 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5690 a
.i
= XT_NAV_FORWARD
;
5700 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5702 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5704 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5711 * cancel, remove, etc. downloads
5714 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5716 struct download find
, *d
= NULL
;
5718 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5720 /* some commands require a valid download id */
5721 if (cmd
!= XT_XTP_DL_LIST
) {
5722 /* lookup download in question */
5724 d
= RB_FIND(download_list
, &downloads
, &find
);
5727 show_oops(t
, "%s: no such download", __func__
);
5732 /* decide what to do */
5734 case XT_XTP_DL_CANCEL
:
5735 webkit_download_cancel(d
->download
);
5737 case XT_XTP_DL_REMOVE
:
5738 webkit_download_cancel(d
->download
); /* just incase */
5739 g_object_unref(d
->download
);
5740 RB_REMOVE(download_list
, &downloads
, d
);
5742 case XT_XTP_DL_LIST
:
5746 show_oops(t
, "%s: unknown command", __func__
);
5749 xtp_page_dl(t
, NULL
);
5753 * Actions on history, only does one thing for now, but
5754 * we provide the function for future actions
5757 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5759 struct history
*h
, *next
;
5763 case XT_XTP_HL_REMOVE
:
5764 /* walk backwards, as listed in reverse */
5765 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5766 next
= RB_PREV(history_list
, &hl
, h
);
5768 RB_REMOVE(history_list
, &hl
, h
);
5769 g_free((gpointer
) h
->title
);
5770 g_free((gpointer
) h
->uri
);
5777 case XT_XTP_HL_LIST
:
5778 /* Nothing - just xtp_page_hl() below */
5781 show_oops(t
, "%s: unknown command", __func__
);
5785 xtp_page_hl(t
, NULL
);
5788 /* remove a favorite */
5790 remove_favorite(struct tab
*t
, int index
)
5792 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5793 char *new_favs
, *tmp
;
5798 /* open favorites */
5799 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5801 if ((f
= fopen(file
, "r")) == NULL
) {
5802 show_oops(t
, "%s: can't open favorites: %s",
5803 __func__
, strerror(errno
));
5807 /* build a string which will become the new favroites file */
5808 new_favs
= g_strdup("");
5811 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5812 if (feof(f
) || ferror(f
))
5814 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5821 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5822 if (feof(f
) || ferror(f
)) {
5823 show_oops(t
, "%s: can't parse favorites %s",
5824 __func__
, strerror(errno
));
5829 /* as long as this isn't the one we are deleting add to file */
5832 new_favs
= g_strdup_printf("%s%s\n%s\n",
5833 new_favs
, title
, uri
);
5845 /* write back new favorites file */
5846 if ((f
= fopen(file
, "w")) == NULL
) {
5847 show_oops(t
, "%s: can't open favorites: %s",
5848 __func__
, strerror(errno
));
5852 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5865 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5868 case XT_XTP_FL_LIST
:
5869 /* nothing, just the below call to xtp_page_fl() */
5871 case XT_XTP_FL_REMOVE
:
5872 remove_favorite(t
, arg
);
5875 show_oops(t
, "%s: invalid favorites command", __func__
);
5879 xtp_page_fl(t
, NULL
);
5883 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5886 case XT_XTP_CL_LIST
:
5887 /* nothing, just xtp_page_cl() */
5889 case XT_XTP_CL_REMOVE
:
5893 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5897 xtp_page_cl(t
, NULL
);
5900 /* link an XTP class to it's session key and handler function */
5901 struct xtp_despatch
{
5904 void (*handle_func
)(struct tab
*, uint8_t, int);
5907 struct xtp_despatch xtp_despatches
[] = {
5908 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5909 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5910 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5911 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5912 { XT_XTP_INVALID
, NULL
, NULL
}
5916 * is the url xtp protocol? (xxxt://)
5917 * if so, parse and despatch correct bahvior
5920 parse_xtp_url(struct tab
*t
, const char *url
)
5922 char *dup
= NULL
, *p
, *last
;
5923 uint8_t n_tokens
= 0;
5924 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5925 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5930 * tokens array meaning:
5932 * tokens[1] = session key
5933 * tokens[2] = action
5934 * tokens[3] = optional argument
5937 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5939 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5942 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5944 /* split out the url */
5945 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5946 (p
= strtok_r(NULL
, "/", &last
))) {
5948 tokens
[n_tokens
++] = p
;
5951 /* should be atleast three fields 'class/seskey/command/arg' */
5955 dsp
= xtp_despatches
;
5956 req_class
= atoi(tokens
[0]);
5957 while (dsp
->xtp_class
) {
5958 if (dsp
->xtp_class
== req_class
) {
5965 /* did we find one atall? */
5966 if (dsp_match
== NULL
) {
5967 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5971 /* check session key and call despatch function */
5972 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5973 ret
= TRUE
; /* all is well, this was a valid xtp request */
5974 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5987 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5989 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5991 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5994 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5999 show_oops(t
, "activate_uri_entry_cb no uri");
6003 uri
+= strspn(uri
, "\t ");
6005 /* if xxxt:// treat specially */
6006 if (parse_xtp_url(t
, uri
))
6009 /* otherwise continue to load page normally */
6010 load_uri(t
, (gchar
*)uri
);
6015 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6017 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6018 char *newuri
= NULL
;
6021 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6024 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6028 if (search_string
== NULL
) {
6029 show_oops(t
, "no search_string");
6033 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6035 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6036 newuri
= g_strdup_printf(search_string
, enc_search
);
6040 webkit_web_view_load_uri(t
->wv
, newuri
);
6048 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6050 struct domain
*d
= NULL
;
6053 if (uri
== NULL
|| t
== NULL
)
6056 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6061 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6062 es
? "enable" : "disable", uri
);
6064 g_object_set(G_OBJECT(t
->settings
),
6065 "enable-html5-local-storage", es
, (char *)NULL
);
6066 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6070 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6072 struct domain
*d
= NULL
;
6075 if (uri
== NULL
|| t
== NULL
)
6078 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6083 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6084 es
? "enable" : "disable", uri
);
6086 g_object_set(G_OBJECT(t
->settings
),
6087 "enable-scripts", es
, (char *)NULL
);
6088 g_object_set(G_OBJECT(t
->settings
),
6089 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6090 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6092 button_set_stockid(t
->js_toggle
,
6093 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6097 color_address_bar(gpointer p
)
6100 struct tab
*tt
, *t
= p
;
6101 gchar
*col_str
= XT_COLOR_YELLOW
;
6103 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6105 /* make sure t still exists */
6108 TAILQ_FOREACH(tt
, &tabs
, entry
)
6114 switch (load_compare_cert(t
, NULL
)) {
6116 col_str
= XT_COLOR_BLUE
;
6119 col_str
= XT_COLOR_GREEN
;
6121 case CERT_UNTRUSTED
:
6122 col_str
= XT_COLOR_YELLOW
;
6125 col_str
= XT_COLOR_RED
;
6129 gdk_color_parse(col_str
, &color
);
6130 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6132 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6133 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6135 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6139 return (FALSE
/* kill thread */);
6143 show_ca_status(struct tab
*t
, const char *uri
)
6146 gchar
*col_str
= XT_COLOR_WHITE
;
6148 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6149 ssl_strict_certs
, ssl_ca_file
, uri
);
6156 if (ssl_ca_file
== NULL
) {
6157 if (g_str_has_prefix(uri
, "http://"))
6159 if (g_str_has_prefix(uri
, "https://")) {
6160 col_str
= XT_COLOR_RED
;
6165 if (g_str_has_prefix(uri
, "http://") ||
6166 !g_str_has_prefix(uri
, "https://"))
6169 /* thread the coloring of the address bar */
6170 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
,
6171 color_address_bar
, t
, NULL
);
6176 gdk_color_parse(col_str
, &color
);
6177 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6179 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6180 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6182 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6187 free_favicon(struct tab
*t
)
6189 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6190 __func__
, t
->icon_download
, t
->icon_request
);
6192 if (t
->icon_request
)
6193 g_object_unref(t
->icon_request
);
6194 if (t
->icon_dest_uri
)
6195 g_free(t
->icon_dest_uri
);
6197 t
->icon_request
= NULL
;
6198 t
->icon_dest_uri
= NULL
;
6202 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6204 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6205 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6207 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6208 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6210 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6211 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6215 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6217 GdkPixbuf
*pb_scaled
;
6219 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6220 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6221 GDK_INTERP_BILINEAR
);
6225 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6226 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6228 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6229 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6231 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6232 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6234 if (pb_scaled
!= pb
)
6235 g_object_unref(pb_scaled
);
6239 xt_icon_from_file(struct tab
*t
, char *file
)
6243 if (g_str_has_prefix(file
, "file://"))
6244 file
+= strlen("file://");
6246 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6248 xt_icon_from_pixbuf(t
, pb
);
6251 xt_icon_from_name(t
, "text-html");
6255 is_valid_icon(char *file
)
6258 const char *mime_type
;
6262 gf
= g_file_new_for_path(file
);
6263 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6265 mime_type
= g_file_info_get_content_type(fi
);
6266 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6267 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6268 g_strcmp0(mime_type
, "image/png") == 0 ||
6269 g_strcmp0(mime_type
, "image/gif") == 0 ||
6270 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6278 set_favicon_from_file(struct tab
*t
, char *file
)
6282 if (t
== NULL
|| file
== NULL
)
6285 if (g_str_has_prefix(file
, "file://"))
6286 file
+= strlen("file://");
6287 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6289 if (!stat(file
, &sb
)) {
6290 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6291 /* corrupt icon so trash it */
6292 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6295 /* no need to set icon to default here */
6299 xt_icon_from_file(t
, file
);
6303 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6306 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6307 struct tab
*tt
= NULL
, *t
= NULL
;
6310 * find the webview instead of passing in the tab as it could have been
6311 * deleted from underneath us.
6313 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6322 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6323 __func__
, t
->tab_id
, status
);
6326 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6328 t
->icon_download
= NULL
;
6331 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6334 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6337 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6339 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6340 __func__
, t
->tab_id
);
6341 t
->icon_download
= NULL
;
6344 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6347 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6348 __func__
, t
->icon_dest_uri
);
6349 set_favicon_from_file(t
, t
->icon_dest_uri
);
6350 /* these will be freed post callback */
6351 t
->icon_request
= NULL
;
6352 t
->icon_download
= NULL
;
6360 abort_favicon_download(struct tab
*t
)
6362 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6364 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6365 if (t
->icon_download
) {
6366 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6367 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6368 webkit_download_cancel(t
->icon_download
);
6369 t
->icon_download
= NULL
;
6374 xt_icon_from_name(t
, "text-html");
6378 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6380 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6382 if (uri
== NULL
|| t
== NULL
)
6385 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6386 /* take icon from WebKitIconDatabase */
6389 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6391 xt_icon_from_pixbuf(t
, pb
);
6394 xt_icon_from_name(t
, "text-html");
6395 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6396 /* download icon to cache dir */
6397 gchar
*name_hash
, file
[PATH_MAX
];
6400 if (t
->icon_request
) {
6401 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6405 /* check to see if we got the icon in cache */
6406 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6407 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6410 if (!stat(file
, &sb
)) {
6411 if (sb
.st_size
> 0) {
6412 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6414 set_favicon_from_file(t
, file
);
6418 /* corrupt icon so trash it */
6419 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6424 /* create download for icon */
6425 t
->icon_request
= webkit_network_request_new(uri
);
6426 if (t
->icon_request
== NULL
) {
6427 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6432 t
->icon_download
= webkit_download_new(t
->icon_request
);
6433 if (t
->icon_download
== NULL
)
6436 /* we have to free icon_dest_uri later */
6437 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6438 webkit_download_set_destination_uri(t
->icon_download
,
6441 if (webkit_download_get_status(t
->icon_download
) ==
6442 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6443 g_object_unref(t
->icon_request
);
6444 g_free(t
->icon_dest_uri
);
6445 t
->icon_request
= NULL
;
6446 t
->icon_dest_uri
= NULL
;
6450 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6451 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6453 webkit_download_start(t
->icon_download
);
6458 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6460 const gchar
*uri
= NULL
, *title
= NULL
;
6461 struct history
*h
, find
;
6465 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6466 webkit_web_view_get_load_status(wview
),
6467 get_uri(t
) ? get_uri(t
) : "NOTHING");
6470 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6474 switch (webkit_web_view_get_load_status(wview
)) {
6475 case WEBKIT_LOAD_PROVISIONAL
:
6477 abort_favicon_download(t
);
6478 #if GTK_CHECK_VERSION(2, 20, 0)
6479 gtk_widget_show(t
->spinner
);
6480 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6482 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6484 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6486 /* assume we are a new address */
6487 gdk_color_parse("white", &color
);
6488 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6489 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6491 /* take focus if we are visible */
6497 case WEBKIT_LOAD_COMMITTED
:
6502 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6508 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6510 /* check if js white listing is enabled */
6511 if (enable_cookie_whitelist
)
6512 check_and_set_cookie(uri
, t
);
6513 if (enable_js_whitelist
)
6514 check_and_set_js(uri
, t
);
6520 /* we know enough to autosave the session */
6521 if (session_autosave
) {
6526 show_ca_status(t
, uri
);
6529 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6533 case WEBKIT_LOAD_FINISHED
:
6539 if (!strncmp(uri
, "http://", strlen("http://")) ||
6540 !strncmp(uri
, "https://", strlen("https://")) ||
6541 !strncmp(uri
, "file://", strlen("file://"))) {
6543 h
= RB_FIND(history_list
, &hl
, &find
);
6545 title
= get_title(t
, FALSE
);
6546 h
= g_malloc(sizeof *h
);
6547 h
->uri
= g_strdup(uri
);
6548 h
->title
= g_strdup(title
);
6549 RB_INSERT(history_list
, &hl
, h
);
6550 completion_add_uri(h
->uri
);
6551 update_history_tabs(NULL
);
6555 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6556 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6557 case WEBKIT_LOAD_FAILED
:
6560 #if GTK_CHECK_VERSION(2, 20, 0)
6561 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6562 gtk_widget_hide(t
->spinner
);
6565 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6570 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6572 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6573 webkit_web_view_can_go_back(wview
));
6575 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6576 webkit_web_view_can_go_forward(wview
));
6581 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6582 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6585 * XXX this function is wrong
6586 * it overwrites perfectly good urls with garbage on load errors
6587 * those happen often when popups fail to resolve dns
6591 t
->tmp_uri
= g_strdup(uri
);
6592 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6593 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6594 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6595 set_status(t
, uri
, XT_STATUS_NOTHING
);
6602 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6604 const gchar
*title
= NULL
, *win_title
= NULL
;
6606 title
= get_title(t
, FALSE
);
6607 win_title
= get_title(t
, TRUE
);
6608 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6609 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6610 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6611 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6615 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6617 run_script(t
, JS_HINTING
);
6621 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6623 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6624 progress
== 100 ? 0 : (double)progress
/ 100);
6625 if (show_url
== 0) {
6626 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6627 progress
== 100 ? 0 : (double)progress
/ 100);
6630 update_statusbar_position(NULL
, NULL
);
6634 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6635 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6636 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6639 WebKitWebNavigationReason reason
;
6640 struct domain
*d
= NULL
;
6643 show_oops(NULL
, "webview_npd_cb invalid parameters");
6647 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6649 webkit_network_request_get_uri(request
));
6651 uri
= (char *)webkit_network_request_get_uri(request
);
6653 /* if this is an xtp url, we don't load anything else */
6654 if (parse_xtp_url(t
, uri
))
6657 if (t
->ctrl_click
) {
6659 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6660 webkit_web_policy_decision_ignore(pd
);
6661 return (TRUE
); /* we made the decission */
6665 * This is a little hairy but it comes down to this:
6666 * when we run in whitelist mode we have to assist the browser in
6667 * opening the URL that it would have opened in a new tab.
6669 reason
= webkit_web_navigation_action_get_reason(na
);
6670 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6671 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6672 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6673 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6675 webkit_web_policy_decision_use(pd
);
6676 return (TRUE
); /* we made the decision */
6683 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6686 struct domain
*d
= NULL
;
6688 WebKitWebView
*webview
= NULL
;
6690 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6691 webkit_web_view_get_uri(wv
));
6694 /* open in current tab */
6696 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6697 uri
= webkit_web_view_get_uri(wv
);
6698 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6701 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6703 } else if (enable_scripts
== 1) {
6704 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6712 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6715 struct domain
*d
= NULL
;
6717 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6719 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6720 uri
= webkit_web_view_get_uri(wv
);
6721 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6725 } else if (enable_scripts
== 1)
6732 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6734 /* we can not eat the event without throwing gtk off so defer it */
6736 /* catch middle click */
6737 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6742 /* catch ctrl click */
6743 if (e
->type
== GDK_BUTTON_RELEASE
&&
6744 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6749 return (XT_CB_PASSTHROUGH
);
6753 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6755 struct mime_type
*m
;
6757 m
= find_mime_type(mime_type
);
6765 show_oops(t
, "can't fork mime handler");
6775 execlp(m
->mt_action
, m
->mt_action
,
6776 webkit_network_request_get_uri(request
), (void *)NULL
);
6785 get_mime_type(char *file
)
6787 const char *mime_type
;
6791 if (g_str_has_prefix(file
, "file://"))
6792 file
+= strlen("file://");
6794 gf
= g_file_new_for_path(file
);
6795 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6797 mime_type
= g_file_info_get_content_type(fi
);
6805 run_download_mimehandler(char *mime_type
, char *file
)
6807 struct mime_type
*m
;
6809 m
= find_mime_type(mime_type
);
6815 show_oops(NULL
, "can't fork download mime handler");
6825 if (g_str_has_prefix(file
, "file://"))
6826 file
+= strlen("file://");
6827 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6836 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6839 WebKitDownloadStatus status
;
6840 const gchar
*file
= NULL
, *mime
= NULL
;
6842 if (download
== NULL
)
6844 status
= webkit_download_get_status(download
);
6845 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6848 file
= webkit_download_get_destination_uri(download
);
6851 mime
= get_mime_type((char *)file
);
6855 run_download_mimehandler((char *)mime
, (char *)file
);
6859 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6860 WebKitNetworkRequest
*request
, char *mime_type
,
6861 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6864 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6868 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6869 t
->tab_id
, mime_type
);
6871 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6872 webkit_web_policy_decision_ignore(decision
);
6877 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6878 webkit_web_policy_decision_download(decision
);
6886 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6890 const gchar
*suggested_name
;
6891 gchar
*filename
= NULL
;
6893 struct download
*download_entry
;
6896 if (wk_download
== NULL
|| t
== NULL
) {
6897 show_oops(NULL
, "%s invalid parameters", __func__
);
6901 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6902 if (suggested_name
== NULL
)
6903 return (FALSE
); /* abort download */
6914 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6916 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6917 filename
: suggested_name
);
6919 } while (!stat(uri
+ strlen("file://"), &sb
));
6921 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6922 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6924 webkit_download_set_destination_uri(wk_download
, uri
);
6926 if (webkit_download_get_status(wk_download
) ==
6927 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6928 show_oops(t
, "%s: download failed to start", __func__
);
6930 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6932 /* connect "download first" mime handler */
6933 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6934 G_CALLBACK(download_status_changed_cb
), NULL
);
6936 download_entry
= g_malloc(sizeof(struct download
));
6937 download_entry
->download
= wk_download
;
6938 download_entry
->tab
= t
;
6939 download_entry
->id
= next_download_id
++;
6940 RB_INSERT(download_list
, &downloads
, download_entry
);
6941 /* get from history */
6942 g_object_ref(wk_download
);
6943 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6944 show_oops(t
, "Download of '%s' started...",
6945 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6954 /* sync other download manager tabs */
6955 update_download_tabs(NULL
);
6958 * NOTE: never redirect/render the current tab before this
6959 * function returns. This will cause the download to never start.
6961 return (ret
); /* start download */
6965 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6967 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6970 show_oops(NULL
, "webview_hover_cb");
6975 set_status(t
, uri
, XT_STATUS_LINK
);
6978 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6983 mark(struct tab
*t
, struct karg
*arg
)
6989 if ((index
= marktoindex(mark
)) == -1)
6992 if (arg
->i
== XT_MARK_SET
)
6993 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6994 else if (arg
->i
== XT_MARK_GOTO
) {
6995 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6996 show_oops(t
, "mark '%c' does not exist", mark
);
6999 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7000 something changes the document size */
7001 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7008 marks_clear(struct tab
*t
)
7012 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7013 t
->mark
[i
] = XT_INVALID_MARK
;
7019 char file
[PATH_MAX
];
7020 char *line
= NULL
, *p
, mark
;
7025 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7026 if ((f
= fopen(file
, "r+")) == NULL
) {
7027 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7031 for (i
= 1; ; i
++) {
7032 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7033 if (feof(f
) || ferror(f
))
7035 if (strlen(line
) == 0 || line
[0] == '#') {
7041 p
= strtok(line
, " \t");
7043 if (p
== NULL
|| strlen(p
) != 1 ||
7044 (index
= marktoindex(*p
)) == -1) {
7045 warnx("corrupt quickmarks file, line %d", i
);
7050 p
= strtok(NULL
, " \t");
7051 if (qmarks
[index
] != NULL
)
7052 g_free(qmarks
[index
]);
7053 qmarks
[index
] = g_strdup(p
);
7064 char file
[PATH_MAX
];
7068 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7069 if ((f
= fopen(file
, "r+")) == NULL
) {
7070 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7074 for (i
= 0; i
< XT_NOMARKS
; i
++)
7075 if (qmarks
[i
] != NULL
)
7076 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7084 qmark(struct tab
*t
, struct karg
*arg
)
7089 mark
= arg
->s
[strlen(arg
->s
)-1];
7090 index
= marktoindex(mark
);
7096 if (qmarks
[index
] != NULL
)
7097 g_free(qmarks
[index
]);
7099 qmarks_load(); /* sync if multiple instances */
7100 qmarks
[index
] = g_strdup(get_uri(t
));
7104 if (qmarks
[index
] != NULL
)
7105 load_uri(t
, qmarks
[index
]);
7107 show_oops(t
, "quickmark \"%c\" does not exist",
7113 if (qmarks
[index
] != NULL
)
7114 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7116 show_oops(t
, "quickmark \"%c\" does not exist",
7127 go_up(struct tab
*t
, struct karg
*args
)
7133 levels
= atoi(args
->s
);
7137 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7138 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7140 tmp
+= strlen(XT_PROTO_DELIM
);
7142 /* if an uri starts with a slash, leave it alone (for file:///) */
7149 p
= strrchr(tmp
, '/');
7163 gototab(struct tab
*t
, struct karg
*args
)
7166 struct karg arg
= {0, NULL
, -1};
7168 tab
= atoi(args
->s
);
7170 arg
.i
= XT_TAB_NEXT
;
7179 zoom_amount(struct tab
*t
, struct karg
*arg
)
7181 struct karg narg
= {0, NULL
, -1};
7183 narg
.i
= atoi(arg
->s
);
7184 resizetab(t
, &narg
);
7190 flip_colon(struct tab
*t
, struct karg
*arg
)
7192 struct karg narg
= {0, NULL
, -1};
7195 if (t
== NULL
|| arg
== NULL
)
7198 p
= strstr(arg
->s
, ":");
7210 /* buffer commands receive the regex that triggered them in arg.s */
7211 char bcmd
[XT_BUFCMD_SZ
];
7215 #define XT_PRE_NO (0)
7216 #define XT_PRE_YES (1)
7217 #define XT_PRE_MAYBE (2)
7219 int (*func
)(struct tab
*, struct karg
*);
7223 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7224 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7225 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7226 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7227 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7228 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7229 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7230 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7231 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7232 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7233 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7234 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7235 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7236 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7237 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7238 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7239 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7240 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7244 buffercmd_init(void)
7248 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7249 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7250 REG_EXTENDED
| REG_NOSUB
))
7251 startpage_add("invalid buffercmd regex %s",
7252 buffercmds
[i
].regex
);
7256 buffercmd_abort(struct tab
*t
)
7260 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7261 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7264 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7265 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7269 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7271 struct karg arg
= {0, NULL
, -1};
7274 arg
.s
= g_strdup(bcmd
);
7276 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7277 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7287 buffercmd_addkey(struct tab
*t
, guint keyval
)
7290 char s
[XT_BUFCMD_SZ
];
7292 if (keyval
== GDK_Escape
) {
7294 return (XT_CB_HANDLED
);
7297 /* key with modifier or non-ascii character */
7298 if (!isascii(keyval
))
7299 return (XT_CB_PASSTHROUGH
);
7301 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7302 "to buffer \"%s\"\n", keyval
, bcmd
);
7304 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7305 if (bcmd
[i
] == '\0') {
7310 /* buffer full, ignore input */
7311 if (i
>= LENGTH(bcmd
) -1) {
7312 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7314 return (XT_CB_HANDLED
);
7317 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7319 /* find exact match */
7320 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7321 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7322 (size_t) 0, NULL
, 0) == 0) {
7323 buffercmd_execute(t
, &buffercmds
[i
]);
7327 /* find non exact matches to see if we need to abort ot not */
7328 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7329 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7332 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7333 if (isdigit(bcmd
[0])) {
7334 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7338 if (sscanf(bcmd
, "%s", s
) == 0)
7341 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7342 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7345 if (sscanf(bcmd
, "%s", s
) == 0)
7348 if (c
== -1 && buffercmds
[i
].precount
)
7350 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7353 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7354 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7357 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7362 return (XT_CB_HANDLED
);
7366 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7368 struct key_binding
*k
;
7370 /* handle keybindings if buffercmd is empty.
7371 if not empty, allow commands like C-n */
7372 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7373 TAILQ_FOREACH(k
, &kbl
, entry
)
7374 if (e
->keyval
== k
->key
7375 && (entry
? k
->use_in_entry
: 1)) {
7377 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7378 return (cmd_execute(t
, k
->cmd
));
7379 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7380 return (cmd_execute(t
, k
->cmd
));
7384 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7385 return buffercmd_addkey(t
, e
->keyval
);
7387 return (XT_CB_PASSTHROUGH
);
7391 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7393 char s
[2], buf
[128];
7394 const char *errstr
= NULL
;
7396 /* don't use w directly; use t->whatever instead */
7399 show_oops(NULL
, "wv_keypress_after_cb");
7400 return (XT_CB_PASSTHROUGH
);
7403 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7404 e
->keyval
, e
->state
, t
);
7408 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7410 return (XT_CB_HANDLED
);
7414 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7416 /* we have a string */
7418 /* we have a number */
7419 snprintf(buf
, sizeof buf
,
7420 "vimprobable_fire(%s)", t
->hint_num
);
7427 /* XXX unfuck this */
7428 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7429 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7430 /* last input was numerical */
7432 l
= strlen(t
->hint_num
);
7439 t
->hint_num
[l
] = '\0';
7443 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7444 /* last input was alphanumerical */
7446 l
= strlen(t
->hint_buf
);
7453 t
->hint_buf
[l
] = '\0';
7463 /* numerical input */
7464 if (CLEAN(e
->state
) == 0 &&
7465 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7466 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7467 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7468 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7469 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7473 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7474 "invalid link number\n");
7477 snprintf(buf
, sizeof buf
,
7478 "vimprobable_update_hints(%s)",
7480 t
->hint_mode
= XT_HINT_NUMERICAL
;
7484 /* empty the counter buffer */
7485 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7486 return (XT_CB_HANDLED
);
7489 /* alphanumerical input */
7490 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7491 e
->keyval
<= GDK_z
) ||
7492 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7493 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7494 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7495 e
->keyval
<= GDK_9
) ||
7496 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7497 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7498 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7499 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7500 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7501 " %s\n", t
->hint_buf
);
7503 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7506 snprintf(buf
, sizeof buf
,
7507 "vimprobable_show_hints('%s')", t
->hint_buf
);
7508 t
->hint_mode
= XT_HINT_ALPHANUM
;
7511 /* empty the counter buffer */
7512 bzero(t
->hint_num
, sizeof t
->hint_num
);
7513 return (XT_CB_HANDLED
);
7516 return (XT_CB_HANDLED
);
7519 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7520 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7521 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7524 return (handle_keypress(t
, e
, 0));
7528 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7532 /* Hide buffers, if they are visible, with escape. */
7533 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7534 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7535 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7537 return (XT_CB_HANDLED
);
7540 return (XT_CB_PASSTHROUGH
);
7544 search_continue(struct tab
*t
)
7546 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7547 gboolean rv
= FALSE
;
7551 if (strlen(c
) == 1) {
7552 webkit_web_view_unmark_text_matches(t
->wv
);
7557 t
->search_forward
= TRUE
;
7558 else if (c
[0] == '?')
7559 t
->search_forward
= FALSE
;
7569 search_cb(struct tab
*t
)
7571 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7574 if (search_continue(t
) == FALSE
)
7578 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7580 /* not found, mark red */
7581 gdk_color_parse(XT_COLOR_RED
, &color
);
7582 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7583 /* unmark and remove selection */
7584 webkit_web_view_unmark_text_matches(t
->wv
);
7585 /* my kingdom for a way to unselect text in webview */
7587 /* found, highlight all */
7588 webkit_web_view_unmark_text_matches(t
->wv
);
7589 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7590 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7591 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7592 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7600 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7602 const gchar
*c
= gtk_entry_get_text(w
);
7605 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7606 return (XT_CB_PASSTHROUGH
);
7609 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7610 e
->keyval
, e
->state
, t
);
7612 if (search_continue(t
) == FALSE
)
7615 /* if search length is > 4 then no longer play timeout games */
7616 if (strlen(c
) > 4) {
7618 g_source_remove(t
->search_id
);
7625 /* reestablish a new timer if the user types fast */
7627 g_source_remove(t
->search_id
);
7628 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7631 return (XT_CB_PASSTHROUGH
);
7635 match_uri(const gchar
*uri
, const gchar
*key
) {
7638 gboolean match
= FALSE
;
7642 if (!strncmp(key
, uri
, len
))
7645 voffset
= strstr(uri
, "/") + 2;
7646 if (!strncmp(key
, voffset
, len
))
7648 else if (g_str_has_prefix(voffset
, "www.")) {
7649 voffset
= voffset
+ strlen("www.");
7650 if (!strncmp(key
, voffset
, len
))
7659 cmd_getlist(int id
, char *key
)
7664 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7665 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7666 if (match_uri(h
->uri
, key
)) {
7667 cmd_status
.list
[c
] = (char *)h
->uri
;
7676 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7678 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7679 if (cmds
[i
].level
< dep
)
7681 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7683 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7691 cmd_getnext(int dir
)
7693 cmd_status
.index
+= dir
;
7695 if (cmd_status
.index
< 0)
7696 cmd_status
.index
= cmd_status
.len
- 1;
7697 else if (cmd_status
.index
>= cmd_status
.len
)
7698 cmd_status
.index
= 0;
7700 return cmd_status
.list
[cmd_status
.index
];
7704 cmd_tokenize(char *s
, char *tokens
[])
7708 size_t len
= strlen(s
);
7711 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7712 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7713 tok
= strtok_r(NULL
, " ", &last
), i
++)
7723 cmd_complete(struct tab
*t
, char *str
, int dir
)
7725 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7726 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7728 char *tok
, *match
, *s
= g_strdup(str
);
7730 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7733 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7736 for (i
= 0; isdigit(s
[i
]); i
++)
7739 for (; isspace(s
[i
]); i
++)
7744 levels
= cmd_tokenize(s
, tokens
);
7746 for (i
= 0; i
< levels
- 1; i
++) {
7749 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7750 if (cmds
[j
].level
< dep
)
7752 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7756 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7763 if (matchcount
== 1) {
7764 strlcat(res
, tok
, sizeof res
);
7765 strlcat(res
, " ", sizeof res
);
7775 if (cmd_status
.index
== -1)
7776 cmd_getlist(parent
, tokens
[i
]);
7778 if (cmd_status
.len
> 0) {
7779 match
= cmd_getnext(dir
);
7780 strlcat(res
, match
, sizeof res
);
7781 gtk_entry_set_text(w
, res
);
7782 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7789 cmd_execute(struct tab
*t
, char *str
)
7791 struct cmd
*cmd
= NULL
;
7792 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7794 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7795 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7796 struct karg arg
= {0, NULL
, -1};
7801 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7807 while (isspace(s
[0]))
7810 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7811 prefix
= atoi(prefixstr
);
7815 for (tok
= strtok_r(s
, " ", &last
); tok
;
7816 tok
= strtok_r(NULL
, " ", &last
)) {
7818 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7819 if (cmds
[j
].level
< dep
)
7821 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7823 if (cmds
[j
].level
== dep
&&
7824 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7828 if (len
== strlen(cmds
[j
].cmd
)) {
7834 if (matchcount
== 1) {
7839 show_oops(t
, "Invalid command: %s", str
);
7847 arg
.precount
= prefix
;
7848 else if (cmd_prefix
> 0)
7849 arg
.precount
= cmd_prefix
;
7851 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
7852 show_oops(t
, "No prefix allowed: %s", str
);
7856 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7857 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7858 arg
.precount
= atoi(arg
.s
);
7859 if (arg
.precount
<= 0) {
7860 if (arg
.s
[0] == '0')
7861 show_oops(t
, "Zero count");
7863 show_oops(t
, "Trailing characters");
7868 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
7869 __func__
, arg
.precount
, arg
.s
);
7885 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7888 show_oops(NULL
, "entry_key_cb invalid parameters");
7889 return (XT_CB_PASSTHROUGH
);
7892 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7893 e
->keyval
, e
->state
, t
);
7897 if (e
->keyval
== GDK_Escape
) {
7898 /* don't use focus_webview(t) because we want to type :cmds */
7899 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7902 return (handle_keypress(t
, e
, 1));
7906 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7908 int rv
= XT_CB_HANDLED
;
7909 const gchar
*c
= gtk_entry_get_text(w
);
7912 show_oops(NULL
, "cmd_keypress_cb parameters");
7913 return (XT_CB_PASSTHROUGH
);
7916 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7917 e
->keyval
, e
->state
, t
);
7921 e
->keyval
= GDK_Escape
;
7922 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7923 e
->keyval
= GDK_Escape
;
7925 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7926 e
->keyval
!= GDK_ISO_Left_Tab
)
7927 cmd_status
.index
= -1;
7929 switch (e
->keyval
) {
7932 cmd_complete(t
, (char *)&c
[1], 1);
7934 case GDK_ISO_Left_Tab
:
7936 cmd_complete(t
, (char *)&c
[1], -1);
7940 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7948 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7949 webkit_web_view_unmark_text_matches(t
->wv
);
7953 rv
= XT_CB_PASSTHROUGH
;
7959 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
7961 /* popup menu enabled */
7966 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7969 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7970 return (XT_CB_PASSTHROUGH
);
7973 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
7974 t
->tab_id
, t
->popup
);
7976 /* if popup is enabled don't lose focus */
7979 return (XT_CB_PASSTHROUGH
);
7985 if (show_url
== 0 || t
->focus_wv
)
7988 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7990 return (XT_CB_PASSTHROUGH
);
7994 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7997 const gchar
*c
= gtk_entry_get_text(entry
);
8000 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8004 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8011 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8017 if (c
[0] == '/' || c
[0] == '?') {
8018 /* see if there is a timer pending */
8020 g_source_remove(t
->search_id
);
8025 if (t
->search_text
) {
8026 g_free(t
->search_text
);
8027 t
->search_text
= NULL
;
8030 t
->search_text
= g_strdup(s
);
8032 g_free(global_search
);
8033 global_search
= g_strdup(s
);
8034 t
->search_forward
= c
[0] == '/';
8046 backward_cb(GtkWidget
*w
, struct tab
*t
)
8051 show_oops(NULL
, "backward_cb invalid parameters");
8055 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8062 forward_cb(GtkWidget
*w
, struct tab
*t
)
8067 show_oops(NULL
, "forward_cb invalid parameters");
8071 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8073 a
.i
= XT_NAV_FORWARD
;
8078 home_cb(GtkWidget
*w
, struct tab
*t
)
8081 show_oops(NULL
, "home_cb invalid parameters");
8085 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8091 stop_cb(GtkWidget
*w
, struct tab
*t
)
8093 WebKitWebFrame
*frame
;
8096 show_oops(NULL
, "stop_cb invalid parameters");
8100 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8102 frame
= webkit_web_view_get_main_frame(t
->wv
);
8103 if (frame
== NULL
) {
8104 show_oops(t
, "stop_cb: no frame");
8108 webkit_web_frame_stop_loading(frame
);
8109 abort_favicon_download(t
);
8113 setup_webkit(struct tab
*t
)
8115 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8116 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8117 FALSE
, (char *)NULL
);
8119 warnx("webkit does not have \"enable-dns-prefetching\" property");
8120 g_object_set(G_OBJECT(t
->settings
),
8121 "user-agent", t
->user_agent
, (char *)NULL
);
8122 g_object_set(G_OBJECT(t
->settings
),
8123 "enable-scripts", enable_scripts
, (char *)NULL
);
8124 g_object_set(G_OBJECT(t
->settings
),
8125 "enable-plugins", enable_plugins
, (char *)NULL
);
8126 g_object_set(G_OBJECT(t
->settings
),
8127 "javascript-can-open-windows-automatically", enable_scripts
,
8129 g_object_set(G_OBJECT(t
->settings
),
8130 "enable-html5-database", FALSE
, (char *)NULL
);
8131 g_object_set(G_OBJECT(t
->settings
),
8132 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8133 g_object_set(G_OBJECT(t
->settings
),
8134 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8135 g_object_set(G_OBJECT(t
->settings
),
8136 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8137 g_object_set(G_OBJECT(t
->wv
),
8138 "full-content-zoom", TRUE
, (char *)NULL
);
8140 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8144 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8146 struct tab
*ti
, *t
= NULL
;
8147 gdouble view_size
, value
, max
;
8150 TAILQ_FOREACH(ti
, &tabs
, entry
)
8151 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8159 if (adjustment
== NULL
)
8160 adjustment
= gtk_scrolled_window_get_vadjustment(
8161 GTK_SCROLLED_WINDOW(t
->browser_win
));
8163 view_size
= gtk_adjustment_get_page_size(adjustment
);
8164 value
= gtk_adjustment_get_value(adjustment
);
8165 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8168 position
= g_strdup("All");
8169 else if (value
== max
)
8170 position
= g_strdup("Bot");
8171 else if (value
== 0)
8172 position
= g_strdup("Top");
8174 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8176 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8183 create_browser(struct tab
*t
)
8187 GtkAdjustment
*adjustment
;
8190 show_oops(NULL
, "create_browser invalid parameters");
8194 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8195 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8196 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8197 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8199 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8200 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8201 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8203 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8204 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8207 t
->settings
= webkit_web_settings_new();
8209 if (user_agent
== NULL
) {
8210 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8212 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8215 t
->user_agent
= g_strdup(user_agent
);
8217 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8220 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8221 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8222 G_CALLBACK(update_statusbar_position
), NULL
);
8234 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8235 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8236 gtk_widget_set_name(w
, "xxxterm");
8237 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8238 g_signal_connect(G_OBJECT(w
), "delete_event",
8239 G_CALLBACK (gtk_main_quit
), NULL
);
8245 create_kiosk_toolbar(struct tab
*t
)
8247 GtkWidget
*toolbar
= NULL
, *b
;
8249 b
= gtk_hbox_new(FALSE
, 0);
8251 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8253 /* backward button */
8254 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8255 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8256 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8257 G_CALLBACK(backward_cb
), t
);
8258 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8260 /* forward button */
8261 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8262 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8263 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8264 G_CALLBACK(forward_cb
), t
);
8265 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8268 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8269 gtk_widget_set_sensitive(t
->gohome
, true);
8270 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8271 G_CALLBACK(home_cb
), t
);
8272 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8274 /* create widgets but don't use them */
8275 t
->uri_entry
= gtk_entry_new();
8276 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8277 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8278 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8284 create_toolbar(struct tab
*t
)
8286 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8288 b
= gtk_hbox_new(FALSE
, 0);
8290 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8292 /* backward button */
8293 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8294 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8295 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8296 G_CALLBACK(backward_cb
), t
);
8297 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8299 /* forward button */
8300 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8301 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8302 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8303 G_CALLBACK(forward_cb
), t
);
8304 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8308 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8309 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8310 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8311 G_CALLBACK(stop_cb
), t
);
8312 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8316 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8317 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8318 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8319 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8320 G_CALLBACK(js_toggle_cb
), t
);
8321 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8323 t
->uri_entry
= gtk_entry_new();
8324 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8325 G_CALLBACK(activate_uri_entry_cb
), t
);
8326 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8327 G_CALLBACK(entry_key_cb
), t
);
8329 eb1
= gtk_hbox_new(FALSE
, 0);
8330 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8331 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8332 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8335 if (search_string
) {
8337 t
->search_entry
= gtk_entry_new();
8338 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8339 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8340 G_CALLBACK(activate_search_entry_cb
), t
);
8341 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8342 G_CALLBACK(entry_key_cb
), t
);
8343 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8344 eb2
= gtk_hbox_new(FALSE
, 0);
8345 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8346 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8348 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8355 create_buffers(struct tab
*t
)
8357 GtkCellRenderer
*renderer
;
8360 view
= gtk_tree_view_new();
8362 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8364 renderer
= gtk_cell_renderer_text_new();
8365 gtk_tree_view_insert_column_with_attributes
8366 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
8368 renderer
= gtk_cell_renderer_text_new();
8369 gtk_tree_view_insert_column_with_attributes
8370 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8373 gtk_tree_view_set_model
8374 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8380 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8381 GtkTreeViewColumn
*col
, struct tab
*t
)
8386 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8388 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8391 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8392 set_current_tab(id
- 1);
8398 /* after tab reordering/creation/removal */
8405 TAILQ_FOREACH(t
, &tabs
, entry
) {
8406 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8407 if (t
->tab_id
> maxid
)
8410 gtk_widget_show(t
->tab_elems
.sep
);
8413 TAILQ_FOREACH(t
, &tabs
, entry
) {
8414 if (t
->tab_id
== maxid
) {
8415 gtk_widget_hide(t
->tab_elems
.sep
);
8421 /* after active tab change */
8423 recolor_compact_tabs(void)
8429 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8430 TAILQ_FOREACH(t
, &tabs
, entry
)
8431 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8434 curid
= gtk_notebook_get_current_page(notebook
);
8435 TAILQ_FOREACH(t
, &tabs
, entry
)
8436 if (t
->tab_id
== curid
) {
8437 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8438 gtk_widget_modify_fg(t
->tab_elems
.label
,
8439 GTK_STATE_NORMAL
, &color
);
8445 set_current_tab(int page_num
)
8447 buffercmd_abort(get_current_tab());
8448 gtk_notebook_set_current_page(notebook
, page_num
);
8449 recolor_compact_tabs();
8453 undo_close_tab_save(struct tab
*t
)
8457 struct undo
*u1
, *u2
;
8459 WebKitWebHistoryItem
*item
;
8461 if ((uri
= get_uri(t
)) == NULL
)
8464 u1
= g_malloc0(sizeof(struct undo
));
8465 u1
->uri
= g_strdup(uri
);
8467 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8469 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8470 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8473 /* forward history */
8474 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8478 u1
->history
= g_list_prepend(u1
->history
,
8479 webkit_web_history_item_copy(item
));
8480 items
= g_list_next(items
);
8485 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8486 u1
->history
= g_list_prepend(u1
->history
,
8487 webkit_web_history_item_copy(item
));
8491 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8495 u1
->history
= g_list_prepend(u1
->history
,
8496 webkit_web_history_item_copy(item
));
8497 items
= g_list_next(items
);
8500 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8502 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8503 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8504 TAILQ_REMOVE(&undos
, u2
, entry
);
8506 g_list_free(u2
->history
);
8515 delete_tab(struct tab
*t
)
8519 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8525 TAILQ_REMOVE(&tabs
, t
, entry
);
8527 /* Halt all webkit activity. */
8528 abort_favicon_download(t
);
8529 webkit_web_view_stop_loading(t
->wv
);
8531 /* Save the tab, so we can undo the close. */
8532 undo_close_tab_save(t
);
8534 if (browser_mode
== XT_BM_KIOSK
) {
8535 gtk_widget_destroy(t
->uri_entry
);
8536 gtk_widget_destroy(t
->stop
);
8537 gtk_widget_destroy(t
->js_toggle
);
8540 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8541 gtk_widget_destroy(t
->vbox
);
8545 g_source_remove(t
->search_id
);
8547 g_free(t
->user_agent
);
8548 g_free(t
->stylesheet
);
8552 if (TAILQ_EMPTY(&tabs
)) {
8553 if (browser_mode
== XT_BM_KIOSK
)
8554 create_new_tab(home
, NULL
, 1, -1);
8556 create_new_tab(NULL
, NULL
, 1, -1);
8559 /* recreate session */
8560 if (session_autosave
) {
8566 recolor_compact_tabs();
8570 update_statusbar_zoom(struct tab
*t
)
8573 char s
[16] = { '\0' };
8575 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8576 if ((zoom
<= 0.99 || zoom
>= 1.01))
8577 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8578 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8582 setzoom_webkit(struct tab
*t
, int adjust
)
8584 #define XT_ZOOMPERCENT 0.04
8589 show_oops(NULL
, "setzoom_webkit invalid parameters");
8593 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8594 if (adjust
== XT_ZOOM_IN
)
8595 zoom
+= XT_ZOOMPERCENT
;
8596 else if (adjust
== XT_ZOOM_OUT
)
8597 zoom
-= XT_ZOOMPERCENT
;
8598 else if (adjust
> 0)
8599 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8601 show_oops(t
, "setzoom_webkit invalid zoom value");
8605 if (zoom
< XT_ZOOMPERCENT
)
8606 zoom
= XT_ZOOMPERCENT
;
8607 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8608 update_statusbar_zoom(t
);
8612 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8614 struct tab
*t
= (struct tab
*) data
;
8616 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8618 switch (event
->button
) {
8620 set_current_tab(t
->tab_id
);
8631 append_tab(struct tab
*t
)
8636 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8637 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8641 create_sbe(int width
)
8645 sbe
= gtk_entry_new();
8646 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8647 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8648 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8649 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8650 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8651 gtk_widget_set_size_request(sbe
, width
, -1);
8657 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8662 WebKitWebHistoryItem
*item
;
8666 int sbe_p
= 0, sbe_b
= 0,
8669 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8671 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8672 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8676 t
= g_malloc0(sizeof *t
);
8678 if (title
== NULL
) {
8679 title
= "(untitled)";
8683 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8685 /* label + button for tab */
8686 b
= gtk_hbox_new(FALSE
, 0);
8689 #if GTK_CHECK_VERSION(2, 20, 0)
8690 t
->spinner
= gtk_spinner_new();
8692 t
->label
= gtk_label_new(title
);
8693 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8694 gtk_widget_set_size_request(t
->label
, 100, 0);
8695 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8696 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8697 gtk_widget_set_size_request(b
, 130, 0);
8699 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8700 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8701 #if GTK_CHECK_VERSION(2, 20, 0)
8702 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8706 if (browser_mode
== XT_BM_KIOSK
) {
8707 t
->toolbar
= create_kiosk_toolbar(t
);
8708 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8711 t
->toolbar
= create_toolbar(t
);
8713 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8721 t
->browser_win
= create_browser(t
);
8722 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8724 /* oops message for user feedback */
8725 t
->oops
= gtk_entry_new();
8726 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8727 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8728 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8729 gdk_color_parse(XT_COLOR_RED
, &color
);
8730 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8731 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8732 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8735 t
->cmd
= gtk_entry_new();
8736 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8737 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8738 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8739 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8742 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8744 t
->sbe
.statusbar
= gtk_entry_new();
8745 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8746 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8747 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8748 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8750 /* create these widgets only if specified in statusbar_elems */
8752 t
->sbe
.position
= create_sbe(40);
8753 t
->sbe
.zoom
= create_sbe(40);
8754 t
->sbe
.buffercmd
= create_sbe(60);
8756 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8758 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8761 /* gtk widgets cannot be added to a box twice. sbe_* variables
8762 make sure of this */
8763 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8767 GtkWidget
*sep
= gtk_vseparator_new();
8769 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8770 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8771 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8772 FALSE
, FALSE
, FALSE
);
8777 warnx("flag \"%c\" specified more than "
8778 "once in statusbar_elems\n", *p
);
8782 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8783 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8787 warnx("flag \"%c\" specified more than "
8788 "once in statusbar_elems\n", *p
);
8792 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8793 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8797 warnx("flag \"%c\" specified more than "
8798 "once in statusbar_elems\n", *p
);
8802 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8803 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8806 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8811 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8814 t
->buffers
= create_buffers(t
);
8815 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8817 /* xtp meaning is normal by default */
8818 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8820 /* set empty favicon */
8821 xt_icon_from_name(t
, "text-html");
8823 /* and show it all */
8824 gtk_widget_show_all(b
);
8825 gtk_widget_show_all(t
->vbox
);
8827 /* compact tab bar */
8828 t
->tab_elems
.label
= gtk_label_new(title
);
8829 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8830 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8831 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8832 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8834 t
->tab_elems
.eventbox
= gtk_event_box_new();
8835 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8836 t
->tab_elems
.sep
= gtk_vseparator_new();
8838 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8839 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8840 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8841 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8842 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8843 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8845 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8847 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8849 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8852 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8854 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8856 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8859 id
= position
>= 0 ? position
:
8860 gtk_notebook_get_current_page(notebook
) + 1;
8861 if (id
> gtk_notebook_get_n_pages(notebook
))
8864 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8865 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8866 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8867 t
->tab_elems
.eventbox
, id
);
8872 #if GTK_CHECK_VERSION(2, 20, 0)
8873 /* turn spinner off if we are a new tab without uri */
8875 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8876 gtk_widget_hide(t
->spinner
);
8879 /* make notebook tabs reorderable */
8880 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8882 /* compact tabs clickable */
8883 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8884 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8886 g_object_connect(G_OBJECT(t
->cmd
),
8887 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8888 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8889 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8890 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8891 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
8894 /* reuse wv_button_cb to hide oops */
8895 g_object_connect(G_OBJECT(t
->oops
),
8896 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8899 g_signal_connect(t
->buffers
,
8900 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8901 g_object_connect(G_OBJECT(t
->buffers
),
8902 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8904 g_object_connect(G_OBJECT(t
->wv
),
8905 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8906 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8907 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8908 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8909 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8910 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8911 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8912 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8913 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8914 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8915 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8916 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8917 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8918 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8919 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8921 g_signal_connect(t
->wv
,
8922 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8924 * XXX this puts invalid url in uri_entry and that is undesirable
8927 g_signal_connect(t
->wv
,
8928 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8930 g_signal_connect(t
->wv
,
8931 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8933 /* hijack the unused keys as if we were the browser */
8934 g_object_connect(G_OBJECT(t
->toolbar
),
8935 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8938 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8939 G_CALLBACK(tab_close_cb
), t
);
8945 url_set_visibility();
8946 statusbar_set_visibility();
8949 set_current_tab(t
->tab_id
);
8950 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8955 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8959 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8964 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8965 /* restore the tab's history */
8966 if (u
&& u
->history
) {
8970 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8971 items
= g_list_next(items
);
8974 item
= g_list_nth_data(u
->history
, u
->back
);
8976 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8979 g_list_free(u
->history
);
8981 webkit_web_back_forward_list_clear(t
->bfl
);
8983 recolor_compact_tabs();
8984 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
8989 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8995 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8997 if (gtk_notebook_get_current_page(notebook
) == -1)
9000 TAILQ_FOREACH(t
, &tabs
, entry
) {
9001 if (t
->tab_id
== pn
) {
9002 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9005 uri
= get_title(t
, TRUE
);
9006 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9012 /* can't use focus_webview here */
9013 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9020 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9023 struct tab
*t
= NULL
, *tt
;
9027 TAILQ_FOREACH(tt
, &tabs
, entry
)
9028 if (tt
->tab_id
== pn
) {
9033 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9035 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9040 menuitem_response(struct tab
*t
)
9042 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9046 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9048 GtkWidget
*menu
, *menu_items
;
9049 GdkEventButton
*bevent
;
9053 if (event
->type
== GDK_BUTTON_PRESS
) {
9054 bevent
= (GdkEventButton
*) event
;
9055 menu
= gtk_menu_new();
9057 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9058 if ((uri
= get_uri(ti
)) == NULL
)
9059 /* XXX make sure there is something to print */
9060 /* XXX add gui pages in here to look purdy */
9062 menu_items
= gtk_menu_item_new_with_label(uri
);
9063 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9064 gtk_widget_show(menu_items
);
9066 g_signal_connect_swapped((menu_items
),
9067 "activate", G_CALLBACK(menuitem_response
),
9071 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9072 bevent
->button
, bevent
->time
);
9074 /* unref object so it'll free itself when popped down */
9075 #if !GTK_CHECK_VERSION(3, 0, 0)
9076 /* XXX does not need unref with gtk+3? */
9077 g_object_ref_sink(menu
);
9078 g_object_unref(menu
);
9081 return (TRUE
/* eat event */);
9084 return (FALSE
/* propagate */);
9088 icon_size_map(int icon_size
)
9090 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9091 icon_size
> GTK_ICON_SIZE_DIALOG
)
9092 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9098 create_button(char *name
, char *stockid
, int size
)
9100 GtkWidget
*button
, *image
;
9104 rcstring
= g_strdup_printf(
9105 "style \"%s-style\"\n"
9107 " GtkWidget::focus-padding = 0\n"
9108 " GtkWidget::focus-line-width = 0\n"
9112 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9113 gtk_rc_parse_string(rcstring
);
9115 button
= gtk_button_new();
9116 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9117 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9119 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9120 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9121 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9122 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9123 gtk_widget_set_name(button
, name
);
9124 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9130 button_set_stockid(GtkWidget
*button
, char *stockid
)
9134 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9135 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9136 gtk_button_set_image(GTK_BUTTON(button
), image
);
9140 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9143 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9146 if (xterm_workaround
== 0)
9150 * xterm doesn't play nice with clipboards because it clears the
9151 * primary when clicked. We rely on primary being set to properly
9152 * handle middle mouse button clicks (paste). So when someone clears
9153 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9154 * other application behavior (as in DON'T clear primary).
9157 p
= gtk_clipboard_wait_for_text(primary
);
9159 if (gdk_property_get(gdk_get_default_root_window(),
9161 gdk_atom_intern("STRING", FALSE
),
9163 1024 * 1024 /* picked out of my butt */,
9169 /* yes sir, we need to NUL the string */
9171 gtk_clipboard_set_text(primary
, p
, -1);
9185 char file
[PATH_MAX
];
9188 vbox
= gtk_vbox_new(FALSE
, 0);
9189 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9190 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9191 #if !GTK_CHECK_VERSION(3, 0, 0)
9192 /* XXX seems to be needed with gtk+2 */
9193 gtk_notebook_set_tab_hborder(notebook
, 0);
9194 gtk_notebook_set_tab_vborder(notebook
, 0);
9196 gtk_notebook_set_scrollable(notebook
, TRUE
);
9197 gtk_notebook_set_show_border(notebook
, FALSE
);
9198 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9200 abtn
= gtk_button_new();
9201 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9202 gtk_widget_set_size_request(arrow
, -1, -1);
9203 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9204 gtk_widget_set_size_request(abtn
, -1, 20);
9206 #if GTK_CHECK_VERSION(2, 20, 0)
9207 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9209 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9211 /* compact tab bar */
9212 tab_bar
= gtk_hbox_new(TRUE
, 0);
9214 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9215 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9216 gtk_widget_set_size_request(vbox
, -1, -1);
9218 g_object_connect(G_OBJECT(notebook
),
9219 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9221 g_object_connect(G_OBJECT(notebook
),
9222 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9223 NULL
, (char *)NULL
);
9224 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9225 G_CALLBACK(arrow_cb
), NULL
);
9227 main_window
= create_window();
9228 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9231 for (i
= 0; i
< LENGTH(icons
); i
++) {
9232 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9233 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9234 l
= g_list_append(l
, pb
);
9236 gtk_window_set_default_icon_list(l
);
9238 /* clipboard work around */
9239 if (xterm_workaround
)
9241 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9242 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9244 gtk_widget_show_all(abtn
);
9245 gtk_widget_show_all(main_window
);
9246 notebook_tab_set_visibility();
9250 set_hook(void **hook
, char *name
)
9253 errx(1, "set_hook");
9255 if (*hook
== NULL
) {
9256 *hook
= dlsym(RTLD_NEXT
, name
);
9258 errx(1, "can't hook %s", name
);
9262 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9264 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9266 g_return_val_if_fail(cookie1
, FALSE
);
9267 g_return_val_if_fail(cookie2
, FALSE
);
9269 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9270 !strcmp (cookie1
->value
, cookie2
->value
) &&
9271 !strcmp (cookie1
->path
, cookie2
->path
) &&
9272 !strcmp (cookie1
->domain
, cookie2
->domain
));
9276 transfer_cookies(void)
9279 SoupCookie
*sc
, *pc
;
9281 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9283 for (;cf
; cf
= cf
->next
) {
9285 sc
= soup_cookie_copy(pc
);
9286 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9289 soup_cookies_free(cf
);
9293 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9298 print_cookie("soup_cookie_jar_delete_cookie", c
);
9300 if (cookies_enabled
== 0)
9303 if (jar
== NULL
|| c
== NULL
)
9306 /* find and remove from persistent jar */
9307 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9309 for (;cf
; cf
= cf
->next
) {
9311 if (soup_cookie_equal(ci
, c
)) {
9312 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9317 soup_cookies_free(cf
);
9319 /* delete from session jar */
9320 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9324 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9326 struct domain
*d
= NULL
;
9330 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9331 jar
, p_cookiejar
, s_cookiejar
);
9333 if (cookies_enabled
== 0)
9336 /* see if we are up and running */
9337 if (p_cookiejar
== NULL
) {
9338 _soup_cookie_jar_add_cookie(jar
, cookie
);
9341 /* disallow p_cookiejar adds, shouldn't happen */
9342 if (jar
== p_cookiejar
)
9346 if (jar
== NULL
|| cookie
== NULL
)
9349 if (enable_cookie_whitelist
&&
9350 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9352 DNPRINTF(XT_D_COOKIE
,
9353 "soup_cookie_jar_add_cookie: reject %s\n",
9355 if (save_rejected_cookies
) {
9356 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9357 show_oops(NULL
, "can't open reject cookie file");
9360 fseek(r_cookie_f
, 0, SEEK_END
);
9361 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9362 cookie
->http_only
? "#HttpOnly_" : "",
9364 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9366 cookie
->secure
? "TRUE" : "FALSE",
9368 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9375 if (!allow_volatile_cookies
)
9379 if (cookie
->expires
== NULL
&& session_timeout
) {
9380 soup_cookie_set_expires(cookie
,
9381 soup_date_new_from_now(session_timeout
));
9382 print_cookie("modified add cookie", cookie
);
9385 /* see if we are white listed for persistence */
9386 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9387 /* add to persistent jar */
9388 c
= soup_cookie_copy(cookie
);
9389 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9390 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9393 /* add to session jar */
9394 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9395 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9401 char file
[PATH_MAX
];
9403 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9404 "soup_cookie_jar_add_cookie");
9405 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9406 "soup_cookie_jar_delete_cookie");
9408 if (cookies_enabled
== 0)
9412 * the following code is intricate due to overriding several libsoup
9414 * do not alter order of these operations.
9417 /* rejected cookies */
9418 if (save_rejected_cookies
)
9419 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9422 /* persistent cookies */
9423 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9424 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9426 /* session cookies */
9427 s_cookiejar
= soup_cookie_jar_new();
9428 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9429 cookie_policy
, (void *)NULL
);
9432 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9436 setup_proxy(char *uri
)
9441 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9442 soup_uri_free(proxy_uri
);
9446 if (http_proxy
!= uri
) {
9453 http_proxy
= g_strdup(uri
);
9454 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9455 suri
= soup_uri_new(http_proxy
);
9456 if (!(suri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(suri
)))
9457 g_object_set(session
, "proxy-uri", proxy_uri
,
9460 soup_uri_free(suri
);
9465 set_http_proxy(char *proxy
)
9472 /* see if we need to clear it instead */
9473 if (strlen(proxy
) == 0) {
9478 uri
= soup_uri_new(proxy
);
9479 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9490 send_cmd_to_socket(char *cmd
)
9493 struct sockaddr_un sa
;
9495 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9496 warnx("%s: socket", __func__
);
9500 sa
.sun_family
= AF_UNIX
;
9501 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9502 work_dir
, XT_SOCKET_FILE
);
9505 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9506 warnx("%s: connect", __func__
);
9510 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9511 warnx("%s: send", __func__
);
9522 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9525 char str
[XT_MAX_URL_LENGTH
];
9526 socklen_t t
= sizeof(struct sockaddr_un
);
9527 struct sockaddr_un sa
;
9532 gint fd
= g_io_channel_unix_get_fd(source
);
9534 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9539 if (getpeereid(s
, &uid
, &gid
) == -1) {
9543 if (uid
!= getuid() || gid
!= getgid()) {
9544 warnx("unauthorized user");
9550 warnx("not a valid user");
9554 n
= recv(s
, str
, sizeof(str
), 0);
9558 tt
= TAILQ_LAST(&tabs
, tab_list
);
9559 cmd_execute(tt
, str
);
9567 struct sockaddr_un sa
;
9569 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9570 warn("is_running: socket");
9574 sa
.sun_family
= AF_UNIX
;
9575 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9576 work_dir
, XT_SOCKET_FILE
);
9579 /* connect to see if there is a listener */
9580 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9581 rv
= 0; /* not running */
9583 rv
= 1; /* already running */
9594 struct sockaddr_un sa
;
9596 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9597 warn("build_socket: socket");
9601 sa
.sun_family
= AF_UNIX
;
9602 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9603 work_dir
, XT_SOCKET_FILE
);
9606 /* connect to see if there is a listener */
9607 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9608 /* no listener so we will */
9609 unlink(sa
.sun_path
);
9611 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9612 warn("build_socket: bind");
9616 if (listen(s
, 1) == -1) {
9617 warn("build_socket: listen");
9630 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9631 GtkTreeIter
*iter
, struct tab
*t
)
9635 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9643 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9644 GtkTreeIter
*iter
, struct tab
*t
)
9648 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9649 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9650 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9657 completion_add_uri(const gchar
*uri
)
9661 /* add uri to list_store */
9662 gtk_list_store_append(completion_model
, &iter
);
9663 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9667 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9668 GtkTreeIter
*iter
, gpointer user_data
)
9671 gboolean match
= FALSE
;
9673 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9679 match
= match_uri(value
, key
);
9686 completion_add(struct tab
*t
)
9688 /* enable completion for tab */
9689 t
->completion
= gtk_entry_completion_new();
9690 gtk_entry_completion_set_text_column(t
->completion
, 0);
9691 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9692 gtk_entry_completion_set_model(t
->completion
,
9693 GTK_TREE_MODEL(completion_model
));
9694 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9696 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9697 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9698 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9699 G_CALLBACK(completion_select_cb
), t
);
9700 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9701 G_CALLBACK(completion_hover_cb
), t
);
9709 if (stat(dir
, &sb
)) {
9710 if (mkdir(dir
, S_IRWXU
) == -1)
9711 err(1, "mkdir %s", dir
);
9713 err(1, "stat %s", dir
);
9715 if (S_ISDIR(sb
.st_mode
) == 0)
9716 errx(1, "%s not a dir", dir
);
9717 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9718 warnx("fixing invalid permissions on %s", dir
);
9719 if (chmod(dir
, S_IRWXU
) == -1)
9720 err(1, "chmod %s", dir
);
9728 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9734 main(int argc
, char *argv
[])
9737 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9738 char conf
[PATH_MAX
] = { '\0' };
9739 char file
[PATH_MAX
];
9740 char *env_proxy
= NULL
;
9744 struct sigaction sact
;
9745 GIOChannel
*channel
;
9750 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9754 RB_INIT(&downloads
);
9758 TAILQ_INIT(&aliases
);
9763 /* fiddle with ulimits */
9764 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9767 /* just use them all */
9768 rlp
.rlim_cur
= rlp
.rlim_max
;
9769 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9771 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9773 else if (rlp
.rlim_cur
<= 256)
9774 startpage_add("%s requires at least 256 file "
9775 "descriptors, currently it has up to %d available",
9776 __progname
, rlp
.rlim_cur
);
9779 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9788 errx(0 , "Version: %s", version
);
9791 strlcpy(conf
, optarg
, sizeof(conf
));
9794 strlcpy(named_session
, optarg
, sizeof(named_session
));
9815 gnutls_global_init();
9817 /* generate session keys for xtp pages */
9818 generate_xtp_session_key(&dl_session_key
);
9819 generate_xtp_session_key(&hl_session_key
);
9820 generate_xtp_session_key(&cl_session_key
);
9821 generate_xtp_session_key(&fl_session_key
);
9824 if (!g_thread_supported()) {
9825 g_thread_init(NULL
);
9827 gdk_threads_enter();
9829 gtk_init(&argc
, &argv
);
9832 bzero(&sact
, sizeof(sact
));
9833 sigemptyset(&sact
.sa_mask
);
9834 sact
.sa_handler
= sigchild
;
9835 sact
.sa_flags
= SA_NOCLDSTOP
;
9836 sigaction(SIGCHLD
, &sact
, NULL
);
9838 /* set download dir */
9839 pwd
= getpwuid(getuid());
9841 errx(1, "invalid user %d", getuid());
9842 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9844 /* compile buffer command regexes */
9847 /* set default string settings */
9848 home
= g_strdup("https://www.cyphertite.com");
9849 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9850 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9851 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9852 cmd_font_name
= g_strdup("monospace normal 9");
9853 oops_font_name
= g_strdup("monospace normal 9");
9854 statusbar_font_name
= g_strdup("monospace normal 9");
9855 tabbar_font_name
= g_strdup("monospace normal 9");
9856 statusbar_elems
= g_strdup("BP");
9858 /* read config file */
9859 if (strlen(conf
) == 0)
9860 snprintf(conf
, sizeof conf
, "%s/.%s",
9861 pwd
->pw_dir
, XT_CONF_FILE
);
9862 config_parse(conf
, 0);
9865 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9866 oops_font
= pango_font_description_from_string(oops_font_name
);
9867 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9868 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9870 /* working directory */
9871 if (strlen(work_dir
) == 0)
9872 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9873 pwd
->pw_dir
, XT_DIR
);
9876 /* icon cache dir */
9877 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9881 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9885 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9886 work_dir
, XT_SESSIONS_DIR
);
9887 xxx_dir(sessions_dir
);
9889 /* runtime settings that can override config file */
9890 if (runtime_settings
[0] != '\0')
9891 config_parse(runtime_settings
, 1);
9894 if (!strcmp(download_dir
, pwd
->pw_dir
))
9895 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9896 xxx_dir(download_dir
);
9898 /* favorites file */
9899 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9900 if (stat(file
, &sb
)) {
9901 warnx("favorites file doesn't exist, creating it");
9902 if ((f
= fopen(file
, "w")) == NULL
)
9903 err(1, "favorites");
9907 /* quickmarks file */
9908 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9909 if (stat(file
, &sb
)) {
9910 warnx("quickmarks file doesn't exist, creating it");
9911 if ((f
= fopen(file
, "w")) == NULL
)
9912 err(1, "quickmarks");
9917 session
= webkit_get_default_session();
9922 if (stat(ssl_ca_file
, &sb
)) {
9923 warnx("no CA file: %s", ssl_ca_file
);
9924 g_free(ssl_ca_file
);
9927 g_object_set(session
,
9928 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9929 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9933 /* guess_search regex */
9934 if (url_regex
== NULL
)
9935 url_regex
= g_strdup(XT_URL_REGEX
);
9937 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
9938 startpage_add("invalid url regex %s", url_regex
);
9941 env_proxy
= getenv("http_proxy");
9943 setup_proxy(env_proxy
);
9945 setup_proxy(http_proxy
);
9948 send_cmd_to_socket(argv
[0]);
9952 /* set some connection parameters */
9953 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9954 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9957 /* see if there is already an xxxterm running */
9958 if (single_instance
&& is_running()) {
9960 warnx("already running");
9965 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9966 send_cmd_to_socket(cmd
);
9976 /* uri completion */
9977 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9980 buffers_store
= gtk_list_store_new
9981 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9987 notebook_tab_set_visibility();
9989 if (save_global_history
)
9990 restore_global_history();
9992 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9993 restore_saved_tabs();
9995 a
.s
= named_session
;
9996 a
.i
= XT_SES_DONOTHING
;
9997 open_tabs(NULL
, &a
);
10000 /* see if we have an exception */
10001 if (!TAILQ_EMPTY(&spl
)) {
10002 create_new_tab("about:startpage", NULL
, focus
, -1);
10007 create_new_tab(argv
[0], NULL
, focus
, -1);
10014 if (TAILQ_EMPTY(&tabs
))
10015 create_new_tab(home
, NULL
, 1, -1);
10018 if ((s
= build_socket()) != -1) {
10019 channel
= g_io_channel_unix_new(s
);
10020 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10025 if (!g_thread_supported()) {
10026 gdk_threads_leave();
10029 gnutls_global_deinit();