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 */
230 #define XT_HINT_NONE (0)
231 #define XT_HINT_NUMERICAL (1)
232 #define XT_HINT_ALPHANUM (2)
236 /* custom stylesheet */
246 WebKitWebSettings
*settings
;
250 double mark
[XT_NOMARKS
];
252 TAILQ_HEAD(tab_list
, tab
);
255 RB_ENTRY(history
) entry
;
259 RB_HEAD(history_list
, history
);
262 RB_ENTRY(download
) entry
;
264 WebKitDownload
*download
;
267 RB_HEAD(download_list
, download
);
270 RB_ENTRY(domain
) entry
;
272 int handy
; /* app use */
274 RB_HEAD(domain_list
, domain
);
277 TAILQ_ENTRY(undo
) entry
;
280 int back
; /* Keeps track of how many back
281 * history items there are. */
283 TAILQ_HEAD(undo_tailq
, undo
);
287 TAILQ_ENTRY(sp
) entry
;
289 TAILQ_HEAD(sp_list
, sp
);
291 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
292 int next_download_id
= 1;
301 #define XT_NAME ("XXXTerm")
302 #define XT_DIR (".xxxterm")
303 #define XT_CACHE_DIR ("cache")
304 #define XT_CERT_DIR ("certs/")
305 #define XT_SESSIONS_DIR ("sessions/")
306 #define XT_CONF_FILE ("xxxterm.conf")
307 #define XT_FAVS_FILE ("favorites")
308 #define XT_QMARKS_FILE ("quickmarks")
309 #define XT_SAVED_TABS_FILE ("main_session")
310 #define XT_RESTART_TABS_FILE ("restart_tabs")
311 #define XT_SOCKET_FILE ("socket")
312 #define XT_HISTORY_FILE ("history")
313 #define XT_REJECT_FILE ("rejected.txt")
314 #define XT_COOKIE_FILE ("cookies.txt")
315 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
316 #define XT_CB_HANDLED (TRUE)
317 #define XT_CB_PASSTHROUGH (FALSE)
318 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
319 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
320 #define XT_DLMAN_REFRESH "10"
321 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
322 "td{overflow: hidden;" \
323 " padding: 2px 2px 2px 2px;" \
324 " border: 1px solid black;" \
325 " vertical-align:top;" \
326 " word-wrap: break-word}\n" \
327 "tr:hover{background: #ffff99}\n" \
328 "th{background-color: #cccccc;" \
329 " border: 1px solid black}\n" \
330 "table{width: 100%%;" \
331 " border: 1px black solid;" \
332 " border-collapse:collapse}\n" \
334 "border: 1px solid black;" \
337 ".progress-inner{float: left;" \
339 " background: green}\n" \
340 ".dlstatus{font-size: small;" \
341 " text-align: center}\n" \
343 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
344 #define XT_MAX_UNDO_CLOSE_TAB (32)
345 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
346 #define XT_PRINT_EXTRA_MARGIN 10
347 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
348 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
351 #define XT_COLOR_RED "#cc0000"
352 #define XT_COLOR_YELLOW "#ffff66"
353 #define XT_COLOR_BLUE "lightblue"
354 #define XT_COLOR_GREEN "#99ff66"
355 #define XT_COLOR_WHITE "white"
356 #define XT_COLOR_BLACK "black"
358 #define XT_COLOR_CT_BACKGROUND "#000000"
359 #define XT_COLOR_CT_INACTIVE "#dddddd"
360 #define XT_COLOR_CT_ACTIVE "#bbbb00"
361 #define XT_COLOR_CT_SEPARATOR "#555555"
363 #define XT_COLOR_SB_SEPARATOR "#555555"
365 #define XT_PROTO_DELIM "://"
368 * xxxterm "protocol" (xtp)
369 * We use this for managing stuff like downloads and favorites. They
370 * make magical HTML pages in memory which have xxxt:// links in order
371 * to communicate with xxxterm's internals. These links take the format:
372 * xxxt://class/session_key/action/arg
374 * Don't begin xtp class/actions as 0. atoi returns that on error.
376 * Typically we have not put addition of items in this framework, as
377 * adding items is either done via an ex-command or via a keybinding instead.
380 #define XT_XTP_STR "xxxt://"
382 /* XTP classes (xxxt://<class>) */
383 #define XT_XTP_INVALID 0 /* invalid */
384 #define XT_XTP_DL 1 /* downloads */
385 #define XT_XTP_HL 2 /* history */
386 #define XT_XTP_CL 3 /* cookies */
387 #define XT_XTP_FL 4 /* favorites */
389 /* XTP download actions */
390 #define XT_XTP_DL_LIST 1
391 #define XT_XTP_DL_CANCEL 2
392 #define XT_XTP_DL_REMOVE 3
394 /* XTP history actions */
395 #define XT_XTP_HL_LIST 1
396 #define XT_XTP_HL_REMOVE 2
398 /* XTP cookie actions */
399 #define XT_XTP_CL_LIST 1
400 #define XT_XTP_CL_REMOVE 2
402 /* XTP cookie actions */
403 #define XT_XTP_FL_LIST 1
404 #define XT_XTP_FL_REMOVE 2
407 #define XT_MOVE_INVALID (0)
408 #define XT_MOVE_DOWN (1)
409 #define XT_MOVE_UP (2)
410 #define XT_MOVE_BOTTOM (3)
411 #define XT_MOVE_TOP (4)
412 #define XT_MOVE_PAGEDOWN (5)
413 #define XT_MOVE_PAGEUP (6)
414 #define XT_MOVE_HALFDOWN (7)
415 #define XT_MOVE_HALFUP (8)
416 #define XT_MOVE_LEFT (9)
417 #define XT_MOVE_FARLEFT (10)
418 #define XT_MOVE_RIGHT (11)
419 #define XT_MOVE_FARRIGHT (12)
420 #define XT_MOVE_PERCENT (13)
422 #define XT_QMARK_SET (0)
423 #define XT_QMARK_OPEN (1)
424 #define XT_QMARK_TAB (2)
426 #define XT_MARK_SET (0)
427 #define XT_MARK_GOTO (1)
429 #define XT_TAB_LAST (-4)
430 #define XT_TAB_FIRST (-3)
431 #define XT_TAB_PREV (-2)
432 #define XT_TAB_NEXT (-1)
433 #define XT_TAB_INVALID (0)
434 #define XT_TAB_NEW (1)
435 #define XT_TAB_DELETE (2)
436 #define XT_TAB_DELQUIT (3)
437 #define XT_TAB_OPEN (4)
438 #define XT_TAB_UNDO_CLOSE (5)
439 #define XT_TAB_SHOW (6)
440 #define XT_TAB_HIDE (7)
441 #define XT_TAB_NEXTSTYLE (8)
443 #define XT_NAV_INVALID (0)
444 #define XT_NAV_BACK (1)
445 #define XT_NAV_FORWARD (2)
446 #define XT_NAV_RELOAD (3)
448 #define XT_FOCUS_INVALID (0)
449 #define XT_FOCUS_URI (1)
450 #define XT_FOCUS_SEARCH (2)
452 #define XT_SEARCH_INVALID (0)
453 #define XT_SEARCH_NEXT (1)
454 #define XT_SEARCH_PREV (2)
456 #define XT_PASTE_CURRENT_TAB (0)
457 #define XT_PASTE_NEW_TAB (1)
459 #define XT_ZOOM_IN (-1)
460 #define XT_ZOOM_OUT (-2)
461 #define XT_ZOOM_NORMAL (100)
463 #define XT_URL_SHOW (1)
464 #define XT_URL_HIDE (2)
466 #define XT_WL_TOGGLE (1<<0)
467 #define XT_WL_ENABLE (1<<1)
468 #define XT_WL_DISABLE (1<<2)
469 #define XT_WL_FQDN (1<<3) /* default */
470 #define XT_WL_TOPLEVEL (1<<4)
471 #define XT_WL_PERSISTENT (1<<5)
472 #define XT_WL_SESSION (1<<6)
473 #define XT_WL_RELOAD (1<<7)
475 #define XT_SHOW (1<<7)
476 #define XT_DELETE (1<<8)
477 #define XT_SAVE (1<<9)
478 #define XT_OPEN (1<<10)
480 #define XT_CMD_OPEN (0)
481 #define XT_CMD_OPEN_CURRENT (1)
482 #define XT_CMD_TABNEW (2)
483 #define XT_CMD_TABNEW_CURRENT (3)
485 #define XT_STATUS_NOTHING (0)
486 #define XT_STATUS_LINK (1)
487 #define XT_STATUS_URI (2)
488 #define XT_STATUS_LOADING (3)
490 #define XT_SES_DONOTHING (0)
491 #define XT_SES_CLOSETABS (1)
493 #define XT_BM_NORMAL (0)
494 #define XT_BM_WHITELIST (1)
495 #define XT_BM_KIOSK (2)
497 #define XT_PREFIX (1<<0)
498 #define XT_USERARG (1<<1)
499 #define XT_URLARG (1<<2)
500 #define XT_INTARG (1<<3)
502 #define XT_TABS_NORMAL 0
503 #define XT_TABS_COMPACT 1
505 #define XT_BUFCMD_SZ (8)
513 TAILQ_ENTRY(mime_type
) entry
;
515 TAILQ_HEAD(mime_type_list
, mime_type
);
521 TAILQ_ENTRY(alias
) entry
;
523 TAILQ_HEAD(alias_list
, alias
);
525 /* settings that require restart */
526 int tabless
= 0; /* allow only 1 tab */
527 int enable_socket
= 0;
528 int single_instance
= 0; /* only allow one xxxterm to run */
529 int fancy_bar
= 1; /* fancy toolbar */
530 int browser_mode
= XT_BM_NORMAL
;
531 int enable_localstorage
= 0;
532 char *statusbar_elems
= NULL
;
534 /* runtime settings */
535 int show_tabs
= 1; /* show tabs on notebook */
536 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
537 int show_url
= 1; /* show url toolbar on notebook */
538 int show_statusbar
= 0; /* vimperator style status bar */
539 int ctrl_click_focus
= 0; /* ctrl click gets focus */
540 int cookies_enabled
= 1; /* enable cookies */
541 int read_only_cookies
= 0; /* enable to not write cookies */
542 int enable_scripts
= 1;
543 int enable_plugins
= 0;
544 gfloat default_zoom_level
= 1.0;
545 char default_script
[PATH_MAX
];
546 int window_height
= 768;
547 int window_width
= 1024;
548 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
549 int refresh_interval
= 10; /* download refresh interval */
550 int enable_cookie_whitelist
= 0;
551 int enable_js_whitelist
= 0;
552 int session_timeout
= 3600; /* cookie session timeout */
553 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
554 char *ssl_ca_file
= NULL
;
555 char *resource_dir
= NULL
;
556 gboolean ssl_strict_certs
= FALSE
;
557 int append_next
= 1; /* append tab after current tab */
559 char *search_string
= NULL
;
560 char *http_proxy
= NULL
;
561 char download_dir
[PATH_MAX
];
562 char runtime_settings
[PATH_MAX
]; /* override of settings */
563 int allow_volatile_cookies
= 0;
564 int save_global_history
= 0; /* save global history to disk */
565 char *user_agent
= NULL
;
566 int save_rejected_cookies
= 0;
567 int session_autosave
= 0;
568 int guess_search
= 0;
569 int dns_prefetch
= FALSE
;
570 gint max_connections
= 25;
571 gint max_host_connections
= 5;
572 gint enable_spell_checking
= 0;
573 char *spell_check_languages
= NULL
;
574 int xterm_workaround
= 0;
575 char *url_regex
= NULL
;
577 char *cmd_font_name
= NULL
;
578 char *oops_font_name
= NULL
;
579 char *statusbar_font_name
= NULL
;
580 char *tabbar_font_name
= NULL
;
581 PangoFontDescription
*cmd_font
;
582 PangoFontDescription
*oops_font
;
583 PangoFontDescription
*statusbar_font
;
584 PangoFontDescription
*tabbar_font
;
585 char *qmarks
[XT_NOMARKS
];
587 int btn_down
; /* M1 down in any wv */
588 regex_t url_re
; /* guess_search regex */
592 int set_browser_mode(struct settings
*, char *);
593 int set_cookie_policy(struct settings
*, char *);
594 int set_download_dir(struct settings
*, char *);
595 int set_default_script(struct settings
*, char *);
596 int set_runtime_dir(struct settings
*, char *);
597 int set_tab_style(struct settings
*, char *);
598 int set_work_dir(struct settings
*, char *);
599 int add_alias(struct settings
*, char *);
600 int add_mime_type(struct settings
*, char *);
601 int add_cookie_wl(struct settings
*, char *);
602 int add_js_wl(struct settings
*, char *);
603 int add_kb(struct settings
*, char *);
604 void button_set_stockid(GtkWidget
*, char *);
605 GtkWidget
* create_button(char *, char *, int);
607 char *get_browser_mode(struct settings
*);
608 char *get_cookie_policy(struct settings
*);
609 char *get_download_dir(struct settings
*);
610 char *get_default_script(struct settings
*);
611 char *get_runtime_dir(struct settings
*);
612 char *get_tab_style(struct settings
*);
613 char *get_work_dir(struct settings
*);
614 void startpage_add(const char *, ...);
616 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
617 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
618 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
619 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
620 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
622 void recalc_tabs(void);
623 void recolor_compact_tabs(void);
624 void set_current_tab(int page_num
);
625 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
626 void marks_clear(struct tab
*t
);
628 int set_http_proxy(char *);
631 int (*set
)(struct settings
*, char *);
632 char *(*get
)(struct settings
*);
633 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
636 struct special s_browser_mode
= {
642 struct special s_cookie
= {
648 struct special s_alias
= {
654 struct special s_mime
= {
660 struct special s_js
= {
666 struct special s_kb
= {
672 struct special s_cookie_wl
= {
678 struct special s_default_script
= {
684 struct special s_download_dir
= {
690 struct special s_work_dir
= {
696 struct special s_tab_style
= {
705 #define XT_S_INVALID (0)
708 #define XT_S_FLOAT (3)
710 #define XT_SF_RESTART (1<<0)
711 #define XT_SF_RUNTIME (1<<1)
716 int (*activate
)(char *);
718 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
719 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
720 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
721 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
722 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
723 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
724 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
725 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
726 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
727 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
728 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
729 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
730 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
731 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
732 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
733 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
734 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
735 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
736 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
737 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
738 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
739 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
740 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
741 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
742 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
743 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
744 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
745 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
746 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
747 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
748 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
749 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
750 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
751 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
752 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
753 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
754 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
755 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
756 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
757 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
758 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
759 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
760 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
761 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
762 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
763 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
766 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
767 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
768 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
769 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
771 /* runtime settings */
772 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
773 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
774 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
775 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
776 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
779 int about(struct tab
*, struct karg
*);
780 int blank(struct tab
*, struct karg
*);
781 int ca_cmd(struct tab
*, struct karg
*);
782 int cookie_show_wl(struct tab
*, struct karg
*);
783 int js_show_wl(struct tab
*, struct karg
*);
784 int help(struct tab
*, struct karg
*);
785 int set(struct tab
*, struct karg
*);
786 int stats(struct tab
*, struct karg
*);
787 int marco(struct tab
*, struct karg
*);
788 int startpage(struct tab
*, struct karg
*);
789 const char * marco_message(int *);
790 int xtp_page_cl(struct tab
*, struct karg
*);
791 int xtp_page_dl(struct tab
*, struct karg
*);
792 int xtp_page_fl(struct tab
*, struct karg
*);
793 int xtp_page_hl(struct tab
*, struct karg
*);
794 void xt_icon_from_file(struct tab
*, char *);
795 const gchar
*get_uri(struct tab
*);
796 const gchar
*get_title(struct tab
*, bool);
798 #define XT_URI_ABOUT ("about:")
799 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
800 #define XT_URI_ABOUT_ABOUT ("about")
801 #define XT_URI_ABOUT_BLANK ("blank")
802 #define XT_URI_ABOUT_CERTS ("certs")
803 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
804 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
805 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
806 #define XT_URI_ABOUT_FAVORITES ("favorites")
807 #define XT_URI_ABOUT_HELP ("help")
808 #define XT_URI_ABOUT_HISTORY ("history")
809 #define XT_URI_ABOUT_JSWL ("jswl")
810 #define XT_URI_ABOUT_SET ("set")
811 #define XT_URI_ABOUT_STATS ("stats")
812 #define XT_URI_ABOUT_MARCO ("marco")
813 #define XT_URI_ABOUT_STARTPAGE ("startpage")
817 int (*func
)(struct tab
*, struct karg
*);
819 { XT_URI_ABOUT_ABOUT
, about
},
820 { XT_URI_ABOUT_BLANK
, blank
},
821 { XT_URI_ABOUT_CERTS
, ca_cmd
},
822 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
823 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
824 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
825 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
826 { XT_URI_ABOUT_HELP
, help
},
827 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
828 { XT_URI_ABOUT_JSWL
, js_show_wl
},
829 { XT_URI_ABOUT_SET
, set
},
830 { XT_URI_ABOUT_STATS
, stats
},
831 { XT_URI_ABOUT_MARCO
, marco
},
832 { XT_URI_ABOUT_STARTPAGE
, startpage
},
835 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
836 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
837 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
838 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
839 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
840 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
841 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
844 extern char *__progname
;
847 GtkWidget
*main_window
;
848 GtkNotebook
*notebook
;
850 GtkWidget
*arrow
, *abtn
;
851 struct tab_list tabs
;
852 struct history_list hl
;
853 struct download_list downloads
;
854 struct domain_list c_wl
;
855 struct domain_list js_wl
;
856 struct undo_tailq undos
;
857 struct keybinding_list kbl
;
860 int updating_dl_tabs
= 0;
861 int updating_hl_tabs
= 0;
862 int updating_cl_tabs
= 0;
863 int updating_fl_tabs
= 0;
865 uint64_t blocked_cookies
= 0;
866 char named_session
[PATH_MAX
];
867 int icon_size_map(int);
869 GtkListStore
*completion_model
;
870 void completion_add(struct tab
*);
871 void completion_add_uri(const gchar
*);
872 GtkListStore
*buffers_store
;
873 void xxx_dir(char *);
875 /* marks and quickmarks array storage.
876 * first a-z, then A-Z, then 0-9 */
883 if (i
>= 0 && i
<= 'z' - 'a')
887 if (i
>= 0 && i
<= 'Z' - 'A')
902 if (m
>= 'a' && m
<= 'z')
903 return ret
+ m
- 'a';
905 ret
+= 'z' - 'a' + 1;
906 if (m
>= 'A' && m
<= 'Z')
907 return ret
+ m
- 'A';
909 ret
+= 'Z' - 'A' + 1;
910 if (m
>= '0' && m
<= '9')
911 return ret
+ m
- '0';
920 int saved_errno
, status
;
925 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
929 if (errno
!= ECHILD
) {
931 clog_warn("sigchild: waitpid:");
937 if (WIFEXITED(status
)) {
938 if (WEXITSTATUS(status
) != 0) {
940 clog_warnx("sigchild: child exit status: %d",
941 WEXITSTATUS(status));
946 clog_warnx("sigchild: child is terminated abnormally");
955 is_g_object_setting(GObject
*o
, char *str
)
957 guint n_props
= 0, i
;
958 GParamSpec
**proplist
;
960 if (! G_IS_OBJECT(o
))
963 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
966 for (i
=0; i
< n_props
; i
++) {
967 if (! strcmp(proplist
[i
]->name
, str
))
974 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
978 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
980 "<title>%s</title>\n"
989 addstyles
? XT_PAGE_STYLE
: "",
998 * Display a web page from a HTML string in memory, rather than from a URL
1001 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1003 char file
[PATH_MAX
];
1006 /* we set this to indicate we want to manually do navaction */
1008 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1010 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1012 /* set t->xtp_meaning */
1013 for (i
= 0; i
< LENGTH(about_list
); i
++)
1014 if (!strcmp(title
, about_list
[i
].name
)) {
1019 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1020 #if GTK_CHECK_VERSION(2, 20, 0)
1021 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1022 gtk_widget_hide(t
->spinner
);
1024 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1025 xt_icon_from_file(t
, file
);
1030 get_current_tab(void)
1034 TAILQ_FOREACH(t
, &tabs
, entry
) {
1035 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1039 warnx("%s: no current tab", __func__
);
1045 set_status(struct tab
*t
, gchar
*s
, int status
)
1053 case XT_STATUS_LOADING
:
1054 type
= g_strdup_printf("Loading: %s", s
);
1057 case XT_STATUS_LINK
:
1058 type
= g_strdup_printf("Link: %s", s
);
1060 t
->status
= g_strdup(gtk_entry_get_text(
1061 GTK_ENTRY(t
->sbe
.statusbar
)));
1065 type
= g_strdup_printf("%s", s
);
1067 t
->status
= g_strdup(type
);
1071 t
->status
= g_strdup(s
);
1073 case XT_STATUS_NOTHING
:
1078 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1084 hide_cmd(struct tab
*t
)
1086 gtk_widget_hide(t
->cmd
);
1090 show_cmd(struct tab
*t
)
1092 gtk_widget_hide(t
->oops
);
1093 gtk_widget_show(t
->cmd
);
1097 hide_buffers(struct tab
*t
)
1099 gtk_widget_hide(t
->buffers
);
1100 gtk_list_store_clear(buffers_store
);
1110 sort_tabs_by_page_num(struct tab
***stabs
)
1115 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1117 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1119 TAILQ_FOREACH(t
, &tabs
, entry
)
1120 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1126 buffers_make_list(void)
1129 const gchar
*title
= NULL
;
1131 struct tab
**stabs
= NULL
;
1133 num_tabs
= sort_tabs_by_page_num(&stabs
);
1135 for (i
= 0; i
< num_tabs
; i
++)
1137 gtk_list_store_append(buffers_store
, &iter
);
1138 title
= get_title(stabs
[i
], FALSE
);
1139 gtk_list_store_set(buffers_store
, &iter
,
1140 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1150 show_buffers(struct tab
*t
)
1152 buffers_make_list();
1153 gtk_widget_show(t
->buffers
);
1154 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1158 toggle_buffers(struct tab
*t
)
1160 if (gtk_widget_get_visible(t
->buffers
))
1167 buffers(struct tab
*t
, struct karg
*args
)
1175 hide_oops(struct tab
*t
)
1177 gtk_widget_hide(t
->oops
);
1181 show_oops(struct tab
*at
, const char *fmt
, ...)
1185 struct tab
*t
= NULL
;
1191 if ((t
= get_current_tab()) == NULL
)
1197 if (vasprintf(&msg
, fmt
, ap
) == -1)
1198 errx(1, "show_oops failed");
1201 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1202 gtk_widget_hide(t
->cmd
);
1203 gtk_widget_show(t
->oops
);
1210 get_as_string(struct settings
*s
)
1221 warnx("get_as_string skip %s\n", s
->name
);
1222 } else if (s
->type
== XT_S_INT
)
1223 r
= g_strdup_printf("%d", *s
->ival
);
1224 else if (s
->type
== XT_S_STR
)
1225 r
= g_strdup(*s
->sval
);
1226 else if (s
->type
== XT_S_FLOAT
)
1227 r
= g_strdup_printf("%f", *s
->fval
);
1229 r
= g_strdup_printf("INVALID TYPE");
1235 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1240 for (i
= 0; i
< LENGTH(rs
); i
++) {
1241 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1242 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1244 s
= get_as_string(&rs
[i
]);
1245 cb(&rs
[i
], s
, cb_args
);
1252 set_browser_mode(struct settings
*s
, char *val
)
1254 if (!strcmp(val
, "whitelist")) {
1255 browser_mode
= XT_BM_WHITELIST
;
1256 allow_volatile_cookies
= 0;
1257 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1258 cookies_enabled
= 1;
1259 enable_cookie_whitelist
= 1;
1260 read_only_cookies
= 0;
1261 save_rejected_cookies
= 0;
1262 session_timeout
= 3600;
1264 enable_js_whitelist
= 1;
1265 enable_localstorage
= 0;
1266 } else if (!strcmp(val
, "normal")) {
1267 browser_mode
= XT_BM_NORMAL
;
1268 allow_volatile_cookies
= 0;
1269 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1270 cookies_enabled
= 1;
1271 enable_cookie_whitelist
= 0;
1272 read_only_cookies
= 0;
1273 save_rejected_cookies
= 0;
1274 session_timeout
= 3600;
1276 enable_js_whitelist
= 0;
1277 enable_localstorage
= 1;
1278 } else if (!strcmp(val
, "kiosk")) {
1279 browser_mode
= XT_BM_KIOSK
;
1280 allow_volatile_cookies
= 0;
1281 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1282 cookies_enabled
= 1;
1283 enable_cookie_whitelist
= 0;
1284 read_only_cookies
= 0;
1285 save_rejected_cookies
= 0;
1286 session_timeout
= 3600;
1288 enable_js_whitelist
= 0;
1289 enable_localstorage
= 1;
1299 get_browser_mode(struct settings
*s
)
1303 if (browser_mode
== XT_BM_WHITELIST
)
1304 r
= g_strdup("whitelist");
1305 else if (browser_mode
== XT_BM_NORMAL
)
1306 r
= g_strdup("normal");
1307 else if (browser_mode
== XT_BM_KIOSK
)
1308 r
= g_strdup("kiosk");
1316 set_cookie_policy(struct settings
*s
, char *val
)
1318 if (!strcmp(val
, "no3rdparty"))
1319 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1320 else if (!strcmp(val
, "accept"))
1321 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1322 else if (!strcmp(val
, "reject"))
1323 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1331 get_cookie_policy(struct settings
*s
)
1335 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1336 r
= g_strdup("no3rdparty");
1337 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1338 r
= g_strdup("accept");
1339 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1340 r
= g_strdup("reject");
1348 get_default_script(struct settings
*s
)
1350 if (default_script
[0] == '\0')
1352 return (g_strdup(default_script
));
1356 set_default_script(struct settings
*s
, char *val
)
1359 snprintf(default_script
, sizeof default_script
, "%s/%s",
1360 pwd
->pw_dir
, &val
[1]);
1362 strlcpy(default_script
, val
, sizeof default_script
);
1368 get_download_dir(struct settings
*s
)
1370 if (download_dir
[0] == '\0')
1372 return (g_strdup(download_dir
));
1376 set_download_dir(struct settings
*s
, char *val
)
1379 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1380 pwd
->pw_dir
, &val
[1]);
1382 strlcpy(download_dir
, val
, sizeof download_dir
);
1389 * We use these to prevent people putting xxxt:// URLs on
1390 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1392 #define XT_XTP_SES_KEY_SZ 8
1393 #define XT_XTP_SES_KEY_HEX_FMT \
1394 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1395 char *dl_session_key
; /* downloads */
1396 char *hl_session_key
; /* history list */
1397 char *cl_session_key
; /* cookie list */
1398 char *fl_session_key
; /* favorites list */
1400 char work_dir
[PATH_MAX
];
1401 char certs_dir
[PATH_MAX
];
1402 char cache_dir
[PATH_MAX
];
1403 char sessions_dir
[PATH_MAX
];
1404 char cookie_file
[PATH_MAX
];
1405 SoupURI
*proxy_uri
= NULL
;
1406 SoupSession
*session
;
1407 SoupCookieJar
*s_cookiejar
;
1408 SoupCookieJar
*p_cookiejar
;
1409 char rc_fname
[PATH_MAX
];
1411 struct mime_type_list mtl
;
1412 struct alias_list aliases
;
1415 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1416 void delete_tab(struct tab
*);
1417 void setzoom_webkit(struct tab
*, int);
1418 int run_script(struct tab
*, char *);
1419 int download_rb_cmp(struct download
*, struct download
*);
1420 gboolean
cmd_execute(struct tab
*t
, char *str
);
1423 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1425 return (strcmp(h1
->uri
, h2
->uri
));
1427 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1430 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1432 return (strcmp(d1
->d
, d2
->d
));
1434 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1437 get_work_dir(struct settings
*s
)
1439 if (work_dir
[0] == '\0')
1441 return (g_strdup(work_dir
));
1445 set_work_dir(struct settings
*s
, char *val
)
1448 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1449 pwd
->pw_dir
, &val
[1]);
1451 strlcpy(work_dir
, val
, sizeof work_dir
);
1457 get_tab_style(struct settings
*s
)
1459 if (tab_style
== XT_TABS_NORMAL
)
1460 return (g_strdup("normal"));
1462 return (g_strdup("compact"));
1466 set_tab_style(struct settings
*s
, char *val
)
1468 if (!strcmp(val
, "normal"))
1469 tab_style
= XT_TABS_NORMAL
;
1470 else if (!strcmp(val
, "compact"))
1471 tab_style
= XT_TABS_COMPACT
;
1479 * generate a session key to secure xtp commands.
1480 * pass in a ptr to the key in question and it will
1481 * be modified in place.
1484 generate_xtp_session_key(char **key
)
1486 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1492 /* make a new one */
1493 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1494 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1495 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1496 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1498 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1502 * validate a xtp session key.
1506 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1508 if (strcmp(trusted
, untrusted
) != 0) {
1509 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1518 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1520 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1522 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1524 struct valid_url_types
{
1535 valid_url_type(char *url
)
1539 for (i
= 0; i
< LENGTH(vut
); i
++)
1540 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1547 print_cookie(char *msg
, SoupCookie
*c
)
1553 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1554 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1555 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1556 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1557 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1558 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1559 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1560 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1561 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1562 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1566 walk_alias(struct settings
*s
,
1567 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1572 if (s
== NULL
|| cb
== NULL
) {
1573 show_oops(NULL
, "walk_alias invalid parameters");
1577 TAILQ_FOREACH(a
, &aliases
, entry
) {
1578 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1579 cb(s
, str
, cb_args
);
1585 match_alias(char *url_in
)
1589 char *url_out
= NULL
, *search
, *enc_arg
;
1591 search
= g_strdup(url_in
);
1593 if (strsep(&arg
, " \t") == NULL
) {
1594 show_oops(NULL
, "match_alias: NULL URL");
1598 TAILQ_FOREACH(a
, &aliases
, entry
) {
1599 if (!strcmp(search
, a
->a_name
))
1604 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1607 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1608 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1611 url_out
= g_strdup_printf(a
->a_uri
, "");
1619 guess_url_type(char *url_in
)
1622 char *url_out
= NULL
, *enc_search
= NULL
;
1624 url_out
= match_alias(url_in
);
1625 if (url_out
!= NULL
)
1628 if (guess_search
&& url_regex
&&
1629 !(g_str_has_prefix(url_in
, "http://") ||
1630 g_str_has_prefix(url_in
, "https://"))) {
1631 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1632 /* invalid URI so search instead */
1633 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1634 url_out
= g_strdup_printf(search_string
, enc_search
);
1640 /* XXX not sure about this heuristic */
1641 if (stat(url_in
, &sb
) == 0)
1642 url_out
= g_strdup_printf("file://%s", url_in
);
1644 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1646 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1652 load_uri(struct tab
*t
, gchar
*uri
)
1655 gchar
*newuri
= NULL
;
1661 /* Strip leading spaces. */
1662 while (*uri
&& isspace(*uri
))
1665 if (strlen(uri
) == 0) {
1670 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1672 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1673 for (i
= 0; i
< LENGTH(about_list
); i
++)
1674 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1675 bzero(&args
, sizeof args
);
1676 about_list
[i
].func(t
, &args
);
1677 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1681 show_oops(t
, "invalid about page");
1685 if (valid_url_type(uri
)) {
1686 newuri
= guess_url_type(uri
);
1690 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1692 webkit_web_view_load_uri(t
->wv
, uri
);
1699 get_uri(struct tab
*t
)
1701 const gchar
*uri
= NULL
;
1703 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1705 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1706 uri
= webkit_web_view_get_uri(t
->wv
);
1708 /* use tmp_uri to make sure it is g_freed */
1711 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1712 about_list
[t
->xtp_meaning
].name
);
1719 get_title(struct tab
*t
, bool window
)
1721 const gchar
*set
= NULL
, *title
= NULL
;
1722 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1724 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1725 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1728 title
= webkit_web_view_get_title(t
->wv
);
1729 if ((set
= title
? title
: get_uri(t
)))
1733 set
= window
? XT_NAME
: "(untitled)";
1739 add_alias(struct settings
*s
, char *line
)
1742 struct alias
*a
= NULL
;
1744 if (s
== NULL
|| line
== NULL
) {
1745 show_oops(NULL
, "add_alias invalid parameters");
1750 a
= g_malloc(sizeof(*a
));
1752 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1753 show_oops(NULL
, "add_alias: incomplete alias definition");
1756 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1757 show_oops(NULL
, "add_alias: invalid alias definition");
1761 a
->a_name
= g_strdup(alias
);
1762 a
->a_uri
= g_strdup(l
);
1764 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1766 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1776 add_mime_type(struct settings
*s
, char *line
)
1780 struct mime_type
*m
= NULL
;
1781 int downloadfirst
= 0;
1783 /* XXX this could be smarter */
1785 if (line
== NULL
|| strlen(line
) == 0) {
1786 show_oops(NULL
, "add_mime_type invalid parameters");
1795 m
= g_malloc(sizeof(*m
));
1797 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1798 show_oops(NULL
, "add_mime_type: invalid mime_type");
1801 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1802 mime_type
[strlen(mime_type
) - 1] = '\0';
1807 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1808 show_oops(NULL
, "add_mime_type: invalid mime_type");
1812 m
->mt_type
= g_strdup(mime_type
);
1813 m
->mt_action
= g_strdup(l
);
1814 m
->mt_download
= downloadfirst
;
1816 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1817 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1819 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1829 find_mime_type(char *mime_type
)
1831 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1833 TAILQ_FOREACH(m
, &mtl
, entry
) {
1834 if (m
->mt_default
&&
1835 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1838 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1851 walk_mime_type(struct settings
*s
,
1852 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1854 struct mime_type
*m
;
1857 if (s
== NULL
|| cb
== NULL
) {
1858 show_oops(NULL
, "walk_mime_type invalid parameters");
1862 TAILQ_FOREACH(m
, &mtl
, entry
) {
1863 str
= g_strdup_printf("%s%s --> %s",
1865 m
->mt_default
? "*" : "",
1867 cb(s
, str
, cb_args
);
1873 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1879 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1882 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1884 /* treat *.moo.com the same as .moo.com */
1885 if (str
[0] == '*' && str
[1] == '.')
1887 else if (str
[0] == '.')
1892 /* slice off port number */
1893 p
= g_strrstr(str
, ":");
1897 d
= g_malloc(sizeof *d
);
1899 d
->d
= g_strdup_printf(".%s", str
);
1901 d
->d
= g_strdup(str
);
1904 if (RB_INSERT(domain_list
, wl
, d
))
1907 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1918 add_cookie_wl(struct settings
*s
, char *entry
)
1920 wl_add(entry
, &c_wl
, 1);
1925 walk_cookie_wl(struct settings
*s
,
1926 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1930 if (s
== NULL
|| cb
== NULL
) {
1931 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1935 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1936 cb(s
, d
->d
, cb_args
);
1940 walk_js_wl(struct settings
*s
,
1941 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1945 if (s
== NULL
|| cb
== NULL
) {
1946 show_oops(NULL
, "walk_js_wl invalid parameters");
1950 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1951 cb(s
, d
->d
, cb_args
);
1955 add_js_wl(struct settings
*s
, char *entry
)
1957 wl_add(entry
, &js_wl
, 1 /* persistent */);
1962 wl_find(const gchar
*search
, struct domain_list
*wl
)
1965 struct domain
*d
= NULL
, dfind
;
1968 if (search
== NULL
|| wl
== NULL
)
1970 if (strlen(search
) < 2)
1973 if (search
[0] != '.')
1974 s
= g_strdup_printf(".%s", search
);
1976 s
= g_strdup(search
);
1978 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1981 d
= RB_FIND(domain_list
, wl
, &dfind
);
1995 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2001 if (s
== NULL
|| wl
== NULL
)
2004 if (!strncmp(s
, "http://", strlen("http://")))
2005 s
= &s
[strlen("http://")];
2006 else if (!strncmp(s
, "https://", strlen("https://")))
2007 s
= &s
[strlen("https://")];
2012 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2013 /* chop string at first slash */
2014 if (s
[i
] == '/' || s
[i
] == '\0') {
2017 r
= wl_find(ss
, wl
);
2026 settings_add(char *var
, char *val
)
2033 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2034 if (strcmp(var
, rs
[i
].name
))
2038 if (rs
[i
].s
->set(&rs
[i
], val
))
2039 errx(1, "invalid value for %s: %s", var
, val
);
2043 switch (rs
[i
].type
) {
2052 errx(1, "invalid sval for %s",
2066 errx(1, "invalid type for %s", var
);
2075 config_parse(char *filename
, int runtime
)
2078 char *line
, *cp
, *var
, *val
;
2079 size_t len
, lineno
= 0;
2081 char file
[PATH_MAX
];
2084 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2086 if (filename
== NULL
)
2089 if (runtime
&& runtime_settings
[0] != '\0') {
2090 snprintf(file
, sizeof file
, "%s/%s",
2091 work_dir
, runtime_settings
);
2092 if (stat(file
, &sb
)) {
2093 warnx("runtime file doesn't exist, creating it");
2094 if ((f
= fopen(file
, "w")) == NULL
)
2096 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2100 strlcpy(file
, filename
, sizeof file
);
2102 if ((config
= fopen(file
, "r")) == NULL
) {
2103 warn("config_parse: cannot open %s", filename
);
2108 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2109 if (feof(config
) || ferror(config
))
2113 cp
+= (long)strspn(cp
, WS
);
2114 if (cp
[0] == '\0') {
2120 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2121 startpage_add("invalid configuration file entry: %s",
2124 cp
+= (long)strspn(cp
, WS
);
2126 if ((val
= strsep(&cp
, "\0")) == NULL
)
2129 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2130 handled
= settings_add(var
, val
);
2132 startpage_add("invalid configuration file entry: %s=%s",
2142 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2148 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2152 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2155 JSStringGetUTF8CString(jsref
, s
, l
);
2156 JSStringRelease(jsref
);
2162 disable_hints(struct tab
*t
)
2164 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2165 bzero(t
->hint_num
, sizeof t
->hint_num
);
2166 run_script(t
, "vimprobable_clear()");
2168 t
->hint_mode
= XT_HINT_NONE
;
2172 enable_hints(struct tab
*t
)
2174 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2175 run_script(t
, "vimprobable_show_hints()");
2177 t
->hint_mode
= XT_HINT_NONE
;
2180 #define XT_JS_OPEN ("open;")
2181 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2182 #define XT_JS_FIRE ("fire;")
2183 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2184 #define XT_JS_FOUND ("found;")
2185 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2188 run_script(struct tab
*t
, char *s
)
2190 JSGlobalContextRef ctx
;
2191 WebKitWebFrame
*frame
;
2193 JSValueRef val
, exception
;
2196 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2197 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2199 frame
= webkit_web_view_get_main_frame(t
->wv
);
2200 ctx
= webkit_web_frame_get_global_context(frame
);
2202 str
= JSStringCreateWithUTF8CString(s
);
2203 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2204 NULL
, 0, &exception
);
2205 JSStringRelease(str
);
2207 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2209 es
= js_ref_to_string(ctx
, exception
);
2210 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2214 es
= js_ref_to_string(ctx
, val
);
2215 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2217 /* handle return value right here */
2218 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2221 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2224 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2225 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2226 &es
[XT_JS_FIRE_LEN
]);
2231 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2232 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2243 hint(struct tab
*t
, struct karg
*args
)
2246 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2248 if (t
->hints_on
== 0)
2257 apply_style(struct tab
*t
)
2259 g_object_set(G_OBJECT(t
->settings
),
2260 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2264 userstyle(struct tab
*t
, struct karg
*args
)
2266 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2270 g_object_set(G_OBJECT(t
->settings
),
2271 "user-stylesheet-uri", NULL
, (char *)NULL
);
2280 * Doesn't work fully, due to the following bug:
2281 * https://bugs.webkit.org/show_bug.cgi?id=51747
2284 restore_global_history(void)
2286 char file
[PATH_MAX
];
2291 const char delim
[3] = {'\\', '\\', '\0'};
2293 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2295 if ((f
= fopen(file
, "r")) == NULL
) {
2296 warnx("%s: fopen", __func__
);
2301 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2302 if (feof(f
) || ferror(f
))
2305 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2306 if (feof(f
) || ferror(f
)) {
2308 warnx("%s: broken history file\n", __func__
);
2312 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2313 webkit_web_history_item_new_with_data(uri
, title
);
2314 h
= g_malloc(sizeof(struct history
));
2315 h
->uri
= g_strdup(uri
);
2316 h
->title
= g_strdup(title
);
2317 RB_INSERT(history_list
, &hl
, h
);
2318 completion_add_uri(h
->uri
);
2320 warnx("%s: failed to restore history\n", __func__
);
2336 save_global_history_to_disk(struct tab
*t
)
2338 char file
[PATH_MAX
];
2342 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2344 if ((f
= fopen(file
, "w")) == NULL
) {
2345 show_oops(t
, "%s: global history file: %s",
2346 __func__
, strerror(errno
));
2350 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2351 if (h
->uri
&& h
->title
)
2352 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2361 quit(struct tab
*t
, struct karg
*args
)
2363 if (save_global_history
)
2364 save_global_history_to_disk(t
);
2372 open_tabs(struct tab
*t
, struct karg
*a
)
2374 char file
[PATH_MAX
];
2378 struct tab
*ti
, *tt
;
2383 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2384 if ((f
= fopen(file
, "r")) == NULL
)
2387 ti
= TAILQ_LAST(&tabs
, tab_list
);
2390 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2391 if (feof(f
) || ferror(f
))
2394 /* retrieve session name */
2395 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2396 strlcpy(named_session
,
2397 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2398 sizeof named_session
);
2402 if (uri
&& strlen(uri
))
2403 create_new_tab(uri
, NULL
, 1, -1);
2409 /* close open tabs */
2410 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2412 tt
= TAILQ_FIRST(&tabs
);
2432 restore_saved_tabs(void)
2434 char file
[PATH_MAX
];
2435 int unlink_file
= 0;
2440 snprintf(file
, sizeof file
, "%s/%s",
2441 sessions_dir
, XT_RESTART_TABS_FILE
);
2442 if (stat(file
, &sb
) == -1)
2443 a
.s
= XT_SAVED_TABS_FILE
;
2446 a
.s
= XT_RESTART_TABS_FILE
;
2449 a
.i
= XT_SES_DONOTHING
;
2450 rv
= open_tabs(NULL
, &a
);
2459 save_tabs(struct tab
*t
, struct karg
*a
)
2461 char file
[PATH_MAX
];
2463 int num_tabs
= 0, i
;
2464 struct tab
**stabs
= NULL
;
2469 snprintf(file
, sizeof file
, "%s/%s",
2470 sessions_dir
, named_session
);
2472 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2474 if ((f
= fopen(file
, "w")) == NULL
) {
2475 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2479 /* save session name */
2480 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2482 /* Save tabs, in the order they are arranged in the notebook. */
2483 num_tabs
= sort_tabs_by_page_num(&stabs
);
2485 for (i
= 0; i
< num_tabs
; i
++)
2487 if (get_uri(stabs
[i
]) != NULL
)
2488 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2489 else if (gtk_entry_get_text(GTK_ENTRY(
2490 stabs
[i
]->uri_entry
)))
2491 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2492 stabs
[i
]->uri_entry
)));
2497 /* try and make sure this gets to disk NOW. XXX Backup first? */
2498 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2499 show_oops(t
, "May not have managed to save session: %s",
2509 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2521 run_page_script(struct tab
*t
, struct karg
*args
)
2524 char *tmp
, script
[PATH_MAX
];
2526 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2527 if (tmp
[0] == '\0') {
2528 show_oops(t
, "no script specified");
2532 if ((uri
= get_uri(t
)) == NULL
) {
2533 show_oops(t
, "tab is empty, not running script");
2538 snprintf(script
, sizeof script
, "%s/%s",
2539 pwd
->pw_dir
, &tmp
[1]);
2541 strlcpy(script
, tmp
, sizeof script
);
2545 show_oops(t
, "can't fork to run script");
2555 execlp(script
, script
, uri
, (void *)NULL
);
2565 yank_uri(struct tab
*t
, struct karg
*args
)
2568 GtkClipboard
*clipboard
;
2570 if ((uri
= get_uri(t
)) == NULL
)
2573 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2574 gtk_clipboard_set_text(clipboard
, uri
, -1);
2580 paste_uri(struct tab
*t
, struct karg
*args
)
2582 GtkClipboard
*clipboard
;
2583 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2585 gchar
*p
= NULL
, *uri
;
2587 /* try primary clipboard first */
2588 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2589 p
= gtk_clipboard_wait_for_text(clipboard
);
2591 /* if it failed get whatever text is in cut_buffer0 */
2592 if (p
== NULL
&& xterm_workaround
)
2593 if (gdk_property_get(gdk_get_default_root_window(),
2595 gdk_atom_intern("STRING", FALSE
),
2597 1024 * 1024 /* picked out of my butt */,
2603 /* yes sir, we need to NUL the string */
2609 while (*uri
&& isspace(*uri
))
2611 if (strlen(uri
) == 0) {
2612 show_oops(t
, "empty paste buffer");
2615 if (guess_search
== 0 && valid_url_type(uri
)) {
2616 /* we can be clever and paste this in search box */
2617 show_oops(t
, "not a valid URL");
2621 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2623 else if (args
->i
== XT_PASTE_NEW_TAB
)
2624 create_new_tab(uri
, NULL
, 1, -1);
2635 find_domain(const gchar
*s
, int toplevel
)
2643 uri
= soup_uri_new(s
);
2645 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2649 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2650 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2651 while(--p
>= uri
->host
&& *p
!= '.');
2658 if (uri
->port
== 80)
2659 ret
= g_strdup_printf(".%s", p
);
2661 ret
= g_strdup_printf(".%s:%d", p
, uri
->port
);
2669 toggle_cwl(struct tab
*t
, struct karg
*args
)
2680 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2682 if (uri
== NULL
|| dom
== NULL
||
2683 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2684 show_oops(t
, "Can't toggle domain in cookie white list");
2687 d
= wl_find(dom
, &c_wl
);
2694 if (args
->i
& XT_WL_TOGGLE
)
2696 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2698 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2702 /* enable cookies for domain */
2703 wl_add(dom
, &c_wl
, 0);
2705 /* disable cookies for domain */
2706 RB_REMOVE(domain_list
, &c_wl
, d
);
2708 if (args
->i
& XT_WL_RELOAD
)
2709 webkit_web_view_reload(t
->wv
);
2717 toggle_js(struct tab
*t
, struct karg
*args
)
2727 g_object_get(G_OBJECT(t
->settings
),
2728 "enable-scripts", &es
, (char *)NULL
);
2729 if (args
->i
& XT_WL_TOGGLE
)
2731 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2733 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2739 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2741 if (uri
== NULL
|| dom
== NULL
||
2742 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2743 show_oops(t
, "Can't toggle domain in JavaScript white list");
2748 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2749 wl_add(dom
, &js_wl
, 0 /* session */);
2751 d
= wl_find(dom
, &js_wl
);
2753 RB_REMOVE(domain_list
, &js_wl
, d
);
2754 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2756 g_object_set(G_OBJECT(t
->settings
),
2757 "enable-scripts", es
, (char *)NULL
);
2758 g_object_set(G_OBJECT(t
->settings
),
2759 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2760 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2762 if (args
->i
& XT_WL_RELOAD
)
2763 webkit_web_view_reload(t
->wv
);
2771 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2775 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2778 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2783 toggle_src(struct tab
*t
, struct karg
*args
)
2790 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2791 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2792 webkit_web_view_reload(t
->wv
);
2798 focus_webview(struct tab
*t
)
2803 /* only grab focus if we are visible */
2804 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2805 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2809 focus(struct tab
*t
, struct karg
*args
)
2811 if (t
== NULL
|| args
== NULL
)
2817 if (args
->i
== XT_FOCUS_URI
)
2818 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2819 else if (args
->i
== XT_FOCUS_SEARCH
)
2820 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2826 stats(struct tab
*t
, struct karg
*args
)
2828 char *page
, *body
, *s
, line
[64 * 1024];
2829 uint64_t line_count
= 0;
2833 show_oops(NULL
, "stats invalid parameters");
2836 if (save_rejected_cookies
) {
2837 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2839 s
= fgets(line
, sizeof line
, r_cookie_f
);
2840 if (s
== NULL
|| feof(r_cookie_f
) ||
2846 snprintf(line
, sizeof line
,
2847 "<br/>Cookies blocked(*) total: %llu", line_count
);
2849 show_oops(t
, "Can't open blocked cookies file: %s",
2853 body
= g_strdup_printf(
2854 "Cookies blocked(*) this session: %llu"
2856 "<p><small><b>*</b> results vary based on settings</small></p>",
2860 page
= get_html_page("Statistics", body
, "", 0);
2863 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2870 marco(struct tab
*t
, struct karg
*args
)
2872 char *page
, line
[64 * 1024];
2876 show_oops(NULL
, "marco invalid parameters");
2879 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2881 page
= get_html_page("Marco Sez...", line
, "", 0);
2883 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2890 blank(struct tab
*t
, struct karg
*args
)
2893 show_oops(NULL
, "blank invalid parameters");
2895 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2901 about(struct tab
*t
, struct karg
*args
)
2906 show_oops(NULL
, "about invalid parameters");
2908 body
= g_strdup_printf("<b>Version: %s</b><p>"
2911 "<li>Marco Peereboom <marco@peereboom.us></li>"
2912 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2913 "<li>Edd Barrett <vext01@gmail.com> </li>"
2914 "<li>Todd T. Fries <todd@fries.net> </li>"
2915 "<li>Raphael Graf <r@undefined.ch> </li>"
2917 "Copyrights and licenses can be found on the XXXTerm "
2918 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2922 page
= get_html_page("About", body
, "", 0);
2925 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2932 help(struct tab
*t
, struct karg
*args
)
2934 char *page
, *head
, *body
;
2937 show_oops(NULL
, "help invalid parameters");
2939 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2940 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2942 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
2943 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2944 "cgi-bin/man-cgi?xxxterm</a>";
2946 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
2948 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2955 startpage(struct tab
*t
, struct karg
*args
)
2957 char *page
, *body
, *b
;
2961 show_oops(NULL
, "startpage invalid parameters");
2963 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
2965 TAILQ_FOREACH(s
, &spl
, entry
) {
2967 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
2971 page
= get_html_page("Startup Exception", body
, "", 0);
2974 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
2981 startpage_add(const char *fmt
, ...)
2991 if (vasprintf(&msg
, fmt
, ap
) == -1)
2992 errx(1, "startpage_add failed");
2995 s
= g_malloc0(sizeof *s
);
2998 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3002 * update all favorite tabs apart from one. Pass NULL if
3003 * you want to update all.
3006 update_favorite_tabs(struct tab
*apart_from
)
3009 if (!updating_fl_tabs
) {
3010 updating_fl_tabs
= 1; /* stop infinite recursion */
3011 TAILQ_FOREACH(t
, &tabs
, entry
)
3012 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3013 && (t
!= apart_from
))
3014 xtp_page_fl(t
, NULL
);
3015 updating_fl_tabs
= 0;
3019 /* show a list of favorites (bookmarks) */
3021 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3023 char file
[PATH_MAX
];
3025 char *uri
= NULL
, *title
= NULL
;
3026 size_t len
, lineno
= 0;
3028 char *body
, *tmp
, *page
= NULL
;
3029 const char delim
[3] = {'\\', '\\', '\0'};
3031 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3034 warn("%s: bad param", __func__
);
3036 /* new session key */
3037 if (!updating_fl_tabs
)
3038 generate_xtp_session_key(&fl_session_key
);
3040 /* open favorites */
3041 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3042 if ((f
= fopen(file
, "r")) == NULL
) {
3043 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3048 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3049 "<th style='width: 40px'>#</th><th>Link</th>"
3050 "<th style='width: 40px'>Rm</th></tr>\n");
3053 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3054 if (feof(f
) || ferror(f
))
3056 if (strlen(title
) == 0 || title
[0] == '#') {
3062 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3063 if (feof(f
) || ferror(f
)) {
3064 show_oops(t
, "favorites file corrupt");
3070 body
= g_strdup_printf("%s<tr>"
3072 "<td><a href='%s'>%s</a></td>"
3073 "<td style='text-align: center'>"
3074 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3076 body
, i
, uri
, title
,
3077 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3089 /* if none, say so */
3092 body
= g_strdup_printf("%s<tr>"
3093 "<td colspan='3' style='text-align: center'>"
3094 "No favorites - To add one use the 'favadd' command."
3095 "</td></tr>", body
);
3100 body
= g_strdup_printf("%s</table>", body
);
3110 page
= get_html_page("Favorites", body
, "", 1);
3111 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3115 update_favorite_tabs(t
);
3124 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3125 size_t cert_count
, char *title
)
3127 gnutls_datum_t cinfo
;
3131 body
= g_strdup("");
3133 for (i
= 0; i
< cert_count
; i
++) {
3134 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3139 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3140 body
, i
, cinfo
.data
);
3141 gnutls_free(cinfo
.data
);
3145 tmp
= get_html_page(title
, body
, "", 0);
3148 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3153 ca_cmd(struct tab
*t
, struct karg
*args
)
3156 int rv
= 1, certs
= 0, certs_read
;
3159 gnutls_x509_crt_t
*c
= NULL
;
3160 char *certs_buf
= NULL
, *s
;
3162 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3163 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3167 if (fstat(fileno(f
), &sb
) == -1) {
3168 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3172 certs_buf
= g_malloc(sb
.st_size
+ 1);
3173 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3174 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3177 certs_buf
[sb
.st_size
] = '\0';
3180 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3182 s
+= strlen("BEGIN CERTIFICATE");
3185 bzero(&dt
, sizeof dt
);
3186 dt
.data
= (unsigned char *)certs_buf
;
3187 dt
.size
= sb
.st_size
;
3188 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3189 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3190 GNUTLS_X509_FMT_PEM
, 0);
3191 if (certs_read
<= 0) {
3192 show_oops(t
, "No cert(s) available");
3195 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3208 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3212 struct addrinfo hints
, *res
= NULL
, *ai
;
3213 int rv
= -1, s
= -1, on
, error
;
3216 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3217 show_oops(t
, "invalid URI");
3221 su
= soup_uri_new(uri
);
3223 show_oops(t
, "invalid soup URI");
3226 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3227 show_oops(t
, "invalid HTTPS URI");
3231 snprintf(port
, sizeof port
, "%d", su
->port
);
3232 bzero(&hints
, sizeof(struct addrinfo
));
3233 hints
.ai_flags
= AI_CANONNAME
;
3234 hints
.ai_family
= AF_UNSPEC
;
3235 hints
.ai_socktype
= SOCK_STREAM
;
3237 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3238 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3242 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3243 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3246 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3248 show_oops(t
, "socket failed: %s", strerror(errno
));
3251 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3252 sizeof(on
)) == -1) {
3253 show_oops(t
, "setsockopt failed: %s", strerror(errno
));
3256 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == -1) {
3257 show_oops(t
, "connect failed: %s", strerror(errno
));
3265 strlcpy(domain
, su
->host
, domain_sz
);
3272 if (rv
== -1 && s
!= -1)
3279 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3282 gnutls_deinit(gsession
);
3284 gnutls_certificate_free_credentials(xcred
);
3290 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3291 gnutls_certificate_credentials_t
*xc
)
3293 gnutls_certificate_credentials_t xcred
;
3294 gnutls_session_t gsession
;
3297 if (gs
== NULL
|| xc
== NULL
)
3303 gnutls_certificate_allocate_credentials(&xcred
);
3304 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3305 GNUTLS_X509_FMT_PEM
);
3307 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3308 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3309 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3310 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3311 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3312 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3314 gnutls_error_is_fatal(rv
),
3315 gnutls_strerror_name(rv
));
3316 stop_tls(gsession
, xcred
);
3320 gnutls_credentials_type_t cred
;
3321 cred
= gnutls_auth_get_type(gsession
);
3322 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3323 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3324 stop_tls(gsession
, xcred
);
3336 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3340 const gnutls_datum_t
*cl
;
3341 gnutls_x509_crt_t
*all_certs
;
3344 if (certs
== NULL
|| cert_count
== NULL
)
3346 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3348 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3352 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3353 for (i
= 0; i
< len
; i
++) {
3354 gnutls_x509_crt_init(&all_certs
[i
]);
3355 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3356 GNUTLS_X509_FMT_PEM
< 0)) {
3370 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3374 for (i
= 0; i
< cert_count
; i
++)
3375 gnutls_x509_crt_deinit(certs
[i
]);
3380 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3382 GdkColor c_text
, c_base
;
3384 gdk_color_parse(text
, &c_text
);
3385 gdk_color_parse(base
, &c_base
);
3387 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3388 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3389 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3390 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3392 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3393 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3394 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3395 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3399 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3400 size_t cert_count
, char *domain
)
3403 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3408 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3411 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3412 if ((f
= fopen(file
, "w")) == NULL
) {
3413 show_oops(t
, "Can't create cert file %s %s",
3414 file
, strerror(errno
));
3418 for (i
= 0; i
< cert_count
; i
++) {
3419 cert_buf_sz
= sizeof cert_buf
;
3420 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3421 cert_buf
, &cert_buf_sz
)) {
3422 show_oops(t
, "gnutls_x509_crt_export failed");
3425 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3426 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3431 /* not the best spot but oh well */
3432 gdk_color_parse("lightblue", &color
);
3433 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3434 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3447 load_compare_cert(struct tab
*t
, struct karg
*args
)
3450 char domain
[8182], file
[PATH_MAX
];
3451 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3452 int s
= -1, i
, error
;
3454 size_t cert_buf_sz
, cert_count
;
3455 enum cert_trust rv
= CERT_UNTRUSTED
;
3457 gnutls_session_t gsession
;
3458 gnutls_x509_crt_t
*certs
;
3459 gnutls_certificate_credentials_t xcred
;
3461 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3466 if ((uri
= get_uri(t
)) == NULL
)
3468 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3470 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3472 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3475 if (start_tls(t
, s
, &gsession
, &xcred
))
3477 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3479 /* verify certs in case cert file doesn't exist */
3480 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3482 show_oops(t
, "Invalid certificates");
3487 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3488 show_oops(t
, "Can't get connection certificates");
3492 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3493 if ((f
= fopen(file
, "r")) == NULL
) {
3499 for (i
= 0; i
< cert_count
; i
++) {
3500 cert_buf_sz
= sizeof cert_buf
;
3501 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3502 cert_buf
, &cert_buf_sz
)) {
3505 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3506 rv
= CERT_BAD
; /* critical */
3509 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3510 rv
= CERT_BAD
; /* critical */
3519 free_connection_certs(certs
, cert_count
);
3521 /* we close the socket first for speed */
3525 /* only complain if we didn't save it locally */
3526 if (error
&& rv
!= CERT_LOCAL
) {
3527 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3528 if (error
& GNUTLS_CERT_INVALID
)
3529 strlcat(serr
, "invalid, ", sizeof serr
);
3530 if (error
& GNUTLS_CERT_REVOKED
)
3531 strlcat(serr
, "revoked, ", sizeof serr
);
3532 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3533 strlcat(serr
, "signer not found, ", sizeof serr
);
3534 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3535 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3536 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3537 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3538 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3539 strlcat(serr
, "not activated, ", sizeof serr
);
3540 if (error
& GNUTLS_CERT_EXPIRED
)
3541 strlcat(serr
, "expired, ", sizeof serr
);
3542 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3543 if (serr
[i
] == ',') {
3550 stop_tls(gsession
, xcred
);
3556 cert_cmd(struct tab
*t
, struct karg
*args
)
3562 gnutls_session_t gsession
;
3563 gnutls_x509_crt_t
*certs
;
3564 gnutls_certificate_credentials_t xcred
;
3569 if (ssl_ca_file
== NULL
) {
3570 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3574 if ((uri
= get_uri(t
)) == NULL
) {
3575 show_oops(t
, "Invalid URI");
3579 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3580 show_oops(t
, "Invalid certificate URI: %s", uri
);
3585 if (start_tls(t
, s
, &gsession
, &xcred
))
3589 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3590 show_oops(t
, "get_connection_certs failed");
3594 if (args
->i
& XT_SHOW
)
3595 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3596 else if (args
->i
& XT_SAVE
)
3597 save_certs(t
, certs
, cert_count
, domain
);
3599 free_connection_certs(certs
, cert_count
);
3601 /* we close the socket first for speed */
3604 stop_tls(gsession
, xcred
);
3610 remove_cookie(int index
)
3616 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3618 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3620 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3624 print_cookie("remove cookie", c
);
3625 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3630 soup_cookies_free(cf
);
3636 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3641 body
= g_strdup("");
3644 if (args
->i
& XT_WL_PERSISTENT
) {
3646 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3648 RB_FOREACH(d
, domain_list
, wl
) {
3652 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3658 if (args
->i
& XT_WL_SESSION
) {
3660 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3662 RB_FOREACH(d
, domain_list
, wl
) {
3666 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3671 tmp
= get_html_page(title
, body
, "", 0);
3674 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3676 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3682 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3684 char file
[PATH_MAX
];
3686 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3695 if (t
== NULL
|| args
== NULL
)
3698 if (runtime_settings
[0] == '\0')
3701 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3702 if ((f
= fopen(file
, "r+")) == NULL
)
3706 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3707 if (uri
== NULL
|| dom
== NULL
||
3708 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3709 show_oops(t
, "Can't add domain to %s white list",
3710 js
? "JavaScript" : "cookie");
3714 /* we don't want to save :port number */
3715 p
= g_strrstr(dom
, ":");
3719 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3722 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3725 if (!strcmp(line
, lt
))
3731 fprintf(f
, "%s\n", lt
);
3736 d
= wl_find(dom
, &js_wl
);
3738 settings_add("js_wl", dom
);
3739 d
= wl_find(dom
, &js_wl
);
3743 d
= wl_find(dom
, &c_wl
);
3745 settings_add("cookie_wl", dom
);
3746 d
= wl_find(dom
, &c_wl
);
3750 /* find and add to persistent jar */
3751 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3752 for (;cf
; cf
= cf
->next
) {
3754 if (!strcmp(dom
, ci
->domain
) ||
3755 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3756 c
= soup_cookie_copy(ci
);
3757 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3760 soup_cookies_free(cf
);
3778 js_show_wl(struct tab
*t
, struct karg
*args
)
3780 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3781 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3787 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3789 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3790 wl_show(t
, args
, "Cookie White List", &c_wl
);
3796 cookie_cmd(struct tab
*t
, struct karg
*args
)
3798 if (args
->i
& XT_SHOW
)
3799 wl_show(t
, args
, "Cookie White List", &c_wl
);
3800 else if (args
->i
& XT_WL_TOGGLE
) {
3801 args
->i
|= XT_WL_RELOAD
;
3802 toggle_cwl(t
, args
);
3803 } else if (args
->i
& XT_SAVE
) {
3804 args
->i
|= XT_WL_RELOAD
;
3805 wl_save(t
, args
, 0);
3806 } else if (args
->i
& XT_DELETE
)
3807 show_oops(t
, "'cookie delete' currently unimplemented");
3813 js_cmd(struct tab
*t
, struct karg
*args
)
3815 if (args
->i
& XT_SHOW
)
3816 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3817 else if (args
->i
& XT_SAVE
) {
3818 args
->i
|= XT_WL_RELOAD
;
3819 wl_save(t
, args
, 1);
3820 } else if (args
->i
& XT_WL_TOGGLE
) {
3821 args
->i
|= XT_WL_RELOAD
;
3823 } else if (args
->i
& XT_DELETE
)
3824 show_oops(t
, "'js delete' currently unimplemented");
3830 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3832 js_toggle_cb(t
->js_toggle
, t
);
3838 add_favorite(struct tab
*t
, struct karg
*args
)
3840 char file
[PATH_MAX
];
3843 size_t urilen
, linelen
;
3844 const gchar
*uri
, *title
;
3849 /* don't allow adding of xtp pages to favorites */
3850 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3851 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3855 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3856 if ((f
= fopen(file
, "r+")) == NULL
) {
3857 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3861 title
= get_title(t
, FALSE
);
3864 if (title
== NULL
|| uri
== NULL
) {
3865 show_oops(t
, "can't add page to favorites");
3869 urilen
= strlen(uri
);
3872 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3873 if (feof(f
) || ferror(f
))
3876 if (linelen
== urilen
&& !strcmp(line
, uri
))
3883 fprintf(f
, "\n%s\n%s", title
, uri
);
3889 update_favorite_tabs(NULL
);
3895 navaction(struct tab
*t
, struct karg
*args
)
3897 WebKitWebHistoryItem
*item
;
3899 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3900 t
->tab_id
, args
->i
);
3902 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3905 if (args
->i
== XT_NAV_BACK
)
3906 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3908 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3910 return (XT_CB_PASSTHROUGH
);
3911 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3913 return (XT_CB_PASSTHROUGH
);
3919 item
= webkit_web_back_forward_list_get_back_item(t
->bfl
);
3921 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3923 case XT_NAV_FORWARD
:
3925 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3927 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3930 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3932 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3935 return (XT_CB_PASSTHROUGH
);
3939 move(struct tab
*t
, struct karg
*args
)
3941 GtkAdjustment
*adjust
;
3942 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3948 case XT_MOVE_BOTTOM
:
3950 case XT_MOVE_PAGEDOWN
:
3951 case XT_MOVE_PAGEUP
:
3952 case XT_MOVE_HALFDOWN
:
3953 case XT_MOVE_HALFUP
:
3954 case XT_MOVE_PERCENT
:
3955 adjust
= t
->adjust_v
;
3958 adjust
= t
->adjust_h
;
3962 pos
= gtk_adjustment_get_value(adjust
);
3963 ps
= gtk_adjustment_get_page_size(adjust
);
3964 upper
= gtk_adjustment_get_upper(adjust
);
3965 lower
= gtk_adjustment_get_lower(adjust
);
3966 si
= gtk_adjustment_get_step_increment(adjust
);
3967 pi
= gtk_adjustment_get_page_increment(adjust
);
3970 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3971 "max %f si %f pi %f\n",
3972 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3973 pos
, ps
, upper
, lower
, max
, si
, pi
);
3979 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3984 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3986 case XT_MOVE_BOTTOM
:
3987 case XT_MOVE_FARRIGHT
:
3988 gtk_adjustment_set_value(adjust
, max
);
3991 case XT_MOVE_FARLEFT
:
3992 gtk_adjustment_set_value(adjust
, lower
);
3994 case XT_MOVE_PAGEDOWN
:
3996 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3998 case XT_MOVE_PAGEUP
:
4000 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4002 case XT_MOVE_HALFDOWN
:
4004 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4006 case XT_MOVE_HALFUP
:
4008 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4010 case XT_MOVE_PERCENT
:
4011 percent
= atoi(args
->s
) / 100.0;
4012 pos
= max
* percent
;
4013 if (pos
< 0.0 || pos
> max
)
4015 gtk_adjustment_set_value(adjust
, pos
);
4018 return (XT_CB_PASSTHROUGH
);
4021 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4023 return (XT_CB_HANDLED
);
4027 url_set_visibility(void)
4031 TAILQ_FOREACH(t
, &tabs
, entry
)
4032 if (show_url
== 0) {
4033 gtk_widget_hide(t
->toolbar
);
4036 gtk_widget_show(t
->toolbar
);
4040 notebook_tab_set_visibility(void)
4042 if (show_tabs
== 0) {
4043 gtk_widget_hide(tab_bar
);
4044 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4046 if (tab_style
== XT_TABS_NORMAL
) {
4047 gtk_widget_hide(tab_bar
);
4048 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4049 } else if (tab_style
== XT_TABS_COMPACT
) {
4050 gtk_widget_show(tab_bar
);
4051 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4057 statusbar_set_visibility(void)
4061 TAILQ_FOREACH(t
, &tabs
, entry
)
4062 if (show_statusbar
== 0) {
4063 gtk_widget_hide(t
->statusbar_box
);
4066 gtk_widget_show(t
->statusbar_box
);
4070 url_set(struct tab
*t
, int enable_url_entry
)
4075 show_url
= enable_url_entry
;
4077 if (enable_url_entry
) {
4078 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4079 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4080 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4082 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4083 GTK_ENTRY_ICON_PRIMARY
);
4085 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4086 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4087 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4088 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4094 fullscreen(struct tab
*t
, struct karg
*args
)
4096 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4099 return (XT_CB_PASSTHROUGH
);
4101 if (show_url
== 0) {
4109 url_set_visibility();
4110 notebook_tab_set_visibility();
4112 return (XT_CB_HANDLED
);
4116 statustoggle(struct tab
*t
, struct karg
*args
)
4118 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4120 if (show_statusbar
== 1) {
4122 statusbar_set_visibility();
4123 } else if (show_statusbar
== 0) {
4125 statusbar_set_visibility();
4127 return (XT_CB_HANDLED
);
4131 urlaction(struct tab
*t
, struct karg
*args
)
4133 int rv
= XT_CB_HANDLED
;
4135 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4138 return (XT_CB_PASSTHROUGH
);
4142 if (show_url
== 0) {
4144 url_set_visibility();
4148 if (show_url
== 1) {
4150 url_set_visibility();
4158 tabaction(struct tab
*t
, struct karg
*args
)
4160 int rv
= XT_CB_HANDLED
;
4161 char *url
= args
->s
;
4165 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4168 return (XT_CB_PASSTHROUGH
);
4172 if (strlen(url
) > 0)
4173 create_new_tab(url
, NULL
, 1, args
->precount
);
4175 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4178 if (args
->precount
< 0)
4181 TAILQ_FOREACH(tt
, &tabs
, entry
)
4182 if (tt
->tab_id
== args
->precount
- 1) {
4187 case XT_TAB_DELQUIT
:
4188 if (gtk_notebook_get_n_pages(notebook
) > 1)
4194 if (strlen(url
) > 0)
4197 rv
= XT_CB_PASSTHROUGH
;
4203 if (show_tabs
== 0) {
4205 notebook_tab_set_visibility();
4209 if (show_tabs
== 1) {
4211 notebook_tab_set_visibility();
4214 case XT_TAB_NEXTSTYLE
:
4215 if (tab_style
== XT_TABS_NORMAL
) {
4216 tab_style
= XT_TABS_COMPACT
;
4217 recolor_compact_tabs();
4220 tab_style
= XT_TABS_NORMAL
;
4221 notebook_tab_set_visibility();
4223 case XT_TAB_UNDO_CLOSE
:
4224 if (undo_count
== 0) {
4225 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4230 u
= TAILQ_FIRST(&undos
);
4231 create_new_tab(u
->uri
, u
, 1, -1);
4233 TAILQ_REMOVE(&undos
, u
, entry
);
4235 /* u->history is freed in create_new_tab() */
4240 rv
= XT_CB_PASSTHROUGH
;
4254 resizetab(struct tab
*t
, struct karg
*args
)
4256 if (t
== NULL
|| args
== NULL
) {
4257 show_oops(NULL
, "resizetab invalid parameters");
4258 return (XT_CB_PASSTHROUGH
);
4261 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4262 t
->tab_id
, args
->i
);
4264 setzoom_webkit(t
, args
->i
);
4266 return (XT_CB_HANDLED
);
4270 movetab(struct tab
*t
, struct karg
*args
)
4274 if (t
== NULL
|| args
== NULL
) {
4275 show_oops(NULL
, "movetab invalid parameters");
4276 return (XT_CB_PASSTHROUGH
);
4279 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4280 t
->tab_id
, args
->i
);
4282 if (args
->i
>= XT_TAB_INVALID
)
4283 return (XT_CB_PASSTHROUGH
);
4285 if (TAILQ_EMPTY(&tabs
))
4286 return (XT_CB_PASSTHROUGH
);
4288 n
= gtk_notebook_get_n_pages(notebook
);
4289 dest
= gtk_notebook_get_current_page(notebook
);
4293 if (args
->precount
< 0)
4294 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4296 dest
= args
->precount
- 1;
4300 if (args
->precount
< 0)
4303 dest
-= args
->precount
% n
;
4316 return (XT_CB_PASSTHROUGH
);
4319 if (dest
< 0 || dest
>= n
)
4320 return (XT_CB_PASSTHROUGH
);
4321 if (t
->tab_id
== dest
) {
4322 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4323 return (XT_CB_HANDLED
);
4326 set_current_tab(dest
);
4328 return (XT_CB_HANDLED
);
4334 command(struct tab
*t
, struct karg
*args
)
4336 char *s
= NULL
, *ss
= NULL
;
4340 if (t
== NULL
|| args
== NULL
) {
4341 show_oops(NULL
, "command invalid parameters");
4342 return (XT_CB_PASSTHROUGH
);
4353 if (cmd_prefix
== 0)
4356 ss
= g_strdup_printf(":%d", cmd_prefix
);
4367 case XT_CMD_OPEN_CURRENT
:
4370 case XT_CMD_TABNEW_CURRENT
:
4371 if (!s
) /* FALL THROUGH? */
4373 if ((uri
= get_uri(t
)) != NULL
) {
4374 ss
= g_strdup_printf("%s%s", s
, uri
);
4379 show_oops(t
, "command: invalid opcode %d", args
->i
);
4380 return (XT_CB_PASSTHROUGH
);
4383 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4385 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4386 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4387 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4389 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4390 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4395 return (XT_CB_HANDLED
);
4399 * Return a new string with a download row (in html)
4400 * appended. Old string is freed.
4403 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4406 WebKitDownloadStatus stat
;
4407 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4409 char cur_sz
[FMT_SCALED_STRSIZE
];
4410 char tot_sz
[FMT_SCALED_STRSIZE
];
4413 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4415 /* All actions wil take this form:
4416 * xxxt://class/seskey
4418 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4419 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4421 stat
= webkit_download_get_status(dl
->download
);
4424 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4425 status_html
= g_strdup_printf("Finished");
4426 cmd_html
= g_strdup_printf(
4427 "<a href='%s%d/%d'>Remove</a>",
4428 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4430 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4431 /* gather size info */
4432 progress
= 100 * webkit_download_get_progress(dl
->download
);
4435 webkit_download_get_current_size(dl
->download
), cur_sz
);
4437 webkit_download_get_total_size(dl
->download
), tot_sz
);
4439 status_html
= g_strdup_printf(
4440 "<div style='width: 100%%' align='center'>"
4441 "<div class='progress-outer'>"
4442 "<div class='progress-inner' style='width: %.2f%%'>"
4443 "</div></div></div>"
4444 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4445 progress
, cur_sz
, tot_sz
, progress
);
4447 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4448 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4452 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4453 status_html
= g_strdup_printf("Cancelled");
4454 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4455 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4457 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4458 status_html
= g_strdup_printf("Error!");
4459 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4460 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4462 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4463 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4464 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4465 status_html
= g_strdup_printf("Starting");
4468 show_oops(t
, "%s: unknown download status", __func__
);
4471 new_html
= g_strdup_printf(
4472 "%s\n<tr><td>%s</td><td>%s</td>"
4473 "<td style='text-align:center'>%s</td></tr>\n",
4474 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4475 status_html
, cmd_html
);
4479 g_free(status_html
);
4490 * update all download tabs apart from one. Pass NULL if
4491 * you want to update all.
4494 update_download_tabs(struct tab
*apart_from
)
4497 if (!updating_dl_tabs
) {
4498 updating_dl_tabs
= 1; /* stop infinite recursion */
4499 TAILQ_FOREACH(t
, &tabs
, entry
)
4500 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4501 && (t
!= apart_from
))
4502 xtp_page_dl(t
, NULL
);
4503 updating_dl_tabs
= 0;
4508 * update all cookie tabs apart from one. Pass NULL if
4509 * you want to update all.
4512 update_cookie_tabs(struct tab
*apart_from
)
4515 if (!updating_cl_tabs
) {
4516 updating_cl_tabs
= 1; /* stop infinite recursion */
4517 TAILQ_FOREACH(t
, &tabs
, entry
)
4518 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4519 && (t
!= apart_from
))
4520 xtp_page_cl(t
, NULL
);
4521 updating_cl_tabs
= 0;
4526 * update all history tabs apart from one. Pass NULL if
4527 * you want to update all.
4530 update_history_tabs(struct tab
*apart_from
)
4534 if (!updating_hl_tabs
) {
4535 updating_hl_tabs
= 1; /* stop infinite recursion */
4536 TAILQ_FOREACH(t
, &tabs
, entry
)
4537 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4538 && (t
!= apart_from
))
4539 xtp_page_hl(t
, NULL
);
4540 updating_hl_tabs
= 0;
4544 /* cookie management XTP page */
4546 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4548 char *body
, *page
, *tmp
;
4549 int i
= 1; /* all ids start 1 */
4550 GSList
*sc
, *pc
, *pc_start
;
4552 char *type
, *table_headers
, *last_domain
;
4554 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4557 show_oops(NULL
, "%s invalid parameters", __func__
);
4561 /* Generate a new session key */
4562 if (!updating_cl_tabs
)
4563 generate_xtp_session_key(&cl_session_key
);
4566 table_headers
= g_strdup_printf("<table><tr>"
4569 "<th style='width:200px'>Value</th>"
4573 "<th>HTTP<br />only</th>"
4574 "<th style='width:40px'>Rm</th></tr>\n");
4576 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4577 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4581 last_domain
= strdup("");
4582 for (; sc
; sc
= sc
->next
) {
4585 if (strcmp(last_domain
, c
->domain
) != 0) {
4588 last_domain
= strdup(c
->domain
);
4592 body
= g_strdup_printf("%s</table>"
4594 body
, c
->domain
, table_headers
);
4598 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4599 c
->domain
, table_headers
);
4604 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4605 if (soup_cookie_equal(pc
->data
, c
)) {
4606 type
= "Session + Persistent";
4611 body
= g_strdup_printf(
4614 "<td style='word-wrap:normal'>%s</td>"
4616 " <textarea rows='4'>%s</textarea>"
4622 "<td style='text-align:center'>"
4623 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4630 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4645 soup_cookies_free(sc
);
4646 soup_cookies_free(pc
);
4648 /* small message if there are none */
4650 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4651 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4654 body
= g_strdup_printf("%s</table>", body
);
4657 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4659 g_free(table_headers
);
4660 g_free(last_domain
);
4662 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4663 update_cookie_tabs(t
);
4671 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4673 char *body
, *page
, *tmp
;
4675 int i
= 1; /* all ids start 1 */
4677 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4680 show_oops(NULL
, "%s invalid parameters", __func__
);
4684 /* Generate a new session key */
4685 if (!updating_hl_tabs
)
4686 generate_xtp_session_key(&hl_session_key
);
4689 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4690 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4692 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4694 body
= g_strdup_printf(
4696 "<td><a href='%s'>%s</a></td>"
4698 "<td style='text-align: center'>"
4699 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4700 body
, h
->uri
, h
->uri
, h
->title
,
4701 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4702 XT_XTP_HL_REMOVE
, i
);
4708 /* small message if there are none */
4711 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4712 "colspan='3'>No History</td></tr>\n", body
);
4717 body
= g_strdup_printf("%s</table>", body
);
4720 page
= get_html_page("History", body
, "", TRUE
);
4724 * update all history manager tabs as the xtp session
4725 * key has now changed. No need to update the current tab.
4726 * Already did that above.
4728 update_history_tabs(t
);
4730 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4737 * Generate a web page detailing the status of any downloads
4740 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4742 struct download
*dl
;
4743 char *body
, *page
, *tmp
;
4747 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4750 show_oops(NULL
, "%s invalid parameters", __func__
);
4755 * Generate a new session key for next page instance.
4756 * This only happens for the top level call to xtp_page_dl()
4757 * in which case updating_dl_tabs is 0.
4759 if (!updating_dl_tabs
)
4760 generate_xtp_session_key(&dl_session_key
);
4762 /* header - with refresh so as to update */
4763 if (refresh_interval
>= 1)
4764 ref
= g_strdup_printf(
4765 "<meta http-equiv='refresh' content='%u"
4766 ";url=%s%d/%s/%d' />\n",
4775 body
= g_strdup_printf("<div align='center'>"
4776 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4777 "</p><table><tr><th style='width: 60%%'>"
4778 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4779 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4781 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4782 body
= xtp_page_dl_row(t
, body
, dl
);
4786 /* message if no downloads in list */
4789 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4790 " style='text-align: center'>"
4791 "No downloads</td></tr>\n", body
);
4796 body
= g_strdup_printf("%s</table></div>", body
);
4799 page
= get_html_page("Downloads", body
, ref
, 1);
4804 * update all download manager tabs as the xtp session
4805 * key has now changed. No need to update the current tab.
4806 * Already did that above.
4808 update_download_tabs(t
);
4810 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4817 search(struct tab
*t
, struct karg
*args
)
4821 if (t
== NULL
|| args
== NULL
) {
4822 show_oops(NULL
, "search invalid parameters");
4825 if (t
->search_text
== NULL
) {
4826 if (global_search
== NULL
)
4827 return (XT_CB_PASSTHROUGH
);
4829 t
->search_text
= g_strdup(global_search
);
4830 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4831 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4835 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4836 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4839 case XT_SEARCH_NEXT
:
4840 d
= t
->search_forward
;
4842 case XT_SEARCH_PREV
:
4843 d
= !t
->search_forward
;
4846 return (XT_CB_PASSTHROUGH
);
4849 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4851 return (XT_CB_HANDLED
);
4854 struct settings_args
{
4860 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4863 struct settings_args
*sa
= cb_args
;
4868 if (s
->flags
& XT_SF_RUNTIME
)
4874 *sa
->body
= g_strdup_printf(
4876 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4877 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4889 set_show(struct tab
*t
, struct karg
*args
)
4891 char *body
, *page
, *tmp
;
4893 struct settings_args sa
;
4895 bzero(&sa
, sizeof sa
);
4899 body
= g_strdup_printf("<div align='center'><table><tr>"
4900 "<th align='left'>Setting</th>"
4901 "<th align='left'>Value</th></tr>\n");
4903 settings_walk(print_setting
, &sa
);
4906 /* small message if there are none */
4909 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4910 "colspan='2'>No settings</td></tr>\n", body
);
4915 body
= g_strdup_printf("%s</table></div>", body
);
4918 page
= get_html_page("Settings", body
, "", 0);
4922 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4926 return (XT_CB_PASSTHROUGH
);
4930 set(struct tab
*t
, struct karg
*args
)
4935 if (args
== NULL
|| args
->s
== NULL
)
4936 return (set_show(t
, args
));
4939 p
= g_strstrip(args
->s
);
4942 return (set_show(t
, args
));
4944 /* we got some sort of string */
4945 val
= g_strrstr(p
, "=");
4948 val
= g_strchomp(val
);
4951 for (i
= 0; i
< LENGTH(rs
); i
++) {
4952 if (strcmp(rs
[i
].name
, p
))
4955 if (rs
[i
].activate
) {
4956 if (rs
[i
].activate(val
))
4957 show_oops(t
, "%s invalid value %s",
4960 show_oops(t
, ":set %s = %s", p
, val
);
4963 show_oops(t
, "not a runtime option: %s", p
);
4967 show_oops(t
, "unknown option: %s", p
);
4971 for (i
= 0; i
< LENGTH(rs
); i
++) {
4972 if (strcmp(rs
[i
].name
, p
))
4975 /* XXX this could use some cleanup */
4976 switch (rs
[i
].type
) {
4979 show_oops(t
, "%s = %d",
4980 rs
[i
].name
, *rs
[i
].ival
);
4981 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4982 show_oops(t
, "%s = %s",
4984 rs
[i
].s
->get(&rs
[i
]));
4985 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4986 show_oops(t
, "%s = ...", rs
[i
].name
);
4988 show_oops(t
, "%s = ", rs
[i
].name
);
4992 show_oops(t
, "%s = %f",
4993 rs
[i
].name
, *rs
[i
].fval
);
4994 else if (rs
[i
].s
&& rs
[i
].s
->get
)
4995 show_oops(t
, "%s = %s",
4997 rs
[i
].s
->get(&rs
[i
]));
4998 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
4999 show_oops(t
, "%s = ...", rs
[i
].name
);
5001 show_oops(t
, "%s = ", rs
[i
].name
);
5004 if (rs
[i
].sval
&& *rs
[i
].sval
)
5005 show_oops(t
, "%s = %s",
5006 rs
[i
].name
, *rs
[i
].sval
);
5007 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5008 show_oops(t
, "%s = %s",
5010 rs
[i
].s
->get(&rs
[i
]));
5011 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5012 show_oops(t
, "%s = ...", rs
[i
].name
);
5014 show_oops(t
, "%s = ", rs
[i
].name
);
5017 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5023 show_oops(t
, "unknown option: %s", p
);
5026 return (XT_CB_PASSTHROUGH
);
5030 session_save(struct tab
*t
, char *filename
)
5035 if (strlen(filename
) == 0)
5038 if (filename
[0] == '.' || filename
[0] == '/')
5042 if (save_tabs(t
, &a
))
5044 strlcpy(named_session
, filename
, sizeof named_session
);
5052 session_open(struct tab
*t
, char *filename
)
5057 if (strlen(filename
) == 0)
5060 if (filename
[0] == '.' || filename
[0] == '/')
5064 a
.i
= XT_SES_CLOSETABS
;
5065 if (open_tabs(t
, &a
))
5068 strlcpy(named_session
, filename
, sizeof named_session
);
5076 session_delete(struct tab
*t
, char *filename
)
5078 char file
[PATH_MAX
];
5081 if (strlen(filename
) == 0)
5084 if (filename
[0] == '.' || filename
[0] == '/')
5087 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5091 if (!strcmp(filename
, named_session
))
5092 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5093 sizeof named_session
);
5101 session_cmd(struct tab
*t
, struct karg
*args
)
5103 char *filename
= args
->s
;
5108 if (args
->i
& XT_SHOW
)
5109 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5110 XT_SAVED_TABS_FILE
: named_session
);
5111 else if (args
->i
& XT_SAVE
) {
5112 if (session_save(t
, filename
)) {
5113 show_oops(t
, "Can't save session: %s",
5114 filename
? filename
: "INVALID");
5117 } else if (args
->i
& XT_OPEN
) {
5118 if (session_open(t
, filename
)) {
5119 show_oops(t
, "Can't open session: %s",
5120 filename
? filename
: "INVALID");
5123 } else if (args
->i
& XT_DELETE
) {
5124 if (session_delete(t
, filename
)) {
5125 show_oops(t
, "Can't delete session: %s",
5126 filename
? filename
: "INVALID");
5131 return (XT_CB_PASSTHROUGH
);
5135 * Make a hardcopy of the page
5138 print_page(struct tab
*t
, struct karg
*args
)
5140 WebKitWebFrame
*frame
;
5142 GtkPrintOperation
*op
;
5143 GtkPrintOperationAction action
;
5144 GtkPrintOperationResult print_res
;
5145 GError
*g_err
= NULL
;
5146 int marg_l
, marg_r
, marg_t
, marg_b
;
5148 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5150 ps
= gtk_page_setup_new();
5151 op
= gtk_print_operation_new();
5152 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5153 frame
= webkit_web_view_get_main_frame(t
->wv
);
5155 /* the default margins are too small, so we will bump them */
5156 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5157 XT_PRINT_EXTRA_MARGIN
;
5158 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5159 XT_PRINT_EXTRA_MARGIN
;
5160 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5161 XT_PRINT_EXTRA_MARGIN
;
5162 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5163 XT_PRINT_EXTRA_MARGIN
;
5166 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5167 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5168 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5169 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5171 gtk_print_operation_set_default_page_setup(op
, ps
);
5173 /* this appears to free 'op' and 'ps' */
5174 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5176 /* check it worked */
5177 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5178 show_oops(NULL
, "can't print: %s", g_err
->message
);
5179 g_error_free (g_err
);
5187 go_home(struct tab
*t
, struct karg
*args
)
5194 restart(struct tab
*t
, struct karg
*args
)
5198 a
.s
= XT_RESTART_TABS_FILE
;
5200 execvp(start_argv
[0], start_argv
);
5206 #define CTRL GDK_CONTROL_MASK
5207 #define MOD1 GDK_MOD1_MASK
5208 #define SHFT GDK_SHIFT_MASK
5210 /* inherent to GTK not all keys will be caught at all times */
5211 /* XXX sort key bindings */
5212 struct key_binding
{
5217 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5219 { "cookiejar", MOD1
, 0, GDK_j
},
5220 { "downloadmgr", MOD1
, 0, GDK_d
},
5221 { "history", MOD1
, 0, GDK_h
},
5222 { "print", CTRL
, 0, GDK_p
},
5223 { "search", 0, 0, GDK_slash
},
5224 { "searchb", 0, 0, GDK_question
},
5225 { "statustoggle", CTRL
, 0, GDK_n
},
5226 { "command", 0, 0, GDK_colon
},
5227 { "qa", CTRL
, 0, GDK_q
},
5228 { "restart", MOD1
, 0, GDK_q
},
5229 { "js toggle", CTRL
, 0, GDK_j
},
5230 { "cookie toggle", MOD1
, 0, GDK_c
},
5231 { "togglesrc", CTRL
, 0, GDK_s
},
5232 { "yankuri", 0, 0, GDK_y
},
5233 { "pasteuricur", 0, 0, GDK_p
},
5234 { "pasteurinew", 0, 0, GDK_P
},
5235 { "toplevel toggle", 0, 0, GDK_F4
},
5236 { "help", 0, 0, GDK_F1
},
5237 { "run_script", MOD1
, 0, GDK_r
},
5240 { "searchnext", 0, 0, GDK_n
},
5241 { "searchprevious", 0, 0, GDK_N
},
5244 { "focusaddress", 0, 0, GDK_F6
},
5245 { "focussearch", 0, 0, GDK_F7
},
5248 { "hinting", 0, 0, GDK_f
},
5250 /* custom stylesheet */
5251 { "userstyle", 0, 0, GDK_i
},
5254 { "goback", 0, 0, GDK_BackSpace
},
5255 { "goback", MOD1
, 0, GDK_Left
},
5256 { "goforward", SHFT
, 0, GDK_BackSpace
},
5257 { "goforward", MOD1
, 0, GDK_Right
},
5258 { "reload", 0, 0, GDK_F5
},
5259 { "reload", CTRL
, 0, GDK_r
},
5260 { "reload", CTRL
, 0, GDK_l
},
5261 { "favorites", MOD1
, 1, GDK_f
},
5263 /* vertical movement */
5264 { "scrolldown", 0, 0, GDK_j
},
5265 { "scrolldown", 0, 0, GDK_Down
},
5266 { "scrollup", 0, 0, GDK_Up
},
5267 { "scrollup", 0, 0, GDK_k
},
5268 { "scrollbottom", 0, 0, GDK_G
},
5269 { "scrollbottom", 0, 0, GDK_End
},
5270 { "scrolltop", 0, 0, GDK_Home
},
5271 { "scrollpagedown", 0, 0, GDK_space
},
5272 { "scrollpagedown", CTRL
, 0, GDK_f
},
5273 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5274 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5275 { "scrollpageup", 0, 0, GDK_Page_Up
},
5276 { "scrollpageup", CTRL
, 0, GDK_b
},
5277 { "scrollhalfup", CTRL
, 0, GDK_u
},
5278 /* horizontal movement */
5279 { "scrollright", 0, 0, GDK_l
},
5280 { "scrollright", 0, 0, GDK_Right
},
5281 { "scrollleft", 0, 0, GDK_Left
},
5282 { "scrollleft", 0, 0, GDK_h
},
5283 { "scrollfarright", 0, 0, GDK_dollar
},
5284 { "scrollfarleft", 0, 0, GDK_0
},
5287 { "tabnew", CTRL
, 0, GDK_t
},
5288 { "999tabnew", CTRL
, 0, GDK_T
},
5289 { "tabclose", CTRL
, 1, GDK_w
},
5290 { "tabundoclose", 0, 0, GDK_U
},
5291 { "tabnext 1", CTRL
, 0, GDK_1
},
5292 { "tabnext 2", CTRL
, 0, GDK_2
},
5293 { "tabnext 3", CTRL
, 0, GDK_3
},
5294 { "tabnext 4", CTRL
, 0, GDK_4
},
5295 { "tabnext 5", CTRL
, 0, GDK_5
},
5296 { "tabnext 6", CTRL
, 0, GDK_6
},
5297 { "tabnext 7", CTRL
, 0, GDK_7
},
5298 { "tabnext 8", CTRL
, 0, GDK_8
},
5299 { "tabnext 9", CTRL
, 0, GDK_9
},
5300 { "tabfirst", CTRL
, 0, GDK_less
},
5301 { "tablast", CTRL
, 0, GDK_greater
},
5302 { "tabprevious", CTRL
, 0, GDK_Left
},
5303 { "tabnext", CTRL
, 0, GDK_Right
},
5304 { "focusout", CTRL
, 0, GDK_minus
},
5305 { "focusin", CTRL
, 0, GDK_plus
},
5306 { "focusin", CTRL
, 0, GDK_equal
},
5307 { "focusreset", CTRL
, 0, GDK_0
},
5309 /* command aliases (handy when -S flag is used) */
5310 { "promptopen", 0, 0, GDK_F9
},
5311 { "promptopencurrent", 0, 0, GDK_F10
},
5312 { "prompttabnew", 0, 0, GDK_F11
},
5313 { "prompttabnewcurrent",0, 0, GDK_F12
},
5315 TAILQ_HEAD(keybinding_list
, key_binding
);
5318 walk_kb(struct settings
*s
,
5319 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5321 struct key_binding
*k
;
5324 if (s
== NULL
|| cb
== NULL
) {
5325 show_oops(NULL
, "walk_kb invalid parameters");
5329 TAILQ_FOREACH(k
, &kbl
, entry
) {
5335 if (gdk_keyval_name(k
->key
) == NULL
)
5338 strlcat(str
, k
->cmd
, sizeof str
);
5339 strlcat(str
, ",", sizeof str
);
5341 if (k
->mask
& GDK_SHIFT_MASK
)
5342 strlcat(str
, "S-", sizeof str
);
5343 if (k
->mask
& GDK_CONTROL_MASK
)
5344 strlcat(str
, "C-", sizeof str
);
5345 if (k
->mask
& GDK_MOD1_MASK
)
5346 strlcat(str
, "M1-", sizeof str
);
5347 if (k
->mask
& GDK_MOD2_MASK
)
5348 strlcat(str
, "M2-", sizeof str
);
5349 if (k
->mask
& GDK_MOD3_MASK
)
5350 strlcat(str
, "M3-", sizeof str
);
5351 if (k
->mask
& GDK_MOD4_MASK
)
5352 strlcat(str
, "M4-", sizeof str
);
5353 if (k
->mask
& GDK_MOD5_MASK
)
5354 strlcat(str
, "M5-", sizeof str
);
5356 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5357 cb(s
, str
, cb_args
);
5362 init_keybindings(void)
5365 struct key_binding
*k
;
5367 for (i
= 0; i
< LENGTH(keys
); i
++) {
5368 k
= g_malloc0(sizeof *k
);
5369 k
->cmd
= keys
[i
].cmd
;
5370 k
->mask
= keys
[i
].mask
;
5371 k
->use_in_entry
= keys
[i
].use_in_entry
;
5372 k
->key
= keys
[i
].key
;
5373 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5375 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5376 k
->cmd
? k
->cmd
: "unnamed key");
5381 keybinding_clearall(void)
5383 struct key_binding
*k
, *next
;
5385 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5386 next
= TAILQ_NEXT(k
, entry
);
5390 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5391 k
->cmd
? k
->cmd
: "unnamed key");
5392 TAILQ_REMOVE(&kbl
, k
, entry
);
5398 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5400 struct key_binding
*k
;
5401 guint keyval
, mask
= 0;
5404 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5406 /* Keys which are to be used in entry have been prefixed with an
5407 * exclamation mark. */
5411 /* find modifier keys */
5412 if (strstr(key
, "S-"))
5413 mask
|= GDK_SHIFT_MASK
;
5414 if (strstr(key
, "C-"))
5415 mask
|= GDK_CONTROL_MASK
;
5416 if (strstr(key
, "M1-"))
5417 mask
|= GDK_MOD1_MASK
;
5418 if (strstr(key
, "M2-"))
5419 mask
|= GDK_MOD2_MASK
;
5420 if (strstr(key
, "M3-"))
5421 mask
|= GDK_MOD3_MASK
;
5422 if (strstr(key
, "M4-"))
5423 mask
|= GDK_MOD4_MASK
;
5424 if (strstr(key
, "M5-"))
5425 mask
|= GDK_MOD5_MASK
;
5428 for (i
= strlen(key
) - 1; i
> 0; i
--)
5432 /* validate keyname */
5433 keyval
= gdk_keyval_from_name(key
);
5434 if (keyval
== GDK_VoidSymbol
) {
5435 warnx("invalid keybinding name %s", key
);
5438 /* must run this test too, gtk+ doesn't handle 10 for example */
5439 if (gdk_keyval_name(keyval
) == NULL
) {
5440 warnx("invalid keybinding name %s", key
);
5444 /* Remove eventual dupes. */
5445 TAILQ_FOREACH(k
, &kbl
, entry
)
5446 if (k
->key
== keyval
&& k
->mask
== mask
) {
5447 TAILQ_REMOVE(&kbl
, k
, entry
);
5453 k
= g_malloc0(sizeof *k
);
5454 k
->cmd
= g_strdup(cmd
);
5456 k
->use_in_entry
= use_in_entry
;
5459 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5464 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5465 k
->cmd
, gdk_keyval_name(keyval
));
5467 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5473 add_kb(struct settings
*s
, char *entry
)
5477 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5479 /* clearall is special */
5480 if (!strcmp(entry
, "clearall")) {
5481 keybinding_clearall();
5485 kb
= strstr(entry
, ",");
5491 return (keybinding_add(entry
, key
, key
[0] == '!'));
5497 int (*func
)(struct tab
*, struct karg
*);
5501 { "command", 0, command
, ':', 0 },
5502 { "search", 0, command
, '/', 0 },
5503 { "searchb", 0, command
, '?', 0 },
5504 { "togglesrc", 0, toggle_src
, 0, 0 },
5506 /* yanking and pasting */
5507 { "yankuri", 0, yank_uri
, 0, 0 },
5508 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5509 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5510 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5513 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5514 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5517 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5518 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5521 { "hinting", 0, hint
, 0, 0 },
5523 /* custom stylesheet */
5524 { "userstyle", 0, userstyle
, 0, 0 },
5527 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5528 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5529 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5531 /* vertical movement */
5532 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5533 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5534 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5535 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5536 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5537 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5538 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5539 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5540 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5541 /* horizontal movement */
5542 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5543 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5544 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5545 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5547 { "favorites", 0, xtp_page_fl
, 0, 0 },
5548 { "fav", 0, xtp_page_fl
, 0, 0 },
5549 { "favadd", 0, add_favorite
, 0, 0 },
5551 { "qall", 0, quit
, 0, 0 },
5552 { "quitall", 0, quit
, 0, 0 },
5553 { "w", 0, save_tabs
, 0, 0 },
5554 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5555 { "help", 0, help
, 0, 0 },
5556 { "about", 0, about
, 0, 0 },
5557 { "stats", 0, stats
, 0, 0 },
5558 { "version", 0, about
, 0, 0 },
5561 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5562 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5563 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5564 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5565 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5566 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5567 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5568 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5569 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5570 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5571 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5573 /* cookie command */
5574 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5575 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5576 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5577 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5578 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5579 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5580 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5581 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5582 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5583 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5584 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5586 /* toplevel (domain) command */
5587 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5588 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5591 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5594 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5595 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5596 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5598 { "ca", 0, ca_cmd
, 0, 0 },
5599 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5600 { "dl", 0, xtp_page_dl
, 0, 0 },
5601 { "h", 0, xtp_page_hl
, 0, 0 },
5602 { "history", 0, xtp_page_hl
, 0, 0 },
5603 { "home", 0, go_home
, 0, 0 },
5604 { "restart", 0, restart
, 0, 0 },
5605 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5606 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5607 { "statustoggle", 0, statustoggle
, 0, 0 },
5608 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5610 { "print", 0, print_page
, 0, 0 },
5613 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5614 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5615 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5616 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5617 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5618 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5619 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5620 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5621 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5622 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5623 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5624 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5625 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5626 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5627 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5628 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5629 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5630 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5631 { "buffers", 0, buffers
, 0, 0 },
5632 { "ls", 0, buffers
, 0, 0 },
5633 { "tabs", 0, buffers
, 0, 0 },
5635 /* command aliases (handy when -S flag is used) */
5636 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5637 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5638 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5639 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5642 { "set", 0, set
, 0, XT_USERARG
},
5644 { "fullscreen", 0, fullscreen
, 0, 0 },
5645 { "f", 0, fullscreen
, 0, 0 },
5648 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5649 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5650 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5651 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5652 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5659 } cmd_status
= {-1, 0};
5662 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5665 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5672 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5679 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5681 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5687 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5689 a
.i
= XT_NAV_FORWARD
;
5699 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5701 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5703 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5710 * cancel, remove, etc. downloads
5713 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5715 struct download find
, *d
= NULL
;
5717 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5719 /* some commands require a valid download id */
5720 if (cmd
!= XT_XTP_DL_LIST
) {
5721 /* lookup download in question */
5723 d
= RB_FIND(download_list
, &downloads
, &find
);
5726 show_oops(t
, "%s: no such download", __func__
);
5731 /* decide what to do */
5733 case XT_XTP_DL_CANCEL
:
5734 webkit_download_cancel(d
->download
);
5736 case XT_XTP_DL_REMOVE
:
5737 webkit_download_cancel(d
->download
); /* just incase */
5738 g_object_unref(d
->download
);
5739 RB_REMOVE(download_list
, &downloads
, d
);
5741 case XT_XTP_DL_LIST
:
5745 show_oops(t
, "%s: unknown command", __func__
);
5748 xtp_page_dl(t
, NULL
);
5752 * Actions on history, only does one thing for now, but
5753 * we provide the function for future actions
5756 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5758 struct history
*h
, *next
;
5762 case XT_XTP_HL_REMOVE
:
5763 /* walk backwards, as listed in reverse */
5764 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5765 next
= RB_PREV(history_list
, &hl
, h
);
5767 RB_REMOVE(history_list
, &hl
, h
);
5768 g_free((gpointer
) h
->title
);
5769 g_free((gpointer
) h
->uri
);
5776 case XT_XTP_HL_LIST
:
5777 /* Nothing - just xtp_page_hl() below */
5780 show_oops(t
, "%s: unknown command", __func__
);
5784 xtp_page_hl(t
, NULL
);
5787 /* remove a favorite */
5789 remove_favorite(struct tab
*t
, int index
)
5791 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5792 char *new_favs
, *tmp
;
5797 /* open favorites */
5798 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5800 if ((f
= fopen(file
, "r")) == NULL
) {
5801 show_oops(t
, "%s: can't open favorites: %s",
5802 __func__
, strerror(errno
));
5806 /* build a string which will become the new favroites file */
5807 new_favs
= g_strdup("");
5810 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5811 if (feof(f
) || ferror(f
))
5813 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5820 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5821 if (feof(f
) || ferror(f
)) {
5822 show_oops(t
, "%s: can't parse favorites %s",
5823 __func__
, strerror(errno
));
5828 /* as long as this isn't the one we are deleting add to file */
5831 new_favs
= g_strdup_printf("%s%s\n%s\n",
5832 new_favs
, title
, uri
);
5844 /* write back new favorites file */
5845 if ((f
= fopen(file
, "w")) == NULL
) {
5846 show_oops(t
, "%s: can't open favorites: %s",
5847 __func__
, strerror(errno
));
5851 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5864 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5867 case XT_XTP_FL_LIST
:
5868 /* nothing, just the below call to xtp_page_fl() */
5870 case XT_XTP_FL_REMOVE
:
5871 remove_favorite(t
, arg
);
5874 show_oops(t
, "%s: invalid favorites command", __func__
);
5878 xtp_page_fl(t
, NULL
);
5882 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5885 case XT_XTP_CL_LIST
:
5886 /* nothing, just xtp_page_cl() */
5888 case XT_XTP_CL_REMOVE
:
5892 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5896 xtp_page_cl(t
, NULL
);
5899 /* link an XTP class to it's session key and handler function */
5900 struct xtp_despatch
{
5903 void (*handle_func
)(struct tab
*, uint8_t, int);
5906 struct xtp_despatch xtp_despatches
[] = {
5907 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5908 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5909 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5910 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5911 { XT_XTP_INVALID
, NULL
, NULL
}
5915 * is the url xtp protocol? (xxxt://)
5916 * if so, parse and despatch correct bahvior
5919 parse_xtp_url(struct tab
*t
, const char *url
)
5921 char *dup
= NULL
, *p
, *last
;
5922 uint8_t n_tokens
= 0;
5923 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5924 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5929 * tokens array meaning:
5931 * tokens[1] = session key
5932 * tokens[2] = action
5933 * tokens[3] = optional argument
5936 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5938 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5941 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5943 /* split out the url */
5944 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5945 (p
= strtok_r(NULL
, "/", &last
))) {
5947 tokens
[n_tokens
++] = p
;
5950 /* should be atleast three fields 'class/seskey/command/arg' */
5954 dsp
= xtp_despatches
;
5955 req_class
= atoi(tokens
[0]);
5956 while (dsp
->xtp_class
) {
5957 if (dsp
->xtp_class
== req_class
) {
5964 /* did we find one atall? */
5965 if (dsp_match
== NULL
) {
5966 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5970 /* check session key and call despatch function */
5971 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5972 ret
= TRUE
; /* all is well, this was a valid xtp request */
5973 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5986 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5988 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5990 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5993 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5998 show_oops(t
, "activate_uri_entry_cb no uri");
6002 uri
+= strspn(uri
, "\t ");
6004 /* if xxxt:// treat specially */
6005 if (parse_xtp_url(t
, uri
))
6008 /* otherwise continue to load page normally */
6009 load_uri(t
, (gchar
*)uri
);
6014 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6016 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6017 char *newuri
= NULL
;
6020 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6023 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6027 if (search_string
== NULL
) {
6028 show_oops(t
, "no search_string");
6032 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6034 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6035 newuri
= g_strdup_printf(search_string
, enc_search
);
6039 webkit_web_view_load_uri(t
->wv
, newuri
);
6047 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6049 struct domain
*d
= NULL
;
6052 if (uri
== NULL
|| t
== NULL
)
6055 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6060 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6061 es
? "enable" : "disable", uri
);
6063 g_object_set(G_OBJECT(t
->settings
),
6064 "enable-html5-local-storage", es
, (char *)NULL
);
6065 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6069 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6071 struct domain
*d
= NULL
;
6074 if (uri
== NULL
|| t
== NULL
)
6077 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6082 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6083 es
? "enable" : "disable", uri
);
6085 g_object_set(G_OBJECT(t
->settings
),
6086 "enable-scripts", es
, (char *)NULL
);
6087 g_object_set(G_OBJECT(t
->settings
),
6088 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6089 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6091 button_set_stockid(t
->js_toggle
,
6092 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6096 color_address_bar(gpointer p
)
6099 struct tab
*tt
, *t
= p
;
6100 gchar
*col_str
= XT_COLOR_YELLOW
;
6102 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6104 /* make sure t still exists */
6107 TAILQ_FOREACH(tt
, &tabs
, entry
)
6113 switch (load_compare_cert(t
, NULL
)) {
6115 col_str
= XT_COLOR_BLUE
;
6118 col_str
= XT_COLOR_GREEN
;
6120 case CERT_UNTRUSTED
:
6121 col_str
= XT_COLOR_YELLOW
;
6124 col_str
= XT_COLOR_RED
;
6128 gdk_color_parse(col_str
, &color
);
6129 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6131 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6132 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6134 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6138 return (FALSE
/* kill thread */);
6142 show_ca_status(struct tab
*t
, const char *uri
)
6145 gchar
*col_str
= XT_COLOR_WHITE
;
6147 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6148 ssl_strict_certs
, ssl_ca_file
, uri
);
6155 if (ssl_ca_file
== NULL
) {
6156 if (g_str_has_prefix(uri
, "http://"))
6158 if (g_str_has_prefix(uri
, "https://")) {
6159 col_str
= XT_COLOR_RED
;
6164 if (g_str_has_prefix(uri
, "http://") ||
6165 !g_str_has_prefix(uri
, "https://"))
6168 /* thread the coloring of the address bar */
6169 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
,
6170 color_address_bar
, t
, NULL
);
6175 gdk_color_parse(col_str
, &color
);
6176 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6178 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6179 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6181 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6186 free_favicon(struct tab
*t
)
6188 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6189 __func__
, t
->icon_download
, t
->icon_request
);
6191 if (t
->icon_request
)
6192 g_object_unref(t
->icon_request
);
6193 if (t
->icon_dest_uri
)
6194 g_free(t
->icon_dest_uri
);
6196 t
->icon_request
= NULL
;
6197 t
->icon_dest_uri
= NULL
;
6201 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6203 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6204 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6206 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6207 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6209 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6210 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6214 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6216 GdkPixbuf
*pb_scaled
;
6218 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6219 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6220 GDK_INTERP_BILINEAR
);
6224 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6225 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6227 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6228 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6230 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6231 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6233 if (pb_scaled
!= pb
)
6234 g_object_unref(pb_scaled
);
6238 xt_icon_from_file(struct tab
*t
, char *file
)
6242 if (g_str_has_prefix(file
, "file://"))
6243 file
+= strlen("file://");
6245 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6247 xt_icon_from_pixbuf(t
, pb
);
6250 xt_icon_from_name(t
, "text-html");
6254 is_valid_icon(char *file
)
6257 const char *mime_type
;
6261 gf
= g_file_new_for_path(file
);
6262 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6264 mime_type
= g_file_info_get_content_type(fi
);
6265 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6266 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6267 g_strcmp0(mime_type
, "image/png") == 0 ||
6268 g_strcmp0(mime_type
, "image/gif") == 0 ||
6269 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6277 set_favicon_from_file(struct tab
*t
, char *file
)
6281 if (t
== NULL
|| file
== NULL
)
6284 if (g_str_has_prefix(file
, "file://"))
6285 file
+= strlen("file://");
6286 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6288 if (!stat(file
, &sb
)) {
6289 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6290 /* corrupt icon so trash it */
6291 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6294 /* no need to set icon to default here */
6298 xt_icon_from_file(t
, file
);
6302 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6305 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6306 struct tab
*tt
= NULL
, *t
= NULL
;
6309 * find the webview instead of passing in the tab as it could have been
6310 * deleted from underneath us.
6312 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6321 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6322 __func__
, t
->tab_id
, status
);
6325 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6327 t
->icon_download
= NULL
;
6330 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6333 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6336 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6338 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6339 __func__
, t
->tab_id
);
6340 t
->icon_download
= NULL
;
6343 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6346 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6347 __func__
, t
->icon_dest_uri
);
6348 set_favicon_from_file(t
, t
->icon_dest_uri
);
6349 /* these will be freed post callback */
6350 t
->icon_request
= NULL
;
6351 t
->icon_download
= NULL
;
6359 abort_favicon_download(struct tab
*t
)
6361 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6363 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6364 if (t
->icon_download
) {
6365 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6366 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6367 webkit_download_cancel(t
->icon_download
);
6368 t
->icon_download
= NULL
;
6373 xt_icon_from_name(t
, "text-html");
6377 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6379 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6381 if (uri
== NULL
|| t
== NULL
)
6384 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6385 /* take icon from WebKitIconDatabase */
6388 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6390 xt_icon_from_pixbuf(t
, pb
);
6393 xt_icon_from_name(t
, "text-html");
6394 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6395 /* download icon to cache dir */
6396 gchar
*name_hash
, file
[PATH_MAX
];
6399 if (t
->icon_request
) {
6400 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6404 /* check to see if we got the icon in cache */
6405 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6406 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6409 if (!stat(file
, &sb
)) {
6410 if (sb
.st_size
> 0) {
6411 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6413 set_favicon_from_file(t
, file
);
6417 /* corrupt icon so trash it */
6418 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6423 /* create download for icon */
6424 t
->icon_request
= webkit_network_request_new(uri
);
6425 if (t
->icon_request
== NULL
) {
6426 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6431 t
->icon_download
= webkit_download_new(t
->icon_request
);
6432 if (t
->icon_download
== NULL
)
6435 /* we have to free icon_dest_uri later */
6436 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6437 webkit_download_set_destination_uri(t
->icon_download
,
6440 if (webkit_download_get_status(t
->icon_download
) ==
6441 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6442 g_object_unref(t
->icon_request
);
6443 g_free(t
->icon_dest_uri
);
6444 t
->icon_request
= NULL
;
6445 t
->icon_dest_uri
= NULL
;
6449 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6450 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6452 webkit_download_start(t
->icon_download
);
6457 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6459 const gchar
*uri
= NULL
, *title
= NULL
;
6460 struct history
*h
, find
;
6464 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6465 webkit_web_view_get_load_status(wview
),
6466 get_uri(t
) ? get_uri(t
) : "NOTHING");
6469 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6473 switch (webkit_web_view_get_load_status(wview
)) {
6474 case WEBKIT_LOAD_PROVISIONAL
:
6476 abort_favicon_download(t
);
6477 #if GTK_CHECK_VERSION(2, 20, 0)
6478 gtk_widget_show(t
->spinner
);
6479 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6481 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6483 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6485 /* assume we are a new address */
6486 gdk_color_parse("white", &color
);
6487 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6488 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6490 /* take focus if we are visible */
6496 case WEBKIT_LOAD_COMMITTED
:
6501 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6507 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6509 /* check if js white listing is enabled */
6510 if (enable_cookie_whitelist
)
6511 check_and_set_cookie(uri
, t
);
6512 if (enable_js_whitelist
)
6513 check_and_set_js(uri
, t
);
6519 /* we know enough to autosave the session */
6520 if (session_autosave
) {
6525 show_ca_status(t
, uri
);
6528 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6532 case WEBKIT_LOAD_FINISHED
:
6538 if (!strncmp(uri
, "http://", strlen("http://")) ||
6539 !strncmp(uri
, "https://", strlen("https://")) ||
6540 !strncmp(uri
, "file://", strlen("file://"))) {
6542 h
= RB_FIND(history_list
, &hl
, &find
);
6544 title
= get_title(t
, FALSE
);
6545 h
= g_malloc(sizeof *h
);
6546 h
->uri
= g_strdup(uri
);
6547 h
->title
= g_strdup(title
);
6548 RB_INSERT(history_list
, &hl
, h
);
6549 completion_add_uri(h
->uri
);
6550 update_history_tabs(NULL
);
6554 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6555 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6556 case WEBKIT_LOAD_FAILED
:
6559 #if GTK_CHECK_VERSION(2, 20, 0)
6560 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6561 gtk_widget_hide(t
->spinner
);
6564 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6569 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6571 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6572 webkit_web_view_can_go_back(wview
));
6574 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6575 webkit_web_view_can_go_forward(wview
));
6580 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6581 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6584 * XXX this function is wrong
6585 * it overwrites perfectly good urls with garbage on load errors
6586 * those happen often when popups fail to resolve dns
6590 t
->tmp_uri
= g_strdup(uri
);
6591 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6592 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6593 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6594 set_status(t
, uri
, XT_STATUS_NOTHING
);
6601 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6603 const gchar
*title
= NULL
, *win_title
= NULL
;
6605 title
= get_title(t
, FALSE
);
6606 win_title
= get_title(t
, TRUE
);
6607 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6608 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6609 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6610 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6614 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6616 run_script(t
, JS_HINTING
);
6620 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6622 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6623 progress
== 100 ? 0 : (double)progress
/ 100);
6624 if (show_url
== 0) {
6625 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6626 progress
== 100 ? 0 : (double)progress
/ 100);
6629 update_statusbar_position(NULL
, NULL
);
6633 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6634 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6635 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6638 WebKitWebNavigationReason reason
;
6639 struct domain
*d
= NULL
;
6642 show_oops(NULL
, "webview_npd_cb invalid parameters");
6646 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6648 webkit_network_request_get_uri(request
));
6650 uri
= (char *)webkit_network_request_get_uri(request
);
6652 /* if this is an xtp url, we don't load anything else */
6653 if (parse_xtp_url(t
, uri
))
6656 if (t
->ctrl_click
) {
6658 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6659 webkit_web_policy_decision_ignore(pd
);
6660 return (TRUE
); /* we made the decission */
6664 * This is a little hairy but it comes down to this:
6665 * when we run in whitelist mode we have to assist the browser in
6666 * opening the URL that it would have opened in a new tab.
6668 reason
= webkit_web_navigation_action_get_reason(na
);
6669 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6670 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6671 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6672 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6674 webkit_web_policy_decision_use(pd
);
6675 return (TRUE
); /* we made the decision */
6682 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6685 struct domain
*d
= NULL
;
6687 WebKitWebView
*webview
= NULL
;
6689 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6690 webkit_web_view_get_uri(wv
));
6693 /* open in current tab */
6695 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6696 uri
= webkit_web_view_get_uri(wv
);
6697 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6700 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6702 } else if (enable_scripts
== 1) {
6703 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6711 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6714 struct domain
*d
= NULL
;
6716 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6718 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6719 uri
= webkit_web_view_get_uri(wv
);
6720 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6724 } else if (enable_scripts
== 1)
6731 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6733 /* we can not eat the event without throwing gtk off so defer it */
6735 /* catch middle click */
6736 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6741 /* catch ctrl click */
6742 if (e
->type
== GDK_BUTTON_RELEASE
&&
6743 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6748 return (XT_CB_PASSTHROUGH
);
6752 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6754 struct mime_type
*m
;
6756 m
= find_mime_type(mime_type
);
6764 show_oops(t
, "can't fork mime handler");
6774 execlp(m
->mt_action
, m
->mt_action
,
6775 webkit_network_request_get_uri(request
), (void *)NULL
);
6784 get_mime_type(char *file
)
6786 const char *mime_type
;
6790 if (g_str_has_prefix(file
, "file://"))
6791 file
+= strlen("file://");
6793 gf
= g_file_new_for_path(file
);
6794 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6796 mime_type
= g_file_info_get_content_type(fi
);
6804 run_download_mimehandler(char *mime_type
, char *file
)
6806 struct mime_type
*m
;
6808 m
= find_mime_type(mime_type
);
6814 show_oops(NULL
, "can't fork download mime handler");
6824 if (g_str_has_prefix(file
, "file://"))
6825 file
+= strlen("file://");
6826 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6835 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6838 WebKitDownloadStatus status
;
6839 const gchar
*file
= NULL
, *mime
= NULL
;
6841 if (download
== NULL
)
6843 status
= webkit_download_get_status(download
);
6844 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6847 file
= webkit_download_get_destination_uri(download
);
6850 mime
= get_mime_type((char *)file
);
6854 run_download_mimehandler((char *)mime
, (char *)file
);
6858 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6859 WebKitNetworkRequest
*request
, char *mime_type
,
6860 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6863 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6867 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6868 t
->tab_id
, mime_type
);
6870 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6871 webkit_web_policy_decision_ignore(decision
);
6876 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6877 webkit_web_policy_decision_download(decision
);
6885 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6889 const gchar
*suggested_name
;
6890 gchar
*filename
= NULL
;
6892 struct download
*download_entry
;
6895 if (wk_download
== NULL
|| t
== NULL
) {
6896 show_oops(NULL
, "%s invalid parameters", __func__
);
6900 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6901 if (suggested_name
== NULL
)
6902 return (FALSE
); /* abort download */
6913 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6915 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6916 filename
: suggested_name
);
6918 } while (!stat(uri
+ strlen("file://"), &sb
));
6920 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6921 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6923 webkit_download_set_destination_uri(wk_download
, uri
);
6925 if (webkit_download_get_status(wk_download
) ==
6926 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6927 show_oops(t
, "%s: download failed to start", __func__
);
6929 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6931 /* connect "download first" mime handler */
6932 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6933 G_CALLBACK(download_status_changed_cb
), NULL
);
6935 download_entry
= g_malloc(sizeof(struct download
));
6936 download_entry
->download
= wk_download
;
6937 download_entry
->tab
= t
;
6938 download_entry
->id
= next_download_id
++;
6939 RB_INSERT(download_list
, &downloads
, download_entry
);
6940 /* get from history */
6941 g_object_ref(wk_download
);
6942 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6943 show_oops(t
, "Download of '%s' started...",
6944 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6953 /* sync other download manager tabs */
6954 update_download_tabs(NULL
);
6957 * NOTE: never redirect/render the current tab before this
6958 * function returns. This will cause the download to never start.
6960 return (ret
); /* start download */
6964 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6966 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6969 show_oops(NULL
, "webview_hover_cb");
6974 set_status(t
, uri
, XT_STATUS_LINK
);
6977 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6982 mark(struct tab
*t
, struct karg
*arg
)
6988 if ((index
= marktoindex(mark
)) == -1)
6991 if (arg
->i
== XT_MARK_SET
)
6992 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6993 else if (arg
->i
== XT_MARK_GOTO
) {
6994 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6995 show_oops(t
, "mark '%c' does not exist", mark
);
6998 /* XXX t->mark[index] can be bigger than the maximum if ajax or
6999 something changes the document size */
7000 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7007 marks_clear(struct tab
*t
)
7011 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7012 t
->mark
[i
] = XT_INVALID_MARK
;
7018 char file
[PATH_MAX
];
7019 char *line
= NULL
, *p
, mark
;
7024 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7025 if ((f
= fopen(file
, "r+")) == NULL
) {
7026 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7030 for (i
= 1; ; i
++) {
7031 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7032 if (feof(f
) || ferror(f
))
7034 if (strlen(line
) == 0 || line
[0] == '#') {
7040 p
= strtok(line
, " \t");
7042 if (p
== NULL
|| strlen(p
) != 1 ||
7043 (index
= marktoindex(*p
)) == -1) {
7044 warnx("corrupt quickmarks file, line %d", i
);
7049 p
= strtok(NULL
, " \t");
7050 if (qmarks
[index
] != NULL
)
7051 g_free(qmarks
[index
]);
7052 qmarks
[index
] = g_strdup(p
);
7063 char file
[PATH_MAX
];
7067 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7068 if ((f
= fopen(file
, "r+")) == NULL
) {
7069 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7073 for (i
= 0; i
< XT_NOMARKS
; i
++)
7074 if (qmarks
[i
] != NULL
)
7075 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7083 qmark(struct tab
*t
, struct karg
*arg
)
7088 mark
= arg
->s
[strlen(arg
->s
)-1];
7089 index
= marktoindex(mark
);
7095 if (qmarks
[index
] != NULL
)
7096 g_free(qmarks
[index
]);
7098 qmarks_load(); /* sync if multiple instances */
7099 qmarks
[index
] = g_strdup(get_uri(t
));
7103 if (qmarks
[index
] != NULL
)
7104 load_uri(t
, qmarks
[index
]);
7106 show_oops(t
, "quickmark \"%c\" does not exist",
7112 if (qmarks
[index
] != NULL
)
7113 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7115 show_oops(t
, "quickmark \"%c\" does not exist",
7126 go_up(struct tab
*t
, struct karg
*args
)
7132 levels
= atoi(args
->s
);
7136 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7137 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7139 tmp
+= strlen(XT_PROTO_DELIM
);
7141 /* if an uri starts with a slash, leave it alone (for file:///) */
7148 p
= strrchr(tmp
, '/');
7162 gototab(struct tab
*t
, struct karg
*args
)
7165 struct karg arg
= {0, NULL
, -1};
7167 tab
= atoi(args
->s
);
7169 arg
.i
= XT_TAB_NEXT
;
7178 zoom_amount(struct tab
*t
, struct karg
*arg
)
7180 struct karg narg
= {0, NULL
, -1};
7182 narg
.i
= atoi(arg
->s
);
7183 resizetab(t
, &narg
);
7189 flip_colon(struct tab
*t
, struct karg
*arg
)
7191 struct karg narg
= {0, NULL
, -1};
7194 if (t
== NULL
|| arg
== NULL
)
7197 p
= strstr(arg
->s
, ":");
7209 /* buffer commands receive the regex that triggered them in arg.s */
7210 char bcmd
[XT_BUFCMD_SZ
];
7214 #define XT_PRE_NO (0)
7215 #define XT_PRE_YES (1)
7216 #define XT_PRE_MAYBE (2)
7218 int (*func
)(struct tab
*, struct karg
*);
7222 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7223 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7224 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7225 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7226 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7227 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7228 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7229 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7230 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7231 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7232 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7233 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7234 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7235 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7236 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7237 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7238 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7239 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7243 buffercmd_init(void)
7247 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7248 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7249 REG_EXTENDED
| REG_NOSUB
))
7250 startpage_add("invalid buffercmd regex %s",
7251 buffercmds
[i
].regex
);
7255 buffercmd_abort(struct tab
*t
)
7259 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7260 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7263 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7264 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7268 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7270 struct karg arg
= {0, NULL
, -1};
7273 arg
.s
= g_strdup(bcmd
);
7275 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7276 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7286 buffercmd_addkey(struct tab
*t
, guint keyval
)
7289 char s
[XT_BUFCMD_SZ
];
7291 if (keyval
== GDK_Escape
) {
7293 return (XT_CB_HANDLED
);
7296 /* key with modifier or non-ascii character */
7297 if (!isascii(keyval
))
7298 return (XT_CB_PASSTHROUGH
);
7300 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7301 "to buffer \"%s\"\n", keyval
, bcmd
);
7303 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7304 if (bcmd
[i
] == '\0') {
7309 /* buffer full, ignore input */
7310 if (i
>= LENGTH(bcmd
) -1) {
7311 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7313 return (XT_CB_HANDLED
);
7316 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7318 /* find exact match */
7319 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7320 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7321 (size_t) 0, NULL
, 0) == 0) {
7322 buffercmd_execute(t
, &buffercmds
[i
]);
7326 /* find non exact matches to see if we need to abort ot not */
7327 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7328 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7331 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7332 if (isdigit(bcmd
[0])) {
7333 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7337 if (sscanf(bcmd
, "%s", s
) == 0)
7340 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7341 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7344 if (sscanf(bcmd
, "%s", s
) == 0)
7347 if (c
== -1 && buffercmds
[i
].precount
)
7349 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7352 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7353 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7356 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7361 return (XT_CB_HANDLED
);
7365 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7367 struct key_binding
*k
;
7369 /* handle keybindings if buffercmd is empty.
7370 if not empty, allow commands like C-n */
7371 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7372 TAILQ_FOREACH(k
, &kbl
, entry
)
7373 if (e
->keyval
== k
->key
7374 && (entry
? k
->use_in_entry
: 1)) {
7376 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7377 return (cmd_execute(t
, k
->cmd
));
7378 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7379 return (cmd_execute(t
, k
->cmd
));
7383 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7384 return buffercmd_addkey(t
, e
->keyval
);
7386 return (XT_CB_PASSTHROUGH
);
7390 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7392 char s
[2], buf
[128];
7393 const char *errstr
= NULL
;
7395 /* don't use w directly; use t->whatever instead */
7398 show_oops(NULL
, "wv_keypress_after_cb");
7399 return (XT_CB_PASSTHROUGH
);
7402 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7403 e
->keyval
, e
->state
, t
);
7407 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7409 return (XT_CB_HANDLED
);
7413 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7415 /* we have a string */
7417 /* we have a number */
7418 snprintf(buf
, sizeof buf
,
7419 "vimprobable_fire(%s)", t
->hint_num
);
7426 /* XXX unfuck this */
7427 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7428 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7429 /* last input was numerical */
7431 l
= strlen(t
->hint_num
);
7438 t
->hint_num
[l
] = '\0';
7442 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7443 /* last input was alphanumerical */
7445 l
= strlen(t
->hint_buf
);
7452 t
->hint_buf
[l
] = '\0';
7462 /* numerical input */
7463 if (CLEAN(e
->state
) == 0 &&
7464 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7465 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7466 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7467 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7468 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7472 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7473 "invalid link number\n");
7476 snprintf(buf
, sizeof buf
,
7477 "vimprobable_update_hints(%s)",
7479 t
->hint_mode
= XT_HINT_NUMERICAL
;
7483 /* empty the counter buffer */
7484 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7485 return (XT_CB_HANDLED
);
7488 /* alphanumerical input */
7489 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7490 e
->keyval
<= GDK_z
) ||
7491 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7492 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7493 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7494 e
->keyval
<= GDK_9
) ||
7495 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7496 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7497 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7498 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7499 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7500 " %s\n", t
->hint_buf
);
7502 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7505 snprintf(buf
, sizeof buf
,
7506 "vimprobable_show_hints('%s')", t
->hint_buf
);
7507 t
->hint_mode
= XT_HINT_ALPHANUM
;
7510 /* empty the counter buffer */
7511 bzero(t
->hint_num
, sizeof t
->hint_num
);
7512 return (XT_CB_HANDLED
);
7515 return (XT_CB_HANDLED
);
7518 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7519 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7520 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7523 return (handle_keypress(t
, e
, 0));
7527 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7531 /* Hide buffers, if they are visible, with escape. */
7532 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7533 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7534 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7536 return (XT_CB_HANDLED
);
7539 return (XT_CB_PASSTHROUGH
);
7543 search_continue(struct tab
*t
)
7545 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7546 gboolean rv
= FALSE
;
7550 if (strlen(c
) == 1) {
7551 webkit_web_view_unmark_text_matches(t
->wv
);
7556 t
->search_forward
= TRUE
;
7557 else if (c
[0] == '?')
7558 t
->search_forward
= FALSE
;
7568 search_cb(struct tab
*t
)
7570 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7573 if (search_continue(t
) == FALSE
)
7577 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7579 /* not found, mark red */
7580 gdk_color_parse(XT_COLOR_RED
, &color
);
7581 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7582 /* unmark and remove selection */
7583 webkit_web_view_unmark_text_matches(t
->wv
);
7584 /* my kingdom for a way to unselect text in webview */
7586 /* found, highlight all */
7587 webkit_web_view_unmark_text_matches(t
->wv
);
7588 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7589 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7590 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7591 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7599 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7601 const gchar
*c
= gtk_entry_get_text(w
);
7604 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7605 return (XT_CB_PASSTHROUGH
);
7608 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7609 e
->keyval
, e
->state
, t
);
7611 if (search_continue(t
) == FALSE
)
7614 /* if search length is > 4 then no longer play timeout games */
7615 if (strlen(c
) > 4) {
7617 g_source_remove(t
->search_id
);
7624 /* reestablish a new timer if the user types fast */
7626 g_source_remove(t
->search_id
);
7627 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7630 return (XT_CB_PASSTHROUGH
);
7634 match_uri(const gchar
*uri
, const gchar
*key
) {
7637 gboolean match
= FALSE
;
7641 if (!strncmp(key
, uri
, len
))
7644 voffset
= strstr(uri
, "/") + 2;
7645 if (!strncmp(key
, voffset
, len
))
7647 else if (g_str_has_prefix(voffset
, "www.")) {
7648 voffset
= voffset
+ strlen("www.");
7649 if (!strncmp(key
, voffset
, len
))
7658 cmd_getlist(int id
, char *key
)
7663 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7664 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7665 if (match_uri(h
->uri
, key
)) {
7666 cmd_status
.list
[c
] = (char *)h
->uri
;
7675 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7677 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7678 if (cmds
[i
].level
< dep
)
7680 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7682 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7690 cmd_getnext(int dir
)
7692 cmd_status
.index
+= dir
;
7694 if (cmd_status
.index
< 0)
7695 cmd_status
.index
= cmd_status
.len
- 1;
7696 else if (cmd_status
.index
>= cmd_status
.len
)
7697 cmd_status
.index
= 0;
7699 return cmd_status
.list
[cmd_status
.index
];
7703 cmd_tokenize(char *s
, char *tokens
[])
7707 size_t len
= strlen(s
);
7710 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7711 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7712 tok
= strtok_r(NULL
, " ", &last
), i
++)
7722 cmd_complete(struct tab
*t
, char *str
, int dir
)
7724 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7725 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7727 char *tok
, *match
, *s
= g_strdup(str
);
7729 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7732 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7735 for (i
= 0; isdigit(s
[i
]); i
++)
7738 for (; isspace(s
[i
]); i
++)
7743 levels
= cmd_tokenize(s
, tokens
);
7745 for (i
= 0; i
< levels
- 1; i
++) {
7748 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7749 if (cmds
[j
].level
< dep
)
7751 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7755 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7762 if (matchcount
== 1) {
7763 strlcat(res
, tok
, sizeof res
);
7764 strlcat(res
, " ", sizeof res
);
7774 if (cmd_status
.index
== -1)
7775 cmd_getlist(parent
, tokens
[i
]);
7777 if (cmd_status
.len
> 0) {
7778 match
= cmd_getnext(dir
);
7779 strlcat(res
, match
, sizeof res
);
7780 gtk_entry_set_text(w
, res
);
7781 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7788 cmd_execute(struct tab
*t
, char *str
)
7790 struct cmd
*cmd
= NULL
;
7791 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7793 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7794 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7795 struct karg arg
= {0, NULL
, -1};
7800 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7806 while (isspace(s
[0]))
7809 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7810 prefix
= atoi(prefixstr
);
7814 for (tok
= strtok_r(s
, " ", &last
); tok
;
7815 tok
= strtok_r(NULL
, " ", &last
)) {
7817 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7818 if (cmds
[j
].level
< dep
)
7820 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7822 if (cmds
[j
].level
== dep
&&
7823 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7827 if (len
== strlen(cmds
[j
].cmd
)) {
7833 if (matchcount
== 1) {
7838 show_oops(t
, "Invalid command: %s", str
);
7846 arg
.precount
= prefix
;
7847 else if (cmd_prefix
> 0)
7848 arg
.precount
= cmd_prefix
;
7850 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
7851 show_oops(t
, "No prefix allowed: %s", str
);
7855 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7856 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7857 arg
.precount
= atoi(arg
.s
);
7858 if (arg
.precount
<= 0) {
7859 if (arg
.s
[0] == '0')
7860 show_oops(t
, "Zero count");
7862 show_oops(t
, "Trailing characters");
7867 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
7868 __func__
, arg
.precount
, arg
.s
);
7884 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7887 show_oops(NULL
, "entry_key_cb invalid parameters");
7888 return (XT_CB_PASSTHROUGH
);
7891 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7892 e
->keyval
, e
->state
, t
);
7896 if (e
->keyval
== GDK_Escape
) {
7897 /* don't use focus_webview(t) because we want to type :cmds */
7898 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7901 return (handle_keypress(t
, e
, 1));
7905 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7907 int rv
= XT_CB_HANDLED
;
7908 const gchar
*c
= gtk_entry_get_text(w
);
7911 show_oops(NULL
, "cmd_keypress_cb parameters");
7912 return (XT_CB_PASSTHROUGH
);
7915 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7916 e
->keyval
, e
->state
, t
);
7920 e
->keyval
= GDK_Escape
;
7921 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7922 e
->keyval
= GDK_Escape
;
7924 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7925 e
->keyval
!= GDK_ISO_Left_Tab
)
7926 cmd_status
.index
= -1;
7928 switch (e
->keyval
) {
7931 cmd_complete(t
, (char *)&c
[1], 1);
7933 case GDK_ISO_Left_Tab
:
7935 cmd_complete(t
, (char *)&c
[1], -1);
7939 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7947 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7948 webkit_web_view_unmark_text_matches(t
->wv
);
7952 rv
= XT_CB_PASSTHROUGH
;
7958 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7961 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7962 return (XT_CB_PASSTHROUGH
);
7964 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7969 if (show_url
== 0 || t
->focus_wv
)
7972 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7974 return (XT_CB_PASSTHROUGH
);
7978 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7981 const gchar
*c
= gtk_entry_get_text(entry
);
7984 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7988 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7995 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8001 if (c
[0] == '/' || c
[0] == '?') {
8002 /* see if there is a timer pending */
8004 g_source_remove(t
->search_id
);
8009 if (t
->search_text
) {
8010 g_free(t
->search_text
);
8011 t
->search_text
= NULL
;
8014 t
->search_text
= g_strdup(s
);
8016 g_free(global_search
);
8017 global_search
= g_strdup(s
);
8018 t
->search_forward
= c
[0] == '/';
8030 backward_cb(GtkWidget
*w
, struct tab
*t
)
8035 show_oops(NULL
, "backward_cb invalid parameters");
8039 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8046 forward_cb(GtkWidget
*w
, struct tab
*t
)
8051 show_oops(NULL
, "forward_cb invalid parameters");
8055 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8057 a
.i
= XT_NAV_FORWARD
;
8062 home_cb(GtkWidget
*w
, struct tab
*t
)
8065 show_oops(NULL
, "home_cb invalid parameters");
8069 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8075 stop_cb(GtkWidget
*w
, struct tab
*t
)
8077 WebKitWebFrame
*frame
;
8080 show_oops(NULL
, "stop_cb invalid parameters");
8084 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8086 frame
= webkit_web_view_get_main_frame(t
->wv
);
8087 if (frame
== NULL
) {
8088 show_oops(t
, "stop_cb: no frame");
8092 webkit_web_frame_stop_loading(frame
);
8093 abort_favicon_download(t
);
8097 setup_webkit(struct tab
*t
)
8099 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8100 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8101 FALSE
, (char *)NULL
);
8103 warnx("webkit does not have \"enable-dns-prefetching\" property");
8104 g_object_set(G_OBJECT(t
->settings
),
8105 "user-agent", t
->user_agent
, (char *)NULL
);
8106 g_object_set(G_OBJECT(t
->settings
),
8107 "enable-scripts", enable_scripts
, (char *)NULL
);
8108 g_object_set(G_OBJECT(t
->settings
),
8109 "enable-plugins", enable_plugins
, (char *)NULL
);
8110 g_object_set(G_OBJECT(t
->settings
),
8111 "javascript-can-open-windows-automatically", enable_scripts
,
8113 g_object_set(G_OBJECT(t
->settings
),
8114 "enable-html5-database", FALSE
, (char *)NULL
);
8115 g_object_set(G_OBJECT(t
->settings
),
8116 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8117 g_object_set(G_OBJECT(t
->settings
),
8118 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8119 g_object_set(G_OBJECT(t
->settings
),
8120 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8121 g_object_set(G_OBJECT(t
->wv
),
8122 "full-content-zoom", TRUE
, (char *)NULL
);
8124 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8128 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8130 struct tab
*ti
, *t
= NULL
;
8131 gdouble view_size
, value
, max
;
8134 TAILQ_FOREACH(ti
, &tabs
, entry
)
8135 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8143 if (adjustment
== NULL
)
8144 adjustment
= gtk_scrolled_window_get_vadjustment(
8145 GTK_SCROLLED_WINDOW(t
->browser_win
));
8147 view_size
= gtk_adjustment_get_page_size(adjustment
);
8148 value
= gtk_adjustment_get_value(adjustment
);
8149 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8152 position
= g_strdup("All");
8153 else if (value
== max
)
8154 position
= g_strdup("Bot");
8155 else if (value
== 0)
8156 position
= g_strdup("Top");
8158 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8160 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8167 create_browser(struct tab
*t
)
8171 GtkAdjustment
*adjustment
;
8174 show_oops(NULL
, "create_browser invalid parameters");
8178 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8179 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8180 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8181 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8183 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8184 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8185 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8187 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8188 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8191 t
->settings
= webkit_web_settings_new();
8193 if (user_agent
== NULL
) {
8194 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8196 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8199 t
->user_agent
= g_strdup(user_agent
);
8201 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8204 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8205 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8206 G_CALLBACK(update_statusbar_position
), NULL
);
8218 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8219 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8220 gtk_widget_set_name(w
, "xxxterm");
8221 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8222 g_signal_connect(G_OBJECT(w
), "delete_event",
8223 G_CALLBACK (gtk_main_quit
), NULL
);
8229 create_kiosk_toolbar(struct tab
*t
)
8231 GtkWidget
*toolbar
= NULL
, *b
;
8233 b
= gtk_hbox_new(FALSE
, 0);
8235 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8237 /* backward button */
8238 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8239 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8240 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8241 G_CALLBACK(backward_cb
), t
);
8242 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8244 /* forward button */
8245 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8246 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8247 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8248 G_CALLBACK(forward_cb
), t
);
8249 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8252 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8253 gtk_widget_set_sensitive(t
->gohome
, true);
8254 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8255 G_CALLBACK(home_cb
), t
);
8256 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8258 /* create widgets but don't use them */
8259 t
->uri_entry
= gtk_entry_new();
8260 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8261 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8262 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8268 create_toolbar(struct tab
*t
)
8270 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8272 b
= gtk_hbox_new(FALSE
, 0);
8274 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8276 /* backward button */
8277 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8278 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8279 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8280 G_CALLBACK(backward_cb
), t
);
8281 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8283 /* forward button */
8284 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8285 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8286 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8287 G_CALLBACK(forward_cb
), t
);
8288 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8292 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8293 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8294 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8295 G_CALLBACK(stop_cb
), t
);
8296 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8300 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8301 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8302 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8303 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8304 G_CALLBACK(js_toggle_cb
), t
);
8305 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8307 t
->uri_entry
= gtk_entry_new();
8308 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8309 G_CALLBACK(activate_uri_entry_cb
), t
);
8310 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8311 G_CALLBACK(entry_key_cb
), t
);
8313 eb1
= gtk_hbox_new(FALSE
, 0);
8314 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8315 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8316 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8319 if (search_string
) {
8321 t
->search_entry
= gtk_entry_new();
8322 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8323 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8324 G_CALLBACK(activate_search_entry_cb
), t
);
8325 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8326 G_CALLBACK(entry_key_cb
), t
);
8327 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8328 eb2
= gtk_hbox_new(FALSE
, 0);
8329 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8330 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8332 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8339 create_buffers(struct tab
*t
)
8341 GtkCellRenderer
*renderer
;
8344 view
= gtk_tree_view_new();
8346 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8348 renderer
= gtk_cell_renderer_text_new();
8349 gtk_tree_view_insert_column_with_attributes
8350 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
8352 renderer
= gtk_cell_renderer_text_new();
8353 gtk_tree_view_insert_column_with_attributes
8354 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8357 gtk_tree_view_set_model
8358 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8364 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8365 GtkTreeViewColumn
*col
, struct tab
*t
)
8370 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8372 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8375 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8376 set_current_tab(id
- 1);
8382 /* after tab reordering/creation/removal */
8389 TAILQ_FOREACH(t
, &tabs
, entry
) {
8390 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8391 if (t
->tab_id
> maxid
)
8394 gtk_widget_show(t
->tab_elems
.sep
);
8397 TAILQ_FOREACH(t
, &tabs
, entry
) {
8398 if (t
->tab_id
== maxid
) {
8399 gtk_widget_hide(t
->tab_elems
.sep
);
8405 /* after active tab change */
8407 recolor_compact_tabs(void)
8413 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8414 TAILQ_FOREACH(t
, &tabs
, entry
)
8415 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8418 curid
= gtk_notebook_get_current_page(notebook
);
8419 TAILQ_FOREACH(t
, &tabs
, entry
)
8420 if (t
->tab_id
== curid
) {
8421 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8422 gtk_widget_modify_fg(t
->tab_elems
.label
,
8423 GTK_STATE_NORMAL
, &color
);
8429 set_current_tab(int page_num
)
8431 buffercmd_abort(get_current_tab());
8432 gtk_notebook_set_current_page(notebook
, page_num
);
8433 recolor_compact_tabs();
8437 undo_close_tab_save(struct tab
*t
)
8441 struct undo
*u1
, *u2
;
8443 WebKitWebHistoryItem
*item
;
8445 if ((uri
= get_uri(t
)) == NULL
)
8448 u1
= g_malloc0(sizeof(struct undo
));
8449 u1
->uri
= g_strdup(uri
);
8451 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8453 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8454 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8457 /* forward history */
8458 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8462 u1
->history
= g_list_prepend(u1
->history
,
8463 webkit_web_history_item_copy(item
));
8464 items
= g_list_next(items
);
8469 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8470 u1
->history
= g_list_prepend(u1
->history
,
8471 webkit_web_history_item_copy(item
));
8475 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8479 u1
->history
= g_list_prepend(u1
->history
,
8480 webkit_web_history_item_copy(item
));
8481 items
= g_list_next(items
);
8484 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8486 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8487 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8488 TAILQ_REMOVE(&undos
, u2
, entry
);
8490 g_list_free(u2
->history
);
8499 delete_tab(struct tab
*t
)
8503 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8509 TAILQ_REMOVE(&tabs
, t
, entry
);
8511 /* Halt all webkit activity. */
8512 abort_favicon_download(t
);
8513 webkit_web_view_stop_loading(t
->wv
);
8515 /* Save the tab, so we can undo the close. */
8516 undo_close_tab_save(t
);
8518 if (browser_mode
== XT_BM_KIOSK
) {
8519 gtk_widget_destroy(t
->uri_entry
);
8520 gtk_widget_destroy(t
->stop
);
8521 gtk_widget_destroy(t
->js_toggle
);
8524 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8525 gtk_widget_destroy(t
->vbox
);
8529 g_source_remove(t
->search_id
);
8531 g_free(t
->user_agent
);
8532 g_free(t
->stylesheet
);
8536 if (TAILQ_EMPTY(&tabs
)) {
8537 if (browser_mode
== XT_BM_KIOSK
)
8538 create_new_tab(home
, NULL
, 1, -1);
8540 create_new_tab(NULL
, NULL
, 1, -1);
8543 /* recreate session */
8544 if (session_autosave
) {
8550 recolor_compact_tabs();
8554 update_statusbar_zoom(struct tab
*t
)
8557 char s
[16] = { '\0' };
8559 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8560 if ((zoom
<= 0.99 || zoom
>= 1.01))
8561 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8562 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8566 setzoom_webkit(struct tab
*t
, int adjust
)
8568 #define XT_ZOOMPERCENT 0.04
8573 show_oops(NULL
, "setzoom_webkit invalid parameters");
8577 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8578 if (adjust
== XT_ZOOM_IN
)
8579 zoom
+= XT_ZOOMPERCENT
;
8580 else if (adjust
== XT_ZOOM_OUT
)
8581 zoom
-= XT_ZOOMPERCENT
;
8582 else if (adjust
> 0)
8583 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8585 show_oops(t
, "setzoom_webkit invalid zoom value");
8589 if (zoom
< XT_ZOOMPERCENT
)
8590 zoom
= XT_ZOOMPERCENT
;
8591 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8592 update_statusbar_zoom(t
);
8596 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8598 struct tab
*t
= (struct tab
*) data
;
8600 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8602 switch (event
->button
) {
8604 set_current_tab(t
->tab_id
);
8615 append_tab(struct tab
*t
)
8620 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8621 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8625 create_sbe(int width
)
8629 sbe
= gtk_entry_new();
8630 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8631 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8632 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8633 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8634 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8635 gtk_widget_set_size_request(sbe
, width
, -1);
8641 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8646 WebKitWebHistoryItem
*item
;
8650 int sbe_p
= 0, sbe_b
= 0,
8653 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8655 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8656 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8660 t
= g_malloc0(sizeof *t
);
8662 if (title
== NULL
) {
8663 title
= "(untitled)";
8667 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8669 /* label + button for tab */
8670 b
= gtk_hbox_new(FALSE
, 0);
8673 #if GTK_CHECK_VERSION(2, 20, 0)
8674 t
->spinner
= gtk_spinner_new();
8676 t
->label
= gtk_label_new(title
);
8677 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8678 gtk_widget_set_size_request(t
->label
, 100, 0);
8679 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8680 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8681 gtk_widget_set_size_request(b
, 130, 0);
8683 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8684 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8685 #if GTK_CHECK_VERSION(2, 20, 0)
8686 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8690 if (browser_mode
== XT_BM_KIOSK
) {
8691 t
->toolbar
= create_kiosk_toolbar(t
);
8692 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8695 t
->toolbar
= create_toolbar(t
);
8697 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8705 t
->browser_win
= create_browser(t
);
8706 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8708 /* oops message for user feedback */
8709 t
->oops
= gtk_entry_new();
8710 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8711 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8712 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8713 gdk_color_parse(XT_COLOR_RED
, &color
);
8714 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8715 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8716 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8719 t
->cmd
= gtk_entry_new();
8720 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8721 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8722 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8723 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8726 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8728 t
->sbe
.statusbar
= gtk_entry_new();
8729 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8730 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8731 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8732 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8734 /* create these widgets only if specified in statusbar_elems */
8736 t
->sbe
.position
= create_sbe(40);
8737 t
->sbe
.zoom
= create_sbe(40);
8738 t
->sbe
.buffercmd
= create_sbe(60);
8740 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8742 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8745 /* gtk widgets cannot be added to a box twice. sbe_* variables
8746 make sure of this */
8747 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8751 GtkWidget
*sep
= gtk_vseparator_new();
8753 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8754 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8755 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8756 FALSE
, FALSE
, FALSE
);
8761 warnx("flag \"%c\" specified more than "
8762 "once in statusbar_elems\n", *p
);
8766 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8767 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8771 warnx("flag \"%c\" specified more than "
8772 "once in statusbar_elems\n", *p
);
8776 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8777 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8781 warnx("flag \"%c\" specified more than "
8782 "once in statusbar_elems\n", *p
);
8786 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8787 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8790 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8795 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8798 t
->buffers
= create_buffers(t
);
8799 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8801 /* xtp meaning is normal by default */
8802 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8804 /* set empty favicon */
8805 xt_icon_from_name(t
, "text-html");
8807 /* and show it all */
8808 gtk_widget_show_all(b
);
8809 gtk_widget_show_all(t
->vbox
);
8811 /* compact tab bar */
8812 t
->tab_elems
.label
= gtk_label_new(title
);
8813 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8814 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8815 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8816 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8818 t
->tab_elems
.eventbox
= gtk_event_box_new();
8819 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8820 t
->tab_elems
.sep
= gtk_vseparator_new();
8822 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8823 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8824 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8825 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8826 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8827 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8829 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8831 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8833 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8836 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8838 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8840 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8843 id
= position
>= 0 ? position
:
8844 gtk_notebook_get_current_page(notebook
) + 1;
8845 if (id
> gtk_notebook_get_n_pages(notebook
))
8848 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8849 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8850 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8851 t
->tab_elems
.eventbox
, id
);
8856 #if GTK_CHECK_VERSION(2, 20, 0)
8857 /* turn spinner off if we are a new tab without uri */
8859 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8860 gtk_widget_hide(t
->spinner
);
8863 /* make notebook tabs reorderable */
8864 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8866 /* compact tabs clickable */
8867 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8868 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8870 g_object_connect(G_OBJECT(t
->cmd
),
8871 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8872 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8873 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8874 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8877 /* reuse wv_button_cb to hide oops */
8878 g_object_connect(G_OBJECT(t
->oops
),
8879 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8882 g_signal_connect(t
->buffers
,
8883 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8884 g_object_connect(G_OBJECT(t
->buffers
),
8885 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8887 g_object_connect(G_OBJECT(t
->wv
),
8888 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8889 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8890 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8891 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8892 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8893 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8894 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8895 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8896 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8897 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8898 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8899 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8900 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8901 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8902 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8904 g_signal_connect(t
->wv
,
8905 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8907 * XXX this puts invalid url in uri_entry and that is undesirable
8910 g_signal_connect(t
->wv
,
8911 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8913 g_signal_connect(t
->wv
,
8914 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8916 /* hijack the unused keys as if we were the browser */
8917 g_object_connect(G_OBJECT(t
->toolbar
),
8918 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8921 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8922 G_CALLBACK(tab_close_cb
), t
);
8928 url_set_visibility();
8929 statusbar_set_visibility();
8932 set_current_tab(t
->tab_id
);
8933 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8938 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8942 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8947 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8948 /* restore the tab's history */
8949 if (u
&& u
->history
) {
8953 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8954 items
= g_list_next(items
);
8957 item
= g_list_nth_data(u
->history
, u
->back
);
8959 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8962 g_list_free(u
->history
);
8964 webkit_web_back_forward_list_clear(t
->bfl
);
8966 recolor_compact_tabs();
8967 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
8972 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8978 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8980 if (gtk_notebook_get_current_page(notebook
) == -1)
8983 TAILQ_FOREACH(t
, &tabs
, entry
) {
8984 if (t
->tab_id
== pn
) {
8985 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
8988 uri
= get_title(t
, TRUE
);
8989 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8995 /* can't use focus_webview here */
8996 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9003 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9006 struct tab
*t
= NULL
, *tt
;
9010 TAILQ_FOREACH(tt
, &tabs
, entry
)
9011 if (tt
->tab_id
== pn
) {
9016 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9018 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9023 menuitem_response(struct tab
*t
)
9025 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9029 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9031 GtkWidget
*menu
, *menu_items
;
9032 GdkEventButton
*bevent
;
9036 if (event
->type
== GDK_BUTTON_PRESS
) {
9037 bevent
= (GdkEventButton
*) event
;
9038 menu
= gtk_menu_new();
9040 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9041 if ((uri
= get_uri(ti
)) == NULL
)
9042 /* XXX make sure there is something to print */
9043 /* XXX add gui pages in here to look purdy */
9045 menu_items
= gtk_menu_item_new_with_label(uri
);
9046 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9047 gtk_widget_show(menu_items
);
9049 g_signal_connect_swapped((menu_items
),
9050 "activate", G_CALLBACK(menuitem_response
),
9054 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9055 bevent
->button
, bevent
->time
);
9057 /* unref object so it'll free itself when popped down */
9058 #if !GTK_CHECK_VERSION(3, 0, 0)
9059 /* XXX does not need unref with gtk+3? */
9060 g_object_ref_sink(menu
);
9061 g_object_unref(menu
);
9064 return (TRUE
/* eat event */);
9067 return (FALSE
/* propagate */);
9071 icon_size_map(int icon_size
)
9073 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9074 icon_size
> GTK_ICON_SIZE_DIALOG
)
9075 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9081 create_button(char *name
, char *stockid
, int size
)
9083 GtkWidget
*button
, *image
;
9087 rcstring
= g_strdup_printf(
9088 "style \"%s-style\"\n"
9090 " GtkWidget::focus-padding = 0\n"
9091 " GtkWidget::focus-line-width = 0\n"
9095 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9096 gtk_rc_parse_string(rcstring
);
9098 button
= gtk_button_new();
9099 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9100 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9102 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9103 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9104 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9105 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9106 gtk_widget_set_name(button
, name
);
9107 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9113 button_set_stockid(GtkWidget
*button
, char *stockid
)
9117 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9118 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9119 gtk_button_set_image(GTK_BUTTON(button
), image
);
9123 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9126 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9129 if (xterm_workaround
== 0)
9133 * xterm doesn't play nice with clipboards because it clears the
9134 * primary when clicked. We rely on primary being set to properly
9135 * handle middle mouse button clicks (paste). So when someone clears
9136 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9137 * other application behavior (as in DON'T clear primary).
9140 p
= gtk_clipboard_wait_for_text(primary
);
9142 if (gdk_property_get(gdk_get_default_root_window(),
9144 gdk_atom_intern("STRING", FALSE
),
9146 1024 * 1024 /* picked out of my butt */,
9152 /* yes sir, we need to NUL the string */
9154 gtk_clipboard_set_text(primary
, p
, -1);
9168 char file
[PATH_MAX
];
9171 vbox
= gtk_vbox_new(FALSE
, 0);
9172 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9173 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9174 #if !GTK_CHECK_VERSION(3, 0, 0)
9175 /* XXX seems to be needed with gtk+2 */
9176 gtk_notebook_set_tab_hborder(notebook
, 0);
9177 gtk_notebook_set_tab_vborder(notebook
, 0);
9179 gtk_notebook_set_scrollable(notebook
, TRUE
);
9180 gtk_notebook_set_show_border(notebook
, FALSE
);
9181 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9183 abtn
= gtk_button_new();
9184 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9185 gtk_widget_set_size_request(arrow
, -1, -1);
9186 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9187 gtk_widget_set_size_request(abtn
, -1, 20);
9189 #if GTK_CHECK_VERSION(2, 20, 0)
9190 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9192 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9194 /* compact tab bar */
9195 tab_bar
= gtk_hbox_new(TRUE
, 0);
9197 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9198 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9199 gtk_widget_set_size_request(vbox
, -1, -1);
9201 g_object_connect(G_OBJECT(notebook
),
9202 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9204 g_object_connect(G_OBJECT(notebook
),
9205 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9206 NULL
, (char *)NULL
);
9207 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9208 G_CALLBACK(arrow_cb
), NULL
);
9210 main_window
= create_window();
9211 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9214 for (i
= 0; i
< LENGTH(icons
); i
++) {
9215 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9216 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9217 l
= g_list_append(l
, pb
);
9219 gtk_window_set_default_icon_list(l
);
9221 /* clipboard work around */
9222 if (xterm_workaround
)
9224 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9225 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9227 gtk_widget_show_all(abtn
);
9228 gtk_widget_show_all(main_window
);
9229 notebook_tab_set_visibility();
9233 set_hook(void **hook
, char *name
)
9236 errx(1, "set_hook");
9238 if (*hook
== NULL
) {
9239 *hook
= dlsym(RTLD_NEXT
, name
);
9241 errx(1, "can't hook %s", name
);
9245 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9247 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9249 g_return_val_if_fail(cookie1
, FALSE
);
9250 g_return_val_if_fail(cookie2
, FALSE
);
9252 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9253 !strcmp (cookie1
->value
, cookie2
->value
) &&
9254 !strcmp (cookie1
->path
, cookie2
->path
) &&
9255 !strcmp (cookie1
->domain
, cookie2
->domain
));
9259 transfer_cookies(void)
9262 SoupCookie
*sc
, *pc
;
9264 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9266 for (;cf
; cf
= cf
->next
) {
9268 sc
= soup_cookie_copy(pc
);
9269 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9272 soup_cookies_free(cf
);
9276 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9281 print_cookie("soup_cookie_jar_delete_cookie", c
);
9283 if (cookies_enabled
== 0)
9286 if (jar
== NULL
|| c
== NULL
)
9289 /* find and remove from persistent jar */
9290 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9292 for (;cf
; cf
= cf
->next
) {
9294 if (soup_cookie_equal(ci
, c
)) {
9295 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9300 soup_cookies_free(cf
);
9302 /* delete from session jar */
9303 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9307 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9309 struct domain
*d
= NULL
;
9313 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9314 jar
, p_cookiejar
, s_cookiejar
);
9316 if (cookies_enabled
== 0)
9319 /* see if we are up and running */
9320 if (p_cookiejar
== NULL
) {
9321 _soup_cookie_jar_add_cookie(jar
, cookie
);
9324 /* disallow p_cookiejar adds, shouldn't happen */
9325 if (jar
== p_cookiejar
)
9329 if (jar
== NULL
|| cookie
== NULL
)
9332 if (enable_cookie_whitelist
&&
9333 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9335 DNPRINTF(XT_D_COOKIE
,
9336 "soup_cookie_jar_add_cookie: reject %s\n",
9338 if (save_rejected_cookies
) {
9339 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9340 show_oops(NULL
, "can't open reject cookie file");
9343 fseek(r_cookie_f
, 0, SEEK_END
);
9344 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9345 cookie
->http_only
? "#HttpOnly_" : "",
9347 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9349 cookie
->secure
? "TRUE" : "FALSE",
9351 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9358 if (!allow_volatile_cookies
)
9362 if (cookie
->expires
== NULL
&& session_timeout
) {
9363 soup_cookie_set_expires(cookie
,
9364 soup_date_new_from_now(session_timeout
));
9365 print_cookie("modified add cookie", cookie
);
9368 /* see if we are white listed for persistence */
9369 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9370 /* add to persistent jar */
9371 c
= soup_cookie_copy(cookie
);
9372 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9373 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9376 /* add to session jar */
9377 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9378 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9384 char file
[PATH_MAX
];
9386 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9387 "soup_cookie_jar_add_cookie");
9388 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9389 "soup_cookie_jar_delete_cookie");
9391 if (cookies_enabled
== 0)
9395 * the following code is intricate due to overriding several libsoup
9397 * do not alter order of these operations.
9400 /* rejected cookies */
9401 if (save_rejected_cookies
)
9402 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9405 /* persistent cookies */
9406 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9407 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9409 /* session cookies */
9410 s_cookiejar
= soup_cookie_jar_new();
9411 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9412 cookie_policy
, (void *)NULL
);
9415 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9419 setup_proxy(char *uri
)
9424 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9425 soup_uri_free(proxy_uri
);
9429 if (http_proxy
!= uri
) {
9436 http_proxy
= g_strdup(uri
);
9437 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9438 suri
= soup_uri_new(http_proxy
);
9439 if (!(suri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(suri
)))
9440 g_object_set(session
, "proxy-uri", proxy_uri
,
9443 soup_uri_free(suri
);
9448 set_http_proxy(char *proxy
)
9455 /* see if we need to clear it instead */
9456 if (strlen(proxy
) == 0) {
9461 uri
= soup_uri_new(proxy
);
9462 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9473 send_cmd_to_socket(char *cmd
)
9476 struct sockaddr_un sa
;
9478 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9479 warnx("%s: socket", __func__
);
9483 sa
.sun_family
= AF_UNIX
;
9484 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9485 work_dir
, XT_SOCKET_FILE
);
9488 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9489 warnx("%s: connect", __func__
);
9493 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9494 warnx("%s: send", __func__
);
9505 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9508 char str
[XT_MAX_URL_LENGTH
];
9509 socklen_t t
= sizeof(struct sockaddr_un
);
9510 struct sockaddr_un sa
;
9515 gint fd
= g_io_channel_unix_get_fd(source
);
9517 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9522 if (getpeereid(s
, &uid
, &gid
) == -1) {
9526 if (uid
!= getuid() || gid
!= getgid()) {
9527 warnx("unauthorized user");
9533 warnx("not a valid user");
9537 n
= recv(s
, str
, sizeof(str
), 0);
9541 tt
= TAILQ_LAST(&tabs
, tab_list
);
9542 cmd_execute(tt
, str
);
9550 struct sockaddr_un sa
;
9552 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9553 warn("is_running: socket");
9557 sa
.sun_family
= AF_UNIX
;
9558 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9559 work_dir
, XT_SOCKET_FILE
);
9562 /* connect to see if there is a listener */
9563 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9564 rv
= 0; /* not running */
9566 rv
= 1; /* already running */
9577 struct sockaddr_un sa
;
9579 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9580 warn("build_socket: socket");
9584 sa
.sun_family
= AF_UNIX
;
9585 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9586 work_dir
, XT_SOCKET_FILE
);
9589 /* connect to see if there is a listener */
9590 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9591 /* no listener so we will */
9592 unlink(sa
.sun_path
);
9594 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9595 warn("build_socket: bind");
9599 if (listen(s
, 1) == -1) {
9600 warn("build_socket: listen");
9613 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9614 GtkTreeIter
*iter
, struct tab
*t
)
9618 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9626 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9627 GtkTreeIter
*iter
, struct tab
*t
)
9631 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9632 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9633 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9640 completion_add_uri(const gchar
*uri
)
9644 /* add uri to list_store */
9645 gtk_list_store_append(completion_model
, &iter
);
9646 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9650 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9651 GtkTreeIter
*iter
, gpointer user_data
)
9654 gboolean match
= FALSE
;
9656 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9662 match
= match_uri(value
, key
);
9669 completion_add(struct tab
*t
)
9671 /* enable completion for tab */
9672 t
->completion
= gtk_entry_completion_new();
9673 gtk_entry_completion_set_text_column(t
->completion
, 0);
9674 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9675 gtk_entry_completion_set_model(t
->completion
,
9676 GTK_TREE_MODEL(completion_model
));
9677 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9679 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9680 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9681 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9682 G_CALLBACK(completion_select_cb
), t
);
9683 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9684 G_CALLBACK(completion_hover_cb
), t
);
9692 if (stat(dir
, &sb
)) {
9693 if (mkdir(dir
, S_IRWXU
) == -1)
9694 err(1, "mkdir %s", dir
);
9696 err(1, "stat %s", dir
);
9698 if (S_ISDIR(sb
.st_mode
) == 0)
9699 errx(1, "%s not a dir", dir
);
9700 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9701 warnx("fixing invalid permissions on %s", dir
);
9702 if (chmod(dir
, S_IRWXU
) == -1)
9703 err(1, "chmod %s", dir
);
9711 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9717 main(int argc
, char *argv
[])
9720 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9721 char conf
[PATH_MAX
] = { '\0' };
9722 char file
[PATH_MAX
];
9723 char *env_proxy
= NULL
;
9727 struct sigaction sact
;
9728 GIOChannel
*channel
;
9733 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9737 RB_INIT(&downloads
);
9741 TAILQ_INIT(&aliases
);
9746 /* fiddle with ulimits */
9747 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9750 /* just use them all */
9751 rlp
.rlim_cur
= rlp
.rlim_max
;
9752 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9754 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9756 else if (rlp
.rlim_cur
<= 256)
9757 startpage_add("%s requires at least 256 file "
9758 "descriptors, currently it has up to %d available",
9759 __progname
, rlp
.rlim_cur
);
9762 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9771 errx(0 , "Version: %s", version
);
9774 strlcpy(conf
, optarg
, sizeof(conf
));
9777 strlcpy(named_session
, optarg
, sizeof(named_session
));
9798 gnutls_global_init();
9800 /* generate session keys for xtp pages */
9801 generate_xtp_session_key(&dl_session_key
);
9802 generate_xtp_session_key(&hl_session_key
);
9803 generate_xtp_session_key(&cl_session_key
);
9804 generate_xtp_session_key(&fl_session_key
);
9807 if (!g_thread_supported()) {
9808 g_thread_init(NULL
);
9810 gdk_threads_enter();
9812 gtk_init(&argc
, &argv
);
9815 bzero(&sact
, sizeof(sact
));
9816 sigemptyset(&sact
.sa_mask
);
9817 sact
.sa_handler
= sigchild
;
9818 sact
.sa_flags
= SA_NOCLDSTOP
;
9819 sigaction(SIGCHLD
, &sact
, NULL
);
9821 /* set download dir */
9822 pwd
= getpwuid(getuid());
9824 errx(1, "invalid user %d", getuid());
9825 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9827 /* compile buffer command regexes */
9830 /* set default string settings */
9831 home
= g_strdup("https://www.cyphertite.com");
9832 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9833 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9834 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9835 cmd_font_name
= g_strdup("monospace normal 9");
9836 oops_font_name
= g_strdup("monospace normal 9");
9837 statusbar_font_name
= g_strdup("monospace normal 9");
9838 tabbar_font_name
= g_strdup("monospace normal 9");
9839 statusbar_elems
= g_strdup("BP");
9841 /* read config file */
9842 if (strlen(conf
) == 0)
9843 snprintf(conf
, sizeof conf
, "%s/.%s",
9844 pwd
->pw_dir
, XT_CONF_FILE
);
9845 config_parse(conf
, 0);
9848 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9849 oops_font
= pango_font_description_from_string(oops_font_name
);
9850 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9851 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9853 /* working directory */
9854 if (strlen(work_dir
) == 0)
9855 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9856 pwd
->pw_dir
, XT_DIR
);
9859 /* icon cache dir */
9860 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9864 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9868 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9869 work_dir
, XT_SESSIONS_DIR
);
9870 xxx_dir(sessions_dir
);
9872 /* runtime settings that can override config file */
9873 if (runtime_settings
[0] != '\0')
9874 config_parse(runtime_settings
, 1);
9877 if (!strcmp(download_dir
, pwd
->pw_dir
))
9878 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9879 xxx_dir(download_dir
);
9881 /* favorites file */
9882 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9883 if (stat(file
, &sb
)) {
9884 warnx("favorites file doesn't exist, creating it");
9885 if ((f
= fopen(file
, "w")) == NULL
)
9886 err(1, "favorites");
9890 /* quickmarks file */
9891 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9892 if (stat(file
, &sb
)) {
9893 warnx("quickmarks file doesn't exist, creating it");
9894 if ((f
= fopen(file
, "w")) == NULL
)
9895 err(1, "quickmarks");
9900 session
= webkit_get_default_session();
9905 if (stat(ssl_ca_file
, &sb
)) {
9906 warnx("no CA file: %s", ssl_ca_file
);
9907 g_free(ssl_ca_file
);
9910 g_object_set(session
,
9911 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9912 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9916 /* guess_search regex */
9917 if (url_regex
== NULL
)
9918 url_regex
= g_strdup(XT_URL_REGEX
);
9920 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
9921 startpage_add("invalid url regex %s", url_regex
);
9924 env_proxy
= getenv("http_proxy");
9926 setup_proxy(env_proxy
);
9928 setup_proxy(http_proxy
);
9931 send_cmd_to_socket(argv
[0]);
9935 /* set some connection parameters */
9936 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9937 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9940 /* see if there is already an xxxterm running */
9941 if (single_instance
&& is_running()) {
9943 warnx("already running");
9948 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9949 send_cmd_to_socket(cmd
);
9959 /* uri completion */
9960 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9963 buffers_store
= gtk_list_store_new
9964 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9970 notebook_tab_set_visibility();
9972 if (save_global_history
)
9973 restore_global_history();
9975 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9976 restore_saved_tabs();
9978 a
.s
= named_session
;
9979 a
.i
= XT_SES_DONOTHING
;
9980 open_tabs(NULL
, &a
);
9983 /* see if we have an exception */
9984 if (!TAILQ_EMPTY(&spl
)) {
9985 create_new_tab("about:startpage", NULL
, focus
, -1);
9990 create_new_tab(argv
[0], NULL
, focus
, -1);
9997 if (TAILQ_EMPTY(&tabs
))
9998 create_new_tab(home
, NULL
, 1, -1);
10001 if ((s
= build_socket()) != -1) {
10002 channel
= g_io_channel_unix_new(s
);
10003 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10008 if (!g_thread_supported()) {
10009 gdk_threads_leave();
10012 gnutls_global_deinit();