3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
8 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * create privacy browsing
26 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #elif defined(__FreeBSD__)
50 #include "freebsd/util.h"
56 #include <sys/queue.h>
57 #include <sys/resource.h>
58 #include <sys/socket.h>
64 #include <gdk/gdkkeysyms.h>
66 #if GTK_CHECK_VERSION(3,0,0)
67 /* we still use GDK_* instead of GDK_KEY_* */
68 #include <gdk/gdkkeysyms-compat.h>
71 #include <webkit/webkit.h>
72 #include <libsoup/soup.h>
73 #include <gnutls/gnutls.h>
74 #include <JavaScriptCore/JavaScript.h>
75 #include <gnutls/x509.h>
77 #include "javascript.h"
80 javascript.h borrowed from vimprobable2 under the following license:
82 Copyright (c) 2009 Leon Winter
83 Copyright (c) 2009 Hannes Schueller
84 Copyright (c) 2009 Matto Fransen
86 Permission is hereby granted, free of charge, to any person obtaining a copy
87 of this software and associated documentation files (the "Software"), to deal
88 in the Software without restriction, including without limitation the rights
89 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
90 copies of the Software, and to permit persons to whom the Software is
91 furnished to do so, subject to the following conditions:
93 The above copyright notice and this permission notice shall be included in
94 all copies or substantial portions of the Software.
96 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
97 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
98 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
99 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
100 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
101 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
105 static char *version
= "$xxxterm$";
107 /* hooked functions */
108 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
109 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
114 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
115 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
116 #define XT_D_MOVE 0x0001
117 #define XT_D_KEY 0x0002
118 #define XT_D_TAB 0x0004
119 #define XT_D_URL 0x0008
120 #define XT_D_CMD 0x0010
121 #define XT_D_NAV 0x0020
122 #define XT_D_DOWNLOAD 0x0040
123 #define XT_D_CONFIG 0x0080
124 #define XT_D_JS 0x0100
125 #define XT_D_FAVORITE 0x0200
126 #define XT_D_PRINTING 0x0400
127 #define XT_D_COOKIE 0x0800
128 #define XT_D_KEYBINDING 0x1000
129 #define XT_D_CLIP 0x2000
130 #define XT_D_BUFFERCMD 0x4000
131 u_int32_t swm_debug
= 0
149 #define DPRINTF(x...)
150 #define DNPRINTF(n,x...)
153 #define LENGTH(x) (sizeof x / sizeof x[0])
154 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
155 ~(GDK_BUTTON1_MASK) & \
156 ~(GDK_BUTTON2_MASK) & \
157 ~(GDK_BUTTON3_MASK) & \
158 ~(GDK_BUTTON4_MASK) & \
161 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
172 TAILQ_ENTRY(tab
) entry
;
174 GtkWidget
*tab_content
;
183 GtkWidget
*uri_entry
;
184 GtkWidget
*search_entry
;
186 GtkWidget
*browser_win
;
187 GtkWidget
*statusbar_box
;
189 GtkWidget
*statusbar
;
190 GtkWidget
*buffercmd
;
201 GtkWidget
*js_toggle
;
202 GtkEntryCompletion
*completion
;
206 WebKitWebHistoryItem
*item
;
207 WebKitWebBackForwardList
*bfl
;
210 WebKitNetworkRequest
*icon_request
;
211 WebKitDownload
*icon_download
;
212 gchar
*icon_dest_uri
;
214 /* adjustments for browser */
217 GtkAdjustment
*adjust_h
;
218 GtkAdjustment
*adjust_v
;
224 int xtp_meaning
; /* identifies dls/favorites */
226 int popup
; /* 1 if cmd_entry has popup visible */
231 #define XT_HINT_NONE (0)
232 #define XT_HINT_NUMERICAL (1)
233 #define XT_HINT_ALPHANUM (2)
237 /* custom stylesheet */
247 WebKitWebSettings
*settings
;
251 double mark
[XT_NOMARKS
];
253 TAILQ_HEAD(tab_list
, tab
);
256 RB_ENTRY(history
) entry
;
260 RB_HEAD(history_list
, history
);
263 RB_ENTRY(download
) entry
;
265 WebKitDownload
*download
;
268 RB_HEAD(download_list
, download
);
271 RB_ENTRY(domain
) entry
;
273 int handy
; /* app use */
275 RB_HEAD(domain_list
, domain
);
278 TAILQ_ENTRY(undo
) entry
;
281 int back
; /* Keeps track of how many back
282 * history items there are. */
284 TAILQ_HEAD(undo_tailq
, undo
);
288 TAILQ_ENTRY(sp
) entry
;
290 TAILQ_HEAD(sp_list
, sp
);
292 struct command_entry
{
294 TAILQ_ENTRY(command_entry
) entry
;
296 TAILQ_HEAD(command_list
, command_entry
);
298 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
299 int next_download_id
= 1;
308 #define XT_NAME ("XXXTerm")
309 #define XT_DIR (".xxxterm")
310 #define XT_CACHE_DIR ("cache")
311 #define XT_CERT_DIR ("certs/")
312 #define XT_SESSIONS_DIR ("sessions/")
313 #define XT_CONF_FILE ("xxxterm.conf")
314 #define XT_FAVS_FILE ("favorites")
315 #define XT_QMARKS_FILE ("quickmarks")
316 #define XT_SAVED_TABS_FILE ("main_session")
317 #define XT_RESTART_TABS_FILE ("restart_tabs")
318 #define XT_SOCKET_FILE ("socket")
319 #define XT_HISTORY_FILE ("history")
320 #define XT_REJECT_FILE ("rejected.txt")
321 #define XT_COOKIE_FILE ("cookies.txt")
322 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
323 #define XT_SEARCH_FILE ("search_history")
324 #define XT_COMMAND_FILE ("command_history")
325 #define XT_CB_HANDLED (TRUE)
326 #define XT_CB_PASSTHROUGH (FALSE)
327 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
328 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
329 #define XT_DLMAN_REFRESH "10"
330 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
331 "td{overflow: hidden;" \
332 " padding: 2px 2px 2px 2px;" \
333 " border: 1px solid black;" \
334 " vertical-align:top;" \
335 " word-wrap: break-word}\n" \
336 "tr:hover{background: #ffff99}\n" \
337 "th{background-color: #cccccc;" \
338 " border: 1px solid black}\n" \
339 "table{width: 100%%;" \
340 " border: 1px black solid;" \
341 " border-collapse:collapse}\n" \
343 "border: 1px solid black;" \
346 ".progress-inner{float: left;" \
348 " background: green}\n" \
349 ".dlstatus{font-size: small;" \
350 " text-align: center}\n" \
352 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
353 #define XT_MAX_UNDO_CLOSE_TAB (32)
354 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
355 #define XT_PRINT_EXTRA_MARGIN 10
356 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
357 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
360 #define XT_COLOR_RED "#cc0000"
361 #define XT_COLOR_YELLOW "#ffff66"
362 #define XT_COLOR_BLUE "lightblue"
363 #define XT_COLOR_GREEN "#99ff66"
364 #define XT_COLOR_WHITE "white"
365 #define XT_COLOR_BLACK "black"
367 #define XT_COLOR_CT_BACKGROUND "#000000"
368 #define XT_COLOR_CT_INACTIVE "#dddddd"
369 #define XT_COLOR_CT_ACTIVE "#bbbb00"
370 #define XT_COLOR_CT_SEPARATOR "#555555"
372 #define XT_COLOR_SB_SEPARATOR "#555555"
374 #define XT_PROTO_DELIM "://"
377 * xxxterm "protocol" (xtp)
378 * We use this for managing stuff like downloads and favorites. They
379 * make magical HTML pages in memory which have xxxt:// links in order
380 * to communicate with xxxterm's internals. These links take the format:
381 * xxxt://class/session_key/action/arg
383 * Don't begin xtp class/actions as 0. atoi returns that on error.
385 * Typically we have not put addition of items in this framework, as
386 * adding items is either done via an ex-command or via a keybinding instead.
389 #define XT_XTP_STR "xxxt://"
391 /* XTP classes (xxxt://<class>) */
392 #define XT_XTP_INVALID 0 /* invalid */
393 #define XT_XTP_DL 1 /* downloads */
394 #define XT_XTP_HL 2 /* history */
395 #define XT_XTP_CL 3 /* cookies */
396 #define XT_XTP_FL 4 /* favorites */
398 /* XTP download actions */
399 #define XT_XTP_DL_LIST 1
400 #define XT_XTP_DL_CANCEL 2
401 #define XT_XTP_DL_REMOVE 3
403 /* XTP history actions */
404 #define XT_XTP_HL_LIST 1
405 #define XT_XTP_HL_REMOVE 2
407 /* XTP cookie actions */
408 #define XT_XTP_CL_LIST 1
409 #define XT_XTP_CL_REMOVE 2
411 /* XTP cookie actions */
412 #define XT_XTP_FL_LIST 1
413 #define XT_XTP_FL_REMOVE 2
416 #define XT_MOVE_INVALID (0)
417 #define XT_MOVE_DOWN (1)
418 #define XT_MOVE_UP (2)
419 #define XT_MOVE_BOTTOM (3)
420 #define XT_MOVE_TOP (4)
421 #define XT_MOVE_PAGEDOWN (5)
422 #define XT_MOVE_PAGEUP (6)
423 #define XT_MOVE_HALFDOWN (7)
424 #define XT_MOVE_HALFUP (8)
425 #define XT_MOVE_LEFT (9)
426 #define XT_MOVE_FARLEFT (10)
427 #define XT_MOVE_RIGHT (11)
428 #define XT_MOVE_FARRIGHT (12)
429 #define XT_MOVE_PERCENT (13)
431 #define XT_QMARK_SET (0)
432 #define XT_QMARK_OPEN (1)
433 #define XT_QMARK_TAB (2)
435 #define XT_MARK_SET (0)
436 #define XT_MARK_GOTO (1)
438 #define XT_TAB_LAST (-4)
439 #define XT_TAB_FIRST (-3)
440 #define XT_TAB_PREV (-2)
441 #define XT_TAB_NEXT (-1)
442 #define XT_TAB_INVALID (0)
443 #define XT_TAB_NEW (1)
444 #define XT_TAB_DELETE (2)
445 #define XT_TAB_DELQUIT (3)
446 #define XT_TAB_OPEN (4)
447 #define XT_TAB_UNDO_CLOSE (5)
448 #define XT_TAB_SHOW (6)
449 #define XT_TAB_HIDE (7)
450 #define XT_TAB_NEXTSTYLE (8)
452 #define XT_NAV_INVALID (0)
453 #define XT_NAV_BACK (1)
454 #define XT_NAV_FORWARD (2)
455 #define XT_NAV_RELOAD (3)
457 #define XT_FOCUS_INVALID (0)
458 #define XT_FOCUS_URI (1)
459 #define XT_FOCUS_SEARCH (2)
461 #define XT_SEARCH_INVALID (0)
462 #define XT_SEARCH_NEXT (1)
463 #define XT_SEARCH_PREV (2)
465 #define XT_PASTE_CURRENT_TAB (0)
466 #define XT_PASTE_NEW_TAB (1)
468 #define XT_ZOOM_IN (-1)
469 #define XT_ZOOM_OUT (-2)
470 #define XT_ZOOM_NORMAL (100)
472 #define XT_URL_SHOW (1)
473 #define XT_URL_HIDE (2)
475 #define XT_WL_TOGGLE (1<<0)
476 #define XT_WL_ENABLE (1<<1)
477 #define XT_WL_DISABLE (1<<2)
478 #define XT_WL_FQDN (1<<3) /* default */
479 #define XT_WL_TOPLEVEL (1<<4)
480 #define XT_WL_PERSISTENT (1<<5)
481 #define XT_WL_SESSION (1<<6)
482 #define XT_WL_RELOAD (1<<7)
484 #define XT_SHOW (1<<7)
485 #define XT_DELETE (1<<8)
486 #define XT_SAVE (1<<9)
487 #define XT_OPEN (1<<10)
489 #define XT_CMD_OPEN (0)
490 #define XT_CMD_OPEN_CURRENT (1)
491 #define XT_CMD_TABNEW (2)
492 #define XT_CMD_TABNEW_CURRENT (3)
494 #define XT_STATUS_NOTHING (0)
495 #define XT_STATUS_LINK (1)
496 #define XT_STATUS_URI (2)
497 #define XT_STATUS_LOADING (3)
499 #define XT_SES_DONOTHING (0)
500 #define XT_SES_CLOSETABS (1)
502 #define XT_BM_NORMAL (0)
503 #define XT_BM_WHITELIST (1)
504 #define XT_BM_KIOSK (2)
506 #define XT_PREFIX (1<<0)
507 #define XT_USERARG (1<<1)
508 #define XT_URLARG (1<<2)
509 #define XT_INTARG (1<<3)
511 #define XT_TABS_NORMAL 0
512 #define XT_TABS_COMPACT 1
514 #define XT_BUFCMD_SZ (8)
522 TAILQ_ENTRY(mime_type
) entry
;
524 TAILQ_HEAD(mime_type_list
, mime_type
);
530 TAILQ_ENTRY(alias
) entry
;
532 TAILQ_HEAD(alias_list
, alias
);
534 /* settings that require restart */
535 int tabless
= 0; /* allow only 1 tab */
536 int enable_socket
= 0;
537 int single_instance
= 0; /* only allow one xxxterm to run */
538 int fancy_bar
= 1; /* fancy toolbar */
539 int browser_mode
= XT_BM_NORMAL
;
540 int enable_localstorage
= 0;
541 char *statusbar_elems
= NULL
;
543 /* runtime settings */
544 int show_tabs
= 1; /* show tabs on notebook */
545 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
546 int show_url
= 1; /* show url toolbar on notebook */
547 int show_statusbar
= 0; /* vimperator style status bar */
548 int ctrl_click_focus
= 0; /* ctrl click gets focus */
549 int cookies_enabled
= 1; /* enable cookies */
550 int read_only_cookies
= 0; /* enable to not write cookies */
551 int enable_scripts
= 1;
552 int enable_plugins
= 0;
553 gfloat default_zoom_level
= 1.0;
554 char default_script
[PATH_MAX
];
555 int window_height
= 768;
556 int window_width
= 1024;
557 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
558 int refresh_interval
= 10; /* download refresh interval */
559 int enable_cookie_whitelist
= 0;
560 int enable_js_whitelist
= 0;
561 int session_timeout
= 3600; /* cookie session timeout */
562 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
563 char *ssl_ca_file
= NULL
;
564 char *resource_dir
= NULL
;
565 gboolean ssl_strict_certs
= FALSE
;
566 int append_next
= 1; /* append tab after current tab */
568 char *search_string
= NULL
;
569 char *http_proxy
= NULL
;
570 char download_dir
[PATH_MAX
];
571 char runtime_settings
[PATH_MAX
]; /* override of settings */
572 int allow_volatile_cookies
= 0;
573 int save_global_history
= 0; /* save global history to disk */
574 char *user_agent
= NULL
;
575 int save_rejected_cookies
= 0;
576 int session_autosave
= 0;
577 int guess_search
= 0;
578 int dns_prefetch
= FALSE
;
579 gint max_connections
= 25;
580 gint max_host_connections
= 5;
581 gint enable_spell_checking
= 0;
582 char *spell_check_languages
= NULL
;
583 int xterm_workaround
= 0;
584 char *url_regex
= NULL
;
585 int history_autosave
= 0;
586 char search_file
[PATH_MAX
];
587 char command_file
[PATH_MAX
];
589 char *cmd_font_name
= NULL
;
590 char *oops_font_name
= NULL
;
591 char *statusbar_font_name
= NULL
;
592 char *tabbar_font_name
= NULL
;
593 PangoFontDescription
*cmd_font
;
594 PangoFontDescription
*oops_font
;
595 PangoFontDescription
*statusbar_font
;
596 PangoFontDescription
*tabbar_font
;
597 char *qmarks
[XT_NOMARKS
];
599 int btn_down
; /* M1 down in any wv */
600 regex_t url_re
; /* guess_search regex */
604 int set_browser_mode(struct settings
*, char *);
605 int set_cookie_policy(struct settings
*, char *);
606 int set_download_dir(struct settings
*, char *);
607 int set_default_script(struct settings
*, char *);
608 int set_runtime_dir(struct settings
*, char *);
609 int set_tab_style(struct settings
*, char *);
610 int set_work_dir(struct settings
*, char *);
611 int add_alias(struct settings
*, char *);
612 int add_mime_type(struct settings
*, char *);
613 int add_cookie_wl(struct settings
*, char *);
614 int add_js_wl(struct settings
*, char *);
615 int add_kb(struct settings
*, char *);
616 void button_set_stockid(GtkWidget
*, char *);
617 GtkWidget
* create_button(char *, char *, int);
619 char *get_browser_mode(struct settings
*);
620 char *get_cookie_policy(struct settings
*);
621 char *get_download_dir(struct settings
*);
622 char *get_default_script(struct settings
*);
623 char *get_runtime_dir(struct settings
*);
624 char *get_tab_style(struct settings
*);
625 char *get_work_dir(struct settings
*);
626 void startpage_add(const char *, ...);
628 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
629 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
630 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
631 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
632 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
634 void recalc_tabs(void);
635 void recolor_compact_tabs(void);
636 void set_current_tab(int page_num
);
637 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
638 void marks_clear(struct tab
*t
);
640 int set_http_proxy(char *);
643 int (*set
)(struct settings
*, char *);
644 char *(*get
)(struct settings
*);
645 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
648 struct special s_browser_mode
= {
654 struct special s_cookie
= {
660 struct special s_alias
= {
666 struct special s_mime
= {
672 struct special s_js
= {
678 struct special s_kb
= {
684 struct special s_cookie_wl
= {
690 struct special s_default_script
= {
696 struct special s_download_dir
= {
702 struct special s_work_dir
= {
708 struct special s_tab_style
= {
717 #define XT_S_INVALID (0)
720 #define XT_S_FLOAT (3)
722 #define XT_SF_RESTART (1<<0)
723 #define XT_SF_RUNTIME (1<<1)
728 int (*activate
)(char *);
730 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
731 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
732 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
733 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
734 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
735 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
736 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
737 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
738 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
739 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
740 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
741 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
742 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
743 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
744 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
745 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
746 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
747 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
748 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
749 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
750 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
751 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
752 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
753 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
754 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
755 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
756 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
757 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
758 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
759 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
760 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
761 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
762 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
763 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
764 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
765 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
766 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
767 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
768 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
769 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
770 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
771 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
772 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
773 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
774 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
775 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
776 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
779 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
780 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
781 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
782 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
784 /* runtime settings */
785 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
786 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
787 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
788 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
789 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
792 int about(struct tab
*, struct karg
*);
793 int blank(struct tab
*, struct karg
*);
794 int ca_cmd(struct tab
*, struct karg
*);
795 int cookie_show_wl(struct tab
*, struct karg
*);
796 int js_show_wl(struct tab
*, struct karg
*);
797 int help(struct tab
*, struct karg
*);
798 int set(struct tab
*, struct karg
*);
799 int stats(struct tab
*, struct karg
*);
800 int marco(struct tab
*, struct karg
*);
801 int startpage(struct tab
*, struct karg
*);
802 const char * marco_message(int *);
803 int xtp_page_cl(struct tab
*, struct karg
*);
804 int xtp_page_dl(struct tab
*, struct karg
*);
805 int xtp_page_fl(struct tab
*, struct karg
*);
806 int xtp_page_hl(struct tab
*, struct karg
*);
807 void xt_icon_from_file(struct tab
*, char *);
808 const gchar
*get_uri(struct tab
*);
809 const gchar
*get_title(struct tab
*, bool);
811 #define XT_URI_ABOUT ("about:")
812 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
813 #define XT_URI_ABOUT_ABOUT ("about")
814 #define XT_URI_ABOUT_BLANK ("blank")
815 #define XT_URI_ABOUT_CERTS ("certs")
816 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
817 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
818 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
819 #define XT_URI_ABOUT_FAVORITES ("favorites")
820 #define XT_URI_ABOUT_HELP ("help")
821 #define XT_URI_ABOUT_HISTORY ("history")
822 #define XT_URI_ABOUT_JSWL ("jswl")
823 #define XT_URI_ABOUT_SET ("set")
824 #define XT_URI_ABOUT_STATS ("stats")
825 #define XT_URI_ABOUT_MARCO ("marco")
826 #define XT_URI_ABOUT_STARTPAGE ("startpage")
830 int (*func
)(struct tab
*, struct karg
*);
832 { XT_URI_ABOUT_ABOUT
, about
},
833 { XT_URI_ABOUT_BLANK
, blank
},
834 { XT_URI_ABOUT_CERTS
, ca_cmd
},
835 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
836 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
837 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
838 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
839 { XT_URI_ABOUT_HELP
, help
},
840 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
841 { XT_URI_ABOUT_JSWL
, js_show_wl
},
842 { XT_URI_ABOUT_SET
, set
},
843 { XT_URI_ABOUT_STATS
, stats
},
844 { XT_URI_ABOUT_MARCO
, marco
},
845 { XT_URI_ABOUT_STARTPAGE
, startpage
},
848 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
849 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
850 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
851 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
852 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
853 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
854 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
857 extern char *__progname
;
860 GtkWidget
*main_window
;
861 GtkNotebook
*notebook
;
863 GtkWidget
*arrow
, *abtn
;
864 struct tab_list tabs
;
865 struct history_list hl
;
866 struct download_list downloads
;
867 struct domain_list c_wl
;
868 struct domain_list js_wl
;
869 struct undo_tailq undos
;
870 struct keybinding_list kbl
;
872 struct command_list chl
;
873 struct command_list shl
;
874 struct command_entry
*history_at
;
875 struct command_entry
*search_at
;
877 int updating_dl_tabs
= 0;
878 int updating_hl_tabs
= 0;
879 int updating_cl_tabs
= 0;
880 int updating_fl_tabs
= 0;
881 int cmd_history_count
= 0;
882 int search_history_count
= 0;
884 uint64_t blocked_cookies
= 0;
885 char named_session
[PATH_MAX
];
886 GtkListStore
*completion_model
;
887 GtkListStore
*buffers_store
;
889 void xxx_dir(char *);
890 int icon_size_map(int);
891 void completion_add(struct tab
*);
892 void completion_add_uri(const gchar
*);
893 void show_oops(struct tab
*, const char *, ...);
896 history_delete(struct command_list
*l
, int *counter
)
898 struct command_entry
*c
;
900 if (l
== NULL
|| counter
== NULL
)
903 c
= TAILQ_LAST(l
, command_list
);
907 TAILQ_REMOVE(l
, c
, entry
);
914 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
916 struct command_entry
*c
;
919 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
922 /* don't add the same line */
923 c
= TAILQ_FIRST(list
);
925 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
928 c
= g_malloc0(sizeof *c
);
929 c
->line
= g_strdup_printf(" %s", l
);
932 TAILQ_INSERT_HEAD(list
, c
, entry
);
935 history_delete(list
, counter
);
937 if (history_autosave
&& file
) {
938 f
= fopen(file
, "w");
940 show_oops(NULL
, "couldn't write history %s", file
);
944 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
946 fprintf(f
, "%s\n", c
->line
);
954 history_read(struct command_list
*list
, char *file
, int *counter
)
957 char *s
, line
[65536];
959 if (list
== NULL
|| file
== NULL
)
962 f
= fopen(file
, "r");
964 startpage_add("couldn't open history file %s", file
);
969 s
= fgets(line
, sizeof line
, f
);
970 if (s
== NULL
|| feof(f
) || ferror(f
))
972 if ((s
= strchr(line
, '\n')) == NULL
) {
973 startpage_add("invalid history file %s", file
);
979 history_add(list
, NULL
, line
+ 1, counter
);
987 /* marks and quickmarks array storage.
988 * first a-z, then A-Z, then 0-9 */
995 if (i
>= 0 && i
<= 'z' - 'a')
999 if (i
>= 0 && i
<= 'Z' - 'A')
1014 if (m
>= 'a' && m
<= 'z')
1015 return ret
+ m
- 'a';
1017 ret
+= 'z' - 'a' + 1;
1018 if (m
>= 'A' && m
<= 'Z')
1019 return ret
+ m
- 'A';
1021 ret
+= 'Z' - 'A' + 1;
1022 if (m
>= '0' && m
<= '9')
1023 return ret
+ m
- '0';
1032 int saved_errno
, status
;
1035 saved_errno
= errno
;
1037 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1041 if (errno
!= ECHILD
) {
1043 clog_warn("sigchild: waitpid:");
1049 if (WIFEXITED(status
)) {
1050 if (WEXITSTATUS(status
) != 0) {
1052 clog_warnx("sigchild: child exit status: %d",
1053 WEXITSTATUS(status));
1058 clog_warnx("sigchild: child is terminated abnormally");
1063 errno
= saved_errno
;
1067 is_g_object_setting(GObject
*o
, char *str
)
1069 guint n_props
= 0, i
;
1070 GParamSpec
**proplist
;
1072 if (! G_IS_OBJECT(o
))
1075 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1078 for (i
=0; i
< n_props
; i
++) {
1079 if (! strcmp(proplist
[i
]->name
, str
))
1086 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1090 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1092 "<title>%s</title>\n"
1101 addstyles
? XT_PAGE_STYLE
: "",
1110 * Display a web page from a HTML string in memory, rather than from a URL
1113 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1115 char file
[PATH_MAX
];
1118 /* we set this to indicate we want to manually do navaction */
1120 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1122 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1124 /* set t->xtp_meaning */
1125 for (i
= 0; i
< LENGTH(about_list
); i
++)
1126 if (!strcmp(title
, about_list
[i
].name
)) {
1131 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1132 #if GTK_CHECK_VERSION(2, 20, 0)
1133 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1134 gtk_widget_hide(t
->spinner
);
1136 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1137 xt_icon_from_file(t
, file
);
1142 get_current_tab(void)
1146 TAILQ_FOREACH(t
, &tabs
, entry
) {
1147 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1151 warnx("%s: no current tab", __func__
);
1157 set_status(struct tab
*t
, gchar
*s
, int status
)
1165 case XT_STATUS_LOADING
:
1166 type
= g_strdup_printf("Loading: %s", s
);
1169 case XT_STATUS_LINK
:
1170 type
= g_strdup_printf("Link: %s", s
);
1172 t
->status
= g_strdup(gtk_entry_get_text(
1173 GTK_ENTRY(t
->sbe
.statusbar
)));
1177 type
= g_strdup_printf("%s", s
);
1179 t
->status
= g_strdup(type
);
1183 t
->status
= g_strdup(s
);
1185 case XT_STATUS_NOTHING
:
1190 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1196 hide_cmd(struct tab
*t
)
1198 history_at
= NULL
; /* just in case */
1199 search_at
= NULL
; /* just in case */
1200 gtk_widget_hide(t
->cmd
);
1204 show_cmd(struct tab
*t
)
1208 gtk_widget_hide(t
->oops
);
1209 gtk_widget_show(t
->cmd
);
1213 hide_buffers(struct tab
*t
)
1215 gtk_widget_hide(t
->buffers
);
1216 gtk_list_store_clear(buffers_store
);
1226 sort_tabs_by_page_num(struct tab
***stabs
)
1231 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1233 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1235 TAILQ_FOREACH(t
, &tabs
, entry
)
1236 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1242 buffers_make_list(void)
1245 const gchar
*title
= NULL
;
1247 struct tab
**stabs
= NULL
;
1249 num_tabs
= sort_tabs_by_page_num(&stabs
);
1251 for (i
= 0; i
< num_tabs
; i
++)
1253 gtk_list_store_append(buffers_store
, &iter
);
1254 title
= get_title(stabs
[i
], FALSE
);
1255 gtk_list_store_set(buffers_store
, &iter
,
1256 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1266 show_buffers(struct tab
*t
)
1268 buffers_make_list();
1269 gtk_widget_show(t
->buffers
);
1270 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1274 toggle_buffers(struct tab
*t
)
1276 if (gtk_widget_get_visible(t
->buffers
))
1283 buffers(struct tab
*t
, struct karg
*args
)
1291 hide_oops(struct tab
*t
)
1293 gtk_widget_hide(t
->oops
);
1297 show_oops(struct tab
*at
, const char *fmt
, ...)
1301 struct tab
*t
= NULL
;
1307 if ((t
= get_current_tab()) == NULL
)
1313 if (vasprintf(&msg
, fmt
, ap
) == -1)
1314 errx(1, "show_oops failed");
1317 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1318 gtk_widget_hide(t
->cmd
);
1319 gtk_widget_show(t
->oops
);
1326 get_as_string(struct settings
*s
)
1337 warnx("get_as_string skip %s\n", s
->name
);
1338 } else if (s
->type
== XT_S_INT
)
1339 r
= g_strdup_printf("%d", *s
->ival
);
1340 else if (s
->type
== XT_S_STR
)
1341 r
= g_strdup(*s
->sval
);
1342 else if (s
->type
== XT_S_FLOAT
)
1343 r
= g_strdup_printf("%f", *s
->fval
);
1345 r
= g_strdup_printf("INVALID TYPE");
1351 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1356 for (i
= 0; i
< LENGTH(rs
); i
++) {
1357 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1358 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1360 s
= get_as_string(&rs
[i
]);
1361 cb(&rs
[i
], s
, cb_args
);
1368 set_browser_mode(struct settings
*s
, char *val
)
1370 if (!strcmp(val
, "whitelist")) {
1371 browser_mode
= XT_BM_WHITELIST
;
1372 allow_volatile_cookies
= 0;
1373 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1374 cookies_enabled
= 1;
1375 enable_cookie_whitelist
= 1;
1376 read_only_cookies
= 0;
1377 save_rejected_cookies
= 0;
1378 session_timeout
= 3600;
1380 enable_js_whitelist
= 1;
1381 enable_localstorage
= 0;
1382 } else if (!strcmp(val
, "normal")) {
1383 browser_mode
= XT_BM_NORMAL
;
1384 allow_volatile_cookies
= 0;
1385 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1386 cookies_enabled
= 1;
1387 enable_cookie_whitelist
= 0;
1388 read_only_cookies
= 0;
1389 save_rejected_cookies
= 0;
1390 session_timeout
= 3600;
1392 enable_js_whitelist
= 0;
1393 enable_localstorage
= 1;
1394 } else if (!strcmp(val
, "kiosk")) {
1395 browser_mode
= XT_BM_KIOSK
;
1396 allow_volatile_cookies
= 0;
1397 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1398 cookies_enabled
= 1;
1399 enable_cookie_whitelist
= 0;
1400 read_only_cookies
= 0;
1401 save_rejected_cookies
= 0;
1402 session_timeout
= 3600;
1404 enable_js_whitelist
= 0;
1405 enable_localstorage
= 1;
1415 get_browser_mode(struct settings
*s
)
1419 if (browser_mode
== XT_BM_WHITELIST
)
1420 r
= g_strdup("whitelist");
1421 else if (browser_mode
== XT_BM_NORMAL
)
1422 r
= g_strdup("normal");
1423 else if (browser_mode
== XT_BM_KIOSK
)
1424 r
= g_strdup("kiosk");
1432 set_cookie_policy(struct settings
*s
, char *val
)
1434 if (!strcmp(val
, "no3rdparty"))
1435 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1436 else if (!strcmp(val
, "accept"))
1437 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1438 else if (!strcmp(val
, "reject"))
1439 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1447 get_cookie_policy(struct settings
*s
)
1451 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1452 r
= g_strdup("no3rdparty");
1453 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1454 r
= g_strdup("accept");
1455 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1456 r
= g_strdup("reject");
1464 get_default_script(struct settings
*s
)
1466 if (default_script
[0] == '\0')
1468 return (g_strdup(default_script
));
1472 set_default_script(struct settings
*s
, char *val
)
1475 snprintf(default_script
, sizeof default_script
, "%s/%s",
1476 pwd
->pw_dir
, &val
[1]);
1478 strlcpy(default_script
, val
, sizeof default_script
);
1484 get_download_dir(struct settings
*s
)
1486 if (download_dir
[0] == '\0')
1488 return (g_strdup(download_dir
));
1492 set_download_dir(struct settings
*s
, char *val
)
1495 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1496 pwd
->pw_dir
, &val
[1]);
1498 strlcpy(download_dir
, val
, sizeof download_dir
);
1505 * We use these to prevent people putting xxxt:// URLs on
1506 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1508 #define XT_XTP_SES_KEY_SZ 8
1509 #define XT_XTP_SES_KEY_HEX_FMT \
1510 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1511 char *dl_session_key
; /* downloads */
1512 char *hl_session_key
; /* history list */
1513 char *cl_session_key
; /* cookie list */
1514 char *fl_session_key
; /* favorites list */
1516 char work_dir
[PATH_MAX
];
1517 char certs_dir
[PATH_MAX
];
1518 char cache_dir
[PATH_MAX
];
1519 char sessions_dir
[PATH_MAX
];
1520 char cookie_file
[PATH_MAX
];
1521 SoupURI
*proxy_uri
= NULL
;
1522 SoupSession
*session
;
1523 SoupCookieJar
*s_cookiejar
;
1524 SoupCookieJar
*p_cookiejar
;
1525 char rc_fname
[PATH_MAX
];
1527 struct mime_type_list mtl
;
1528 struct alias_list aliases
;
1531 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1532 void delete_tab(struct tab
*);
1533 void setzoom_webkit(struct tab
*, int);
1534 int run_script(struct tab
*, char *);
1535 int download_rb_cmp(struct download
*, struct download
*);
1536 gboolean
cmd_execute(struct tab
*t
, char *str
);
1539 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1541 return (strcmp(h1
->uri
, h2
->uri
));
1543 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1546 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1548 return (strcmp(d1
->d
, d2
->d
));
1550 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1553 get_work_dir(struct settings
*s
)
1555 if (work_dir
[0] == '\0')
1557 return (g_strdup(work_dir
));
1561 set_work_dir(struct settings
*s
, char *val
)
1564 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1565 pwd
->pw_dir
, &val
[1]);
1567 strlcpy(work_dir
, val
, sizeof work_dir
);
1573 get_tab_style(struct settings
*s
)
1575 if (tab_style
== XT_TABS_NORMAL
)
1576 return (g_strdup("normal"));
1578 return (g_strdup("compact"));
1582 set_tab_style(struct settings
*s
, char *val
)
1584 if (!strcmp(val
, "normal"))
1585 tab_style
= XT_TABS_NORMAL
;
1586 else if (!strcmp(val
, "compact"))
1587 tab_style
= XT_TABS_COMPACT
;
1595 * generate a session key to secure xtp commands.
1596 * pass in a ptr to the key in question and it will
1597 * be modified in place.
1600 generate_xtp_session_key(char **key
)
1602 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1608 /* make a new one */
1609 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1610 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1611 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1612 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1614 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1618 * validate a xtp session key.
1622 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1624 if (strcmp(trusted
, untrusted
) != 0) {
1625 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1634 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1636 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1638 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1640 struct valid_url_types
{
1651 valid_url_type(char *url
)
1655 for (i
= 0; i
< LENGTH(vut
); i
++)
1656 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1663 print_cookie(char *msg
, SoupCookie
*c
)
1669 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1670 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1671 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1672 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1673 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1674 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1675 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1676 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1677 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1678 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1682 walk_alias(struct settings
*s
,
1683 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1688 if (s
== NULL
|| cb
== NULL
) {
1689 show_oops(NULL
, "walk_alias invalid parameters");
1693 TAILQ_FOREACH(a
, &aliases
, entry
) {
1694 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1695 cb(s
, str
, cb_args
);
1701 match_alias(char *url_in
)
1705 char *url_out
= NULL
, *search
, *enc_arg
;
1707 search
= g_strdup(url_in
);
1709 if (strsep(&arg
, " \t") == NULL
) {
1710 show_oops(NULL
, "match_alias: NULL URL");
1714 TAILQ_FOREACH(a
, &aliases
, entry
) {
1715 if (!strcmp(search
, a
->a_name
))
1720 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1723 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1724 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1727 url_out
= g_strdup_printf(a
->a_uri
, "");
1735 guess_url_type(char *url_in
)
1738 char *url_out
= NULL
, *enc_search
= NULL
;
1741 /* substitute aliases */
1742 url_out
= match_alias(url_in
);
1743 if (url_out
!= NULL
)
1746 /* see if we are an about page */
1747 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1748 for (i
= 0; i
< LENGTH(about_list
); i
++)
1749 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1750 about_list
[i
].name
)) {
1751 url_out
= g_strdup(url_in
);
1755 if (guess_search
&& url_regex
&&
1756 !(g_str_has_prefix(url_in
, "http://") ||
1757 g_str_has_prefix(url_in
, "https://"))) {
1758 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1759 /* invalid URI so search instead */
1760 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1761 url_out
= g_strdup_printf(search_string
, enc_search
);
1767 /* XXX not sure about this heuristic */
1768 if (stat(url_in
, &sb
) == 0)
1769 url_out
= g_strdup_printf("file://%s", url_in
);
1771 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1773 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1779 load_uri(struct tab
*t
, gchar
*uri
)
1782 gchar
*newuri
= NULL
;
1788 /* Strip leading spaces. */
1789 while (*uri
&& isspace(*uri
))
1792 if (strlen(uri
) == 0) {
1797 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1799 if (valid_url_type(uri
)) {
1800 newuri
= guess_url_type(uri
);
1804 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1805 for (i
= 0; i
< LENGTH(about_list
); i
++)
1806 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1807 bzero(&args
, sizeof args
);
1808 about_list
[i
].func(t
, &args
);
1809 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1813 show_oops(t
, "invalid about page");
1817 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1819 webkit_web_view_load_uri(t
->wv
, uri
);
1826 get_uri(struct tab
*t
)
1828 const gchar
*uri
= NULL
;
1830 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1832 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1833 uri
= webkit_web_view_get_uri(t
->wv
);
1835 /* use tmp_uri to make sure it is g_freed */
1838 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1839 about_list
[t
->xtp_meaning
].name
);
1846 get_title(struct tab
*t
, bool window
)
1848 const gchar
*set
= NULL
, *title
= NULL
;
1849 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1851 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1852 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1855 title
= webkit_web_view_get_title(t
->wv
);
1856 if ((set
= title
? title
: get_uri(t
)))
1860 set
= window
? XT_NAME
: "(untitled)";
1866 add_alias(struct settings
*s
, char *line
)
1869 struct alias
*a
= NULL
;
1871 if (s
== NULL
|| line
== NULL
) {
1872 show_oops(NULL
, "add_alias invalid parameters");
1877 a
= g_malloc(sizeof(*a
));
1879 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1880 show_oops(NULL
, "add_alias: incomplete alias definition");
1883 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1884 show_oops(NULL
, "add_alias: invalid alias definition");
1888 a
->a_name
= g_strdup(alias
);
1889 a
->a_uri
= g_strdup(l
);
1891 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1893 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1903 add_mime_type(struct settings
*s
, char *line
)
1907 struct mime_type
*m
= NULL
;
1908 int downloadfirst
= 0;
1910 /* XXX this could be smarter */
1912 if (line
== NULL
|| strlen(line
) == 0) {
1913 show_oops(NULL
, "add_mime_type invalid parameters");
1922 m
= g_malloc(sizeof(*m
));
1924 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1925 show_oops(NULL
, "add_mime_type: invalid mime_type");
1928 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1929 mime_type
[strlen(mime_type
) - 1] = '\0';
1934 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1935 show_oops(NULL
, "add_mime_type: invalid mime_type");
1939 m
->mt_type
= g_strdup(mime_type
);
1940 m
->mt_action
= g_strdup(l
);
1941 m
->mt_download
= downloadfirst
;
1943 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1944 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1946 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1956 find_mime_type(char *mime_type
)
1958 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1960 TAILQ_FOREACH(m
, &mtl
, entry
) {
1961 if (m
->mt_default
&&
1962 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1965 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1978 walk_mime_type(struct settings
*s
,
1979 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1981 struct mime_type
*m
;
1984 if (s
== NULL
|| cb
== NULL
) {
1985 show_oops(NULL
, "walk_mime_type invalid parameters");
1989 TAILQ_FOREACH(m
, &mtl
, entry
) {
1990 str
= g_strdup_printf("%s%s --> %s",
1992 m
->mt_default
? "*" : "",
1994 cb(s
, str
, cb_args
);
2000 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2006 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2009 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2011 /* treat *.moo.com the same as .moo.com */
2012 if (str
[0] == '*' && str
[1] == '.')
2014 else if (str
[0] == '.')
2019 /* slice off port number */
2020 p
= g_strrstr(str
, ":");
2024 d
= g_malloc(sizeof *d
);
2026 d
->d
= g_strdup_printf(".%s", str
);
2028 d
->d
= g_strdup(str
);
2031 if (RB_INSERT(domain_list
, wl
, d
))
2034 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2045 add_cookie_wl(struct settings
*s
, char *entry
)
2047 wl_add(entry
, &c_wl
, 1);
2052 walk_cookie_wl(struct settings
*s
,
2053 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2057 if (s
== NULL
|| cb
== NULL
) {
2058 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2062 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2063 cb(s
, d
->d
, cb_args
);
2067 walk_js_wl(struct settings
*s
,
2068 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2072 if (s
== NULL
|| cb
== NULL
) {
2073 show_oops(NULL
, "walk_js_wl invalid parameters");
2077 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2078 cb(s
, d
->d
, cb_args
);
2082 add_js_wl(struct settings
*s
, char *entry
)
2084 wl_add(entry
, &js_wl
, 1 /* persistent */);
2089 wl_find(const gchar
*search
, struct domain_list
*wl
)
2092 struct domain
*d
= NULL
, dfind
;
2095 if (search
== NULL
|| wl
== NULL
)
2097 if (strlen(search
) < 2)
2100 if (search
[0] != '.')
2101 s
= g_strdup_printf(".%s", search
);
2103 s
= g_strdup(search
);
2105 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2108 d
= RB_FIND(domain_list
, wl
, &dfind
);
2122 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2128 if (s
== NULL
|| wl
== NULL
)
2131 if (!strncmp(s
, "http://", strlen("http://")))
2132 s
= &s
[strlen("http://")];
2133 else if (!strncmp(s
, "https://", strlen("https://")))
2134 s
= &s
[strlen("https://")];
2139 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2140 /* chop string at first slash */
2141 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2144 r
= wl_find(ss
, wl
);
2153 settings_add(char *var
, char *val
)
2160 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2161 if (strcmp(var
, rs
[i
].name
))
2165 if (rs
[i
].s
->set(&rs
[i
], val
))
2166 errx(1, "invalid value for %s: %s", var
, val
);
2170 switch (rs
[i
].type
) {
2179 errx(1, "invalid sval for %s",
2193 errx(1, "invalid type for %s", var
);
2202 config_parse(char *filename
, int runtime
)
2205 char *line
, *cp
, *var
, *val
;
2206 size_t len
, lineno
= 0;
2208 char file
[PATH_MAX
];
2211 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2213 if (filename
== NULL
)
2216 if (runtime
&& runtime_settings
[0] != '\0') {
2217 snprintf(file
, sizeof file
, "%s/%s",
2218 work_dir
, runtime_settings
);
2219 if (stat(file
, &sb
)) {
2220 warnx("runtime file doesn't exist, creating it");
2221 if ((f
= fopen(file
, "w")) == NULL
)
2223 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2227 strlcpy(file
, filename
, sizeof file
);
2229 if ((config
= fopen(file
, "r")) == NULL
) {
2230 warn("config_parse: cannot open %s", filename
);
2235 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2236 if (feof(config
) || ferror(config
))
2240 cp
+= (long)strspn(cp
, WS
);
2241 if (cp
[0] == '\0') {
2247 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2248 startpage_add("invalid configuration file entry: %s",
2251 cp
+= (long)strspn(cp
, WS
);
2253 if ((val
= strsep(&cp
, "\0")) == NULL
)
2256 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2257 handled
= settings_add(var
, val
);
2259 startpage_add("invalid configuration file entry: %s=%s",
2269 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2275 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2279 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2282 JSStringGetUTF8CString(jsref
, s
, l
);
2283 JSStringRelease(jsref
);
2289 disable_hints(struct tab
*t
)
2291 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2292 bzero(t
->hint_num
, sizeof t
->hint_num
);
2293 run_script(t
, "vimprobable_clear()");
2295 t
->hint_mode
= XT_HINT_NONE
;
2299 enable_hints(struct tab
*t
)
2301 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2302 run_script(t
, "vimprobable_show_hints()");
2304 t
->hint_mode
= XT_HINT_NONE
;
2307 #define XT_JS_OPEN ("open;")
2308 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2309 #define XT_JS_FIRE ("fire;")
2310 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2311 #define XT_JS_FOUND ("found;")
2312 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2315 run_script(struct tab
*t
, char *s
)
2317 JSGlobalContextRef ctx
;
2318 WebKitWebFrame
*frame
;
2320 JSValueRef val
, exception
;
2323 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2324 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2326 frame
= webkit_web_view_get_main_frame(t
->wv
);
2327 ctx
= webkit_web_frame_get_global_context(frame
);
2329 str
= JSStringCreateWithUTF8CString(s
);
2330 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2331 NULL
, 0, &exception
);
2332 JSStringRelease(str
);
2334 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2336 es
= js_ref_to_string(ctx
, exception
);
2337 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2341 es
= js_ref_to_string(ctx
, val
);
2342 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2344 /* handle return value right here */
2345 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2348 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2351 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2352 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2353 &es
[XT_JS_FIRE_LEN
]);
2358 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2359 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2370 hint(struct tab
*t
, struct karg
*args
)
2373 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2375 if (t
->hints_on
== 0)
2384 apply_style(struct tab
*t
)
2386 g_object_set(G_OBJECT(t
->settings
),
2387 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2391 userstyle(struct tab
*t
, struct karg
*args
)
2393 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2397 g_object_set(G_OBJECT(t
->settings
),
2398 "user-stylesheet-uri", NULL
, (char *)NULL
);
2407 * Doesn't work fully, due to the following bug:
2408 * https://bugs.webkit.org/show_bug.cgi?id=51747
2411 restore_global_history(void)
2413 char file
[PATH_MAX
];
2418 const char delim
[3] = {'\\', '\\', '\0'};
2420 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2422 if ((f
= fopen(file
, "r")) == NULL
) {
2423 warnx("%s: fopen", __func__
);
2428 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2429 if (feof(f
) || ferror(f
))
2432 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2433 if (feof(f
) || ferror(f
)) {
2435 warnx("%s: broken history file\n", __func__
);
2439 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2440 webkit_web_history_item_new_with_data(uri
, title
);
2441 h
= g_malloc(sizeof(struct history
));
2442 h
->uri
= g_strdup(uri
);
2443 h
->title
= g_strdup(title
);
2444 RB_INSERT(history_list
, &hl
, h
);
2445 completion_add_uri(h
->uri
);
2447 warnx("%s: failed to restore history\n", __func__
);
2463 save_global_history_to_disk(struct tab
*t
)
2465 char file
[PATH_MAX
];
2469 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2471 if ((f
= fopen(file
, "w")) == NULL
) {
2472 show_oops(t
, "%s: global history file: %s",
2473 __func__
, strerror(errno
));
2477 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2478 if (h
->uri
&& h
->title
)
2479 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2488 quit(struct tab
*t
, struct karg
*args
)
2490 if (save_global_history
)
2491 save_global_history_to_disk(t
);
2499 open_tabs(struct tab
*t
, struct karg
*a
)
2501 char file
[PATH_MAX
];
2505 struct tab
*ti
, *tt
;
2510 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2511 if ((f
= fopen(file
, "r")) == NULL
)
2514 ti
= TAILQ_LAST(&tabs
, tab_list
);
2517 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2518 if (feof(f
) || ferror(f
))
2521 /* retrieve session name */
2522 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2523 strlcpy(named_session
,
2524 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2525 sizeof named_session
);
2529 if (uri
&& strlen(uri
))
2530 create_new_tab(uri
, NULL
, 1, -1);
2536 /* close open tabs */
2537 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2539 tt
= TAILQ_FIRST(&tabs
);
2559 restore_saved_tabs(void)
2561 char file
[PATH_MAX
];
2562 int unlink_file
= 0;
2567 snprintf(file
, sizeof file
, "%s/%s",
2568 sessions_dir
, XT_RESTART_TABS_FILE
);
2569 if (stat(file
, &sb
) == -1)
2570 a
.s
= XT_SAVED_TABS_FILE
;
2573 a
.s
= XT_RESTART_TABS_FILE
;
2576 a
.i
= XT_SES_DONOTHING
;
2577 rv
= open_tabs(NULL
, &a
);
2586 save_tabs(struct tab
*t
, struct karg
*a
)
2588 char file
[PATH_MAX
];
2590 int num_tabs
= 0, i
;
2591 struct tab
**stabs
= NULL
;
2596 snprintf(file
, sizeof file
, "%s/%s",
2597 sessions_dir
, named_session
);
2599 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2601 if ((f
= fopen(file
, "w")) == NULL
) {
2602 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2606 /* save session name */
2607 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2609 /* Save tabs, in the order they are arranged in the notebook. */
2610 num_tabs
= sort_tabs_by_page_num(&stabs
);
2612 for (i
= 0; i
< num_tabs
; i
++)
2614 if (get_uri(stabs
[i
]) != NULL
)
2615 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2616 else if (gtk_entry_get_text(GTK_ENTRY(
2617 stabs
[i
]->uri_entry
)))
2618 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2619 stabs
[i
]->uri_entry
)));
2624 /* try and make sure this gets to disk NOW. XXX Backup first? */
2625 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2626 show_oops(t
, "May not have managed to save session: %s",
2636 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2648 run_page_script(struct tab
*t
, struct karg
*args
)
2651 char *tmp
, script
[PATH_MAX
];
2653 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2654 if (tmp
[0] == '\0') {
2655 show_oops(t
, "no script specified");
2659 if ((uri
= get_uri(t
)) == NULL
) {
2660 show_oops(t
, "tab is empty, not running script");
2665 snprintf(script
, sizeof script
, "%s/%s",
2666 pwd
->pw_dir
, &tmp
[1]);
2668 strlcpy(script
, tmp
, sizeof script
);
2672 show_oops(t
, "can't fork to run script");
2682 execlp(script
, script
, uri
, (void *)NULL
);
2692 yank_uri(struct tab
*t
, struct karg
*args
)
2695 GtkClipboard
*clipboard
;
2697 if ((uri
= get_uri(t
)) == NULL
)
2700 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2701 gtk_clipboard_set_text(clipboard
, uri
, -1);
2707 paste_uri(struct tab
*t
, struct karg
*args
)
2709 GtkClipboard
*clipboard
;
2710 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2712 gchar
*p
= NULL
, *uri
;
2714 /* try primary clipboard first */
2715 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2716 p
= gtk_clipboard_wait_for_text(clipboard
);
2718 /* if it failed get whatever text is in cut_buffer0 */
2719 if (p
== NULL
&& xterm_workaround
)
2720 if (gdk_property_get(gdk_get_default_root_window(),
2722 gdk_atom_intern("STRING", FALSE
),
2724 1024 * 1024 /* picked out of my butt */,
2730 /* yes sir, we need to NUL the string */
2736 while (*uri
&& isspace(*uri
))
2738 if (strlen(uri
) == 0) {
2739 show_oops(t
, "empty paste buffer");
2742 if (guess_search
== 0 && valid_url_type(uri
)) {
2743 /* we can be clever and paste this in search box */
2744 show_oops(t
, "not a valid URL");
2748 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2750 else if (args
->i
== XT_PASTE_NEW_TAB
)
2751 create_new_tab(uri
, NULL
, 1, -1);
2762 find_domain(const gchar
*s
, int toplevel
)
2770 uri
= soup_uri_new(s
);
2772 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2776 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2777 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2778 while(--p
>= uri
->host
&& *p
!= '.');
2785 ret
= g_strdup_printf(".%s", p
);
2793 toggle_cwl(struct tab
*t
, struct karg
*args
)
2804 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2806 if (uri
== NULL
|| dom
== NULL
||
2807 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2808 show_oops(t
, "Can't toggle domain in cookie white list");
2811 d
= wl_find(dom
, &c_wl
);
2818 if (args
->i
& XT_WL_TOGGLE
)
2820 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2822 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2826 /* enable cookies for domain */
2827 wl_add(dom
, &c_wl
, 0);
2829 /* disable cookies for domain */
2830 RB_REMOVE(domain_list
, &c_wl
, d
);
2832 if (args
->i
& XT_WL_RELOAD
)
2833 webkit_web_view_reload(t
->wv
);
2841 toggle_js(struct tab
*t
, struct karg
*args
)
2851 g_object_get(G_OBJECT(t
->settings
),
2852 "enable-scripts", &es
, (char *)NULL
);
2853 if (args
->i
& XT_WL_TOGGLE
)
2855 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2857 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2863 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2865 if (uri
== NULL
|| dom
== NULL
||
2866 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2867 show_oops(t
, "Can't toggle domain in JavaScript white list");
2872 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2873 wl_add(dom
, &js_wl
, 0 /* session */);
2875 d
= wl_find(dom
, &js_wl
);
2877 RB_REMOVE(domain_list
, &js_wl
, d
);
2878 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2880 g_object_set(G_OBJECT(t
->settings
),
2881 "enable-scripts", es
, (char *)NULL
);
2882 g_object_set(G_OBJECT(t
->settings
),
2883 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2884 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2886 if (args
->i
& XT_WL_RELOAD
)
2887 webkit_web_view_reload(t
->wv
);
2895 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2899 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2902 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2907 toggle_src(struct tab
*t
, struct karg
*args
)
2914 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2915 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2916 webkit_web_view_reload(t
->wv
);
2922 focus_webview(struct tab
*t
)
2927 /* only grab focus if we are visible */
2928 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2929 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2933 focus(struct tab
*t
, struct karg
*args
)
2935 if (t
== NULL
|| args
== NULL
)
2941 if (args
->i
== XT_FOCUS_URI
)
2942 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2943 else if (args
->i
== XT_FOCUS_SEARCH
)
2944 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2950 stats(struct tab
*t
, struct karg
*args
)
2952 char *page
, *body
, *s
, line
[64 * 1024];
2953 uint64_t line_count
= 0;
2957 show_oops(NULL
, "stats invalid parameters");
2960 if (save_rejected_cookies
) {
2961 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2963 s
= fgets(line
, sizeof line
, r_cookie_f
);
2964 if (s
== NULL
|| feof(r_cookie_f
) ||
2970 snprintf(line
, sizeof line
,
2971 "<br/>Cookies blocked(*) total: %llu", line_count
);
2973 show_oops(t
, "Can't open blocked cookies file: %s",
2977 body
= g_strdup_printf(
2978 "Cookies blocked(*) this session: %llu"
2980 "<p><small><b>*</b> results vary based on settings</small></p>",
2984 page
= get_html_page("Statistics", body
, "", 0);
2987 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2994 marco(struct tab
*t
, struct karg
*args
)
2996 char *page
, line
[64 * 1024];
3000 show_oops(NULL
, "marco invalid parameters");
3003 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3005 page
= get_html_page("Marco Sez...", line
, "", 0);
3007 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3014 blank(struct tab
*t
, struct karg
*args
)
3017 show_oops(NULL
, "blank invalid parameters");
3019 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3025 about(struct tab
*t
, struct karg
*args
)
3030 show_oops(NULL
, "about invalid parameters");
3032 body
= g_strdup_printf("<b>Version: %s</b><p>"
3035 "<li>Marco Peereboom <marco@peereboom.us></li>"
3036 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3037 "<li>Edd Barrett <vext01@gmail.com> </li>"
3038 "<li>Todd T. Fries <todd@fries.net> </li>"
3039 "<li>Raphael Graf <r@undefined.ch> </li>"
3041 "Copyrights and licenses can be found on the XXXTerm "
3042 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
3046 page
= get_html_page("About", body
, "", 0);
3049 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3056 help(struct tab
*t
, struct karg
*args
)
3058 char *page
, *head
, *body
;
3061 show_oops(NULL
, "help invalid parameters");
3063 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3064 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3066 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3067 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3068 "cgi-bin/man-cgi?xxxterm</a>";
3070 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3072 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3079 startpage(struct tab
*t
, struct karg
*args
)
3081 char *page
, *body
, *b
;
3085 show_oops(NULL
, "startpage invalid parameters");
3087 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3089 TAILQ_FOREACH(s
, &spl
, entry
) {
3091 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3095 page
= get_html_page("Startup Exception", body
, "", 0);
3098 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3105 startpage_add(const char *fmt
, ...)
3115 if (vasprintf(&msg
, fmt
, ap
) == -1)
3116 errx(1, "startpage_add failed");
3119 s
= g_malloc0(sizeof *s
);
3122 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3126 * update all favorite tabs apart from one. Pass NULL if
3127 * you want to update all.
3130 update_favorite_tabs(struct tab
*apart_from
)
3133 if (!updating_fl_tabs
) {
3134 updating_fl_tabs
= 1; /* stop infinite recursion */
3135 TAILQ_FOREACH(t
, &tabs
, entry
)
3136 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3137 && (t
!= apart_from
))
3138 xtp_page_fl(t
, NULL
);
3139 updating_fl_tabs
= 0;
3143 /* show a list of favorites (bookmarks) */
3145 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3147 char file
[PATH_MAX
];
3149 char *uri
= NULL
, *title
= NULL
;
3150 size_t len
, lineno
= 0;
3152 char *body
, *tmp
, *page
= NULL
;
3153 const char delim
[3] = {'\\', '\\', '\0'};
3155 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3158 warn("%s: bad param", __func__
);
3160 /* new session key */
3161 if (!updating_fl_tabs
)
3162 generate_xtp_session_key(&fl_session_key
);
3164 /* open favorites */
3165 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3166 if ((f
= fopen(file
, "r")) == NULL
) {
3167 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3172 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3173 "<th style='width: 40px'>#</th><th>Link</th>"
3174 "<th style='width: 40px'>Rm</th></tr>\n");
3177 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3178 if (feof(f
) || ferror(f
))
3180 if (strlen(title
) == 0 || title
[0] == '#') {
3186 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3187 if (feof(f
) || ferror(f
)) {
3188 show_oops(t
, "favorites file corrupt");
3194 body
= g_strdup_printf("%s<tr>"
3196 "<td><a href='%s'>%s</a></td>"
3197 "<td style='text-align: center'>"
3198 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3200 body
, i
, uri
, title
,
3201 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3213 /* if none, say so */
3216 body
= g_strdup_printf("%s<tr>"
3217 "<td colspan='3' style='text-align: center'>"
3218 "No favorites - To add one use the 'favadd' command."
3219 "</td></tr>", body
);
3224 body
= g_strdup_printf("%s</table>", body
);
3234 page
= get_html_page("Favorites", body
, "", 1);
3235 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3239 update_favorite_tabs(t
);
3248 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3249 size_t cert_count
, char *title
)
3251 gnutls_datum_t cinfo
;
3255 body
= g_strdup("");
3257 for (i
= 0; i
< cert_count
; i
++) {
3258 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3263 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3264 body
, i
, cinfo
.data
);
3265 gnutls_free(cinfo
.data
);
3269 tmp
= get_html_page(title
, body
, "", 0);
3272 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3277 ca_cmd(struct tab
*t
, struct karg
*args
)
3280 int rv
= 1, certs
= 0, certs_read
;
3283 gnutls_x509_crt_t
*c
= NULL
;
3284 char *certs_buf
= NULL
, *s
;
3286 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3287 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3291 if (fstat(fileno(f
), &sb
) == -1) {
3292 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3296 certs_buf
= g_malloc(sb
.st_size
+ 1);
3297 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3298 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3301 certs_buf
[sb
.st_size
] = '\0';
3304 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3306 s
+= strlen("BEGIN CERTIFICATE");
3309 bzero(&dt
, sizeof dt
);
3310 dt
.data
= (unsigned char *)certs_buf
;
3311 dt
.size
= sb
.st_size
;
3312 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3313 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3314 GNUTLS_X509_FMT_PEM
, 0);
3315 if (certs_read
<= 0) {
3316 show_oops(t
, "No cert(s) available");
3319 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3332 connect_socket_from_uri(struct tab
*t
, const gchar
*uri
, char *domain
,
3336 struct addrinfo hints
, *res
= NULL
, *ai
;
3337 int rv
= -1, s
= -1, on
, error
;
3340 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3341 show_oops(t
, "invalid URI");
3345 su
= soup_uri_new(uri
);
3347 show_oops(t
, "invalid soup URI");
3350 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3351 show_oops(t
, "invalid HTTPS URI");
3355 snprintf(port
, sizeof port
, "%d", su
->port
);
3356 bzero(&hints
, sizeof(struct addrinfo
));
3357 hints
.ai_flags
= AI_CANONNAME
;
3358 hints
.ai_family
= AF_UNSPEC
;
3359 hints
.ai_socktype
= SOCK_STREAM
;
3361 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3362 show_oops(t
, "getaddrinfo failed: %s", gai_strerror(errno
));
3366 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3372 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3374 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3377 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3380 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3384 show_oops(t
, "could not obtain certificates from: %s",
3390 strlcpy(domain
, su
->host
, domain_sz
);
3397 if (rv
== -1 && s
!= -1)
3404 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3407 gnutls_deinit(gsession
);
3409 gnutls_certificate_free_credentials(xcred
);
3415 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3416 gnutls_certificate_credentials_t
*xc
)
3418 gnutls_certificate_credentials_t xcred
;
3419 gnutls_session_t gsession
;
3422 if (gs
== NULL
|| xc
== NULL
)
3428 gnutls_certificate_allocate_credentials(&xcred
);
3429 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3430 GNUTLS_X509_FMT_PEM
);
3432 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3433 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3434 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3435 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3436 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3437 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3439 gnutls_error_is_fatal(rv
),
3440 gnutls_strerror_name(rv
));
3441 stop_tls(gsession
, xcred
);
3445 gnutls_credentials_type_t cred
;
3446 cred
= gnutls_auth_get_type(gsession
);
3447 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3448 show_oops(t
, "gnutls_auth_get_type failed %d", (int)cred
);
3449 stop_tls(gsession
, xcred
);
3461 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3465 const gnutls_datum_t
*cl
;
3466 gnutls_x509_crt_t
*all_certs
;
3469 if (certs
== NULL
|| cert_count
== NULL
)
3471 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3473 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3477 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3478 for (i
= 0; i
< len
; i
++) {
3479 gnutls_x509_crt_init(&all_certs
[i
]);
3480 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3481 GNUTLS_X509_FMT_PEM
< 0)) {
3495 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3499 for (i
= 0; i
< cert_count
; i
++)
3500 gnutls_x509_crt_deinit(certs
[i
]);
3505 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3507 GdkColor c_text
, c_base
;
3509 gdk_color_parse(text
, &c_text
);
3510 gdk_color_parse(base
, &c_base
);
3512 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3513 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3514 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3515 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3517 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3518 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3519 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3520 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3524 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3525 size_t cert_count
, char *domain
)
3528 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3533 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3536 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3537 if ((f
= fopen(file
, "w")) == NULL
) {
3538 show_oops(t
, "Can't create cert file %s %s",
3539 file
, strerror(errno
));
3543 for (i
= 0; i
< cert_count
; i
++) {
3544 cert_buf_sz
= sizeof cert_buf
;
3545 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3546 cert_buf
, &cert_buf_sz
)) {
3547 show_oops(t
, "gnutls_x509_crt_export failed");
3550 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3551 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3556 /* not the best spot but oh well */
3557 gdk_color_parse("lightblue", &color
);
3558 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3559 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3572 load_compare_cert(struct tab
*t
, struct karg
*args
)
3575 char domain
[8182], file
[PATH_MAX
];
3576 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3577 int s
= -1, i
, error
;
3579 size_t cert_buf_sz
, cert_count
;
3580 enum cert_trust rv
= CERT_UNTRUSTED
;
3582 gnutls_session_t gsession
;
3583 gnutls_x509_crt_t
*certs
;
3584 gnutls_certificate_credentials_t xcred
;
3586 DNPRINTF(XT_D_URL
, "%s: %p %p\n", __func__
, t
, args
);
3591 if ((uri
= get_uri(t
)) == NULL
)
3593 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3595 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1)
3597 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3600 if (start_tls(t
, s
, &gsession
, &xcred
))
3602 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3604 /* verify certs in case cert file doesn't exist */
3605 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3607 show_oops(t
, "Invalid certificates");
3612 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3613 show_oops(t
, "Can't get connection certificates");
3617 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3618 if ((f
= fopen(file
, "r")) == NULL
) {
3624 for (i
= 0; i
< cert_count
; i
++) {
3625 cert_buf_sz
= sizeof cert_buf
;
3626 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3627 cert_buf
, &cert_buf_sz
)) {
3630 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3631 rv
= CERT_BAD
; /* critical */
3634 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3635 rv
= CERT_BAD
; /* critical */
3644 free_connection_certs(certs
, cert_count
);
3646 /* we close the socket first for speed */
3650 /* only complain if we didn't save it locally */
3651 if (error
&& rv
!= CERT_LOCAL
) {
3652 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3653 if (error
& GNUTLS_CERT_INVALID
)
3654 strlcat(serr
, "invalid, ", sizeof serr
);
3655 if (error
& GNUTLS_CERT_REVOKED
)
3656 strlcat(serr
, "revoked, ", sizeof serr
);
3657 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3658 strlcat(serr
, "signer not found, ", sizeof serr
);
3659 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3660 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3661 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3662 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3663 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3664 strlcat(serr
, "not activated, ", sizeof serr
);
3665 if (error
& GNUTLS_CERT_EXPIRED
)
3666 strlcat(serr
, "expired, ", sizeof serr
);
3667 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3668 if (serr
[i
] == ',') {
3675 stop_tls(gsession
, xcred
);
3681 cert_cmd(struct tab
*t
, struct karg
*args
)
3687 gnutls_session_t gsession
;
3688 gnutls_x509_crt_t
*certs
;
3689 gnutls_certificate_credentials_t xcred
;
3694 if (ssl_ca_file
== NULL
) {
3695 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3699 if ((uri
= get_uri(t
)) == NULL
) {
3700 show_oops(t
, "Invalid URI");
3704 if ((s
= connect_socket_from_uri(t
, uri
, domain
, sizeof domain
)) == -1) {
3705 show_oops(t
, "Invalid certificate URI: %s", uri
);
3710 if (start_tls(t
, s
, &gsession
, &xcred
))
3714 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3715 show_oops(t
, "get_connection_certs failed");
3719 if (args
->i
& XT_SHOW
)
3720 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3721 else if (args
->i
& XT_SAVE
)
3722 save_certs(t
, certs
, cert_count
, domain
);
3724 free_connection_certs(certs
, cert_count
);
3726 /* we close the socket first for speed */
3729 stop_tls(gsession
, xcred
);
3735 remove_cookie(int index
)
3741 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3743 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3745 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3749 print_cookie("remove cookie", c
);
3750 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3755 soup_cookies_free(cf
);
3761 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3766 body
= g_strdup("");
3769 if (args
->i
& XT_WL_PERSISTENT
) {
3771 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3773 RB_FOREACH(d
, domain_list
, wl
) {
3777 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3783 if (args
->i
& XT_WL_SESSION
) {
3785 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3787 RB_FOREACH(d
, domain_list
, wl
) {
3791 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3796 tmp
= get_html_page(title
, body
, "", 0);
3799 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3801 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3807 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3809 char file
[PATH_MAX
];
3811 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3819 if (t
== NULL
|| args
== NULL
)
3822 if (runtime_settings
[0] == '\0')
3825 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3826 if ((f
= fopen(file
, "r+")) == NULL
)
3830 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3831 if (uri
== NULL
|| dom
== NULL
||
3832 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3833 show_oops(t
, "Can't add domain to %s white list",
3834 js
? "JavaScript" : "cookie");
3838 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3841 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3844 if (!strcmp(line
, lt
))
3850 fprintf(f
, "%s\n", lt
);
3855 d
= wl_find(dom
, &js_wl
);
3857 settings_add("js_wl", dom
);
3858 d
= wl_find(dom
, &js_wl
);
3862 d
= wl_find(dom
, &c_wl
);
3864 settings_add("cookie_wl", dom
);
3865 d
= wl_find(dom
, &c_wl
);
3869 /* find and add to persistent jar */
3870 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3871 for (;cf
; cf
= cf
->next
) {
3873 if (!strcmp(dom
, ci
->domain
) ||
3874 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3875 c
= soup_cookie_copy(ci
);
3876 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3879 soup_cookies_free(cf
);
3897 js_show_wl(struct tab
*t
, struct karg
*args
)
3899 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3900 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3906 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3908 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3909 wl_show(t
, args
, "Cookie White List", &c_wl
);
3915 cookie_cmd(struct tab
*t
, struct karg
*args
)
3917 if (args
->i
& XT_SHOW
)
3918 wl_show(t
, args
, "Cookie White List", &c_wl
);
3919 else if (args
->i
& XT_WL_TOGGLE
) {
3920 args
->i
|= XT_WL_RELOAD
;
3921 toggle_cwl(t
, args
);
3922 } else if (args
->i
& XT_SAVE
) {
3923 args
->i
|= XT_WL_RELOAD
;
3924 wl_save(t
, args
, 0);
3925 } else if (args
->i
& XT_DELETE
)
3926 show_oops(t
, "'cookie delete' currently unimplemented");
3932 js_cmd(struct tab
*t
, struct karg
*args
)
3934 if (args
->i
& XT_SHOW
)
3935 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3936 else if (args
->i
& XT_SAVE
) {
3937 args
->i
|= XT_WL_RELOAD
;
3938 wl_save(t
, args
, 1);
3939 } else if (args
->i
& XT_WL_TOGGLE
) {
3940 args
->i
|= XT_WL_RELOAD
;
3942 } else if (args
->i
& XT_DELETE
)
3943 show_oops(t
, "'js delete' currently unimplemented");
3949 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3951 js_toggle_cb(t
->js_toggle
, t
);
3957 add_favorite(struct tab
*t
, struct karg
*args
)
3959 char file
[PATH_MAX
];
3962 size_t urilen
, linelen
;
3963 const gchar
*uri
, *title
;
3968 /* don't allow adding of xtp pages to favorites */
3969 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3970 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3974 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3975 if ((f
= fopen(file
, "r+")) == NULL
) {
3976 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3980 title
= get_title(t
, FALSE
);
3983 if (title
== NULL
|| uri
== NULL
) {
3984 show_oops(t
, "can't add page to favorites");
3988 urilen
= strlen(uri
);
3991 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3992 if (feof(f
) || ferror(f
))
3995 if (linelen
== urilen
&& !strcmp(line
, uri
))
4002 fprintf(f
, "\n%s\n%s", title
, uri
);
4008 update_favorite_tabs(NULL
);
4014 can_go_back_for_real(struct tab
*t
)
4017 WebKitWebHistoryItem
*item
;
4019 /* rely on webkit to make sure we can go backward when on an about page */
4020 if (get_uri(t
) == NULL
|| g_str_has_prefix(get_uri(t
), "about:"))
4021 return (webkit_web_view_can_go_forward(t
->wv
));
4024 /* the back/forwars list is stupid so help determine if we can go back */
4025 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4027 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4028 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
)))
4036 can_go_forward_for_real(struct tab
*t
)
4039 WebKitWebHistoryItem
*item
;
4041 /* rely on webkit to make sure we can go forward when on an about page */
4042 if (get_uri(t
) == NULL
|| g_str_has_prefix(get_uri(t
), "about:"))
4043 return (webkit_web_view_can_go_forward(t
->wv
));
4045 /* the back/forwars list is stupid so help selecting a different item */
4046 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4048 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4049 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
)))
4057 go_back_for_real(struct tab
*t
)
4060 WebKitWebHistoryItem
*item
;
4062 /* the back/forwars list is stupid so help selecting a different item */
4063 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4065 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4066 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
))) {
4067 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4074 go_forward_for_real(struct tab
*t
)
4077 WebKitWebHistoryItem
*item
;
4079 /* the back/forwars list is stupid so help selecting a different item */
4080 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4082 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4083 if (strcmp(webkit_web_history_item_get_uri(item
), get_uri(t
))) {
4084 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4091 navaction(struct tab
*t
, struct karg
*args
)
4093 WebKitWebHistoryItem
*item
;
4094 WebKitWebFrame
*frame
;
4096 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4097 t
->tab_id
, args
->i
);
4099 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4101 if (args
->i
== XT_NAV_BACK
)
4102 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4104 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4106 return (XT_CB_PASSTHROUGH
);
4107 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4109 return (XT_CB_PASSTHROUGH
);
4115 go_back_for_real(t
);
4117 case XT_NAV_FORWARD
:
4119 go_forward_for_real(t
);
4122 frame
= webkit_web_view_get_main_frame(t
->wv
);
4123 webkit_web_frame_reload(frame
);
4126 return (XT_CB_PASSTHROUGH
);
4130 move(struct tab
*t
, struct karg
*args
)
4132 GtkAdjustment
*adjust
;
4133 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4139 case XT_MOVE_BOTTOM
:
4141 case XT_MOVE_PAGEDOWN
:
4142 case XT_MOVE_PAGEUP
:
4143 case XT_MOVE_HALFDOWN
:
4144 case XT_MOVE_HALFUP
:
4145 case XT_MOVE_PERCENT
:
4146 adjust
= t
->adjust_v
;
4149 adjust
= t
->adjust_h
;
4153 pos
= gtk_adjustment_get_value(adjust
);
4154 ps
= gtk_adjustment_get_page_size(adjust
);
4155 upper
= gtk_adjustment_get_upper(adjust
);
4156 lower
= gtk_adjustment_get_lower(adjust
);
4157 si
= gtk_adjustment_get_step_increment(adjust
);
4158 pi
= gtk_adjustment_get_page_increment(adjust
);
4161 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4162 "max %f si %f pi %f\n",
4163 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4164 pos
, ps
, upper
, lower
, max
, si
, pi
);
4170 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4175 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4177 case XT_MOVE_BOTTOM
:
4178 case XT_MOVE_FARRIGHT
:
4179 gtk_adjustment_set_value(adjust
, max
);
4182 case XT_MOVE_FARLEFT
:
4183 gtk_adjustment_set_value(adjust
, lower
);
4185 case XT_MOVE_PAGEDOWN
:
4187 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4189 case XT_MOVE_PAGEUP
:
4191 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4193 case XT_MOVE_HALFDOWN
:
4195 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4197 case XT_MOVE_HALFUP
:
4199 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4201 case XT_MOVE_PERCENT
:
4202 percent
= atoi(args
->s
) / 100.0;
4203 pos
= max
* percent
;
4204 if (pos
< 0.0 || pos
> max
)
4206 gtk_adjustment_set_value(adjust
, pos
);
4209 return (XT_CB_PASSTHROUGH
);
4212 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4214 return (XT_CB_HANDLED
);
4218 url_set_visibility(void)
4222 TAILQ_FOREACH(t
, &tabs
, entry
)
4223 if (show_url
== 0) {
4224 gtk_widget_hide(t
->toolbar
);
4227 gtk_widget_show(t
->toolbar
);
4231 notebook_tab_set_visibility(void)
4233 if (show_tabs
== 0) {
4234 gtk_widget_hide(tab_bar
);
4235 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4237 if (tab_style
== XT_TABS_NORMAL
) {
4238 gtk_widget_hide(tab_bar
);
4239 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4240 } else if (tab_style
== XT_TABS_COMPACT
) {
4241 gtk_widget_show(tab_bar
);
4242 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4248 statusbar_set_visibility(void)
4252 TAILQ_FOREACH(t
, &tabs
, entry
)
4253 if (show_statusbar
== 0) {
4254 gtk_widget_hide(t
->statusbar_box
);
4257 gtk_widget_show(t
->statusbar_box
);
4261 url_set(struct tab
*t
, int enable_url_entry
)
4266 show_url
= enable_url_entry
;
4268 if (enable_url_entry
) {
4269 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4270 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4271 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4273 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4274 GTK_ENTRY_ICON_PRIMARY
);
4276 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4277 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4278 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4279 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4285 fullscreen(struct tab
*t
, struct karg
*args
)
4287 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4290 return (XT_CB_PASSTHROUGH
);
4292 if (show_url
== 0) {
4300 url_set_visibility();
4301 notebook_tab_set_visibility();
4303 return (XT_CB_HANDLED
);
4307 statustoggle(struct tab
*t
, struct karg
*args
)
4309 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4311 if (show_statusbar
== 1) {
4313 statusbar_set_visibility();
4314 } else if (show_statusbar
== 0) {
4316 statusbar_set_visibility();
4318 return (XT_CB_HANDLED
);
4322 urlaction(struct tab
*t
, struct karg
*args
)
4324 int rv
= XT_CB_HANDLED
;
4326 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4329 return (XT_CB_PASSTHROUGH
);
4333 if (show_url
== 0) {
4335 url_set_visibility();
4339 if (show_url
== 1) {
4341 url_set_visibility();
4349 tabaction(struct tab
*t
, struct karg
*args
)
4351 int rv
= XT_CB_HANDLED
;
4352 char *url
= args
->s
;
4356 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4359 return (XT_CB_PASSTHROUGH
);
4363 if (strlen(url
) > 0)
4364 create_new_tab(url
, NULL
, 1, args
->precount
);
4366 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4369 if (args
->precount
< 0)
4372 TAILQ_FOREACH(tt
, &tabs
, entry
)
4373 if (tt
->tab_id
== args
->precount
- 1) {
4378 case XT_TAB_DELQUIT
:
4379 if (gtk_notebook_get_n_pages(notebook
) > 1)
4385 if (strlen(url
) > 0)
4388 rv
= XT_CB_PASSTHROUGH
;
4394 if (show_tabs
== 0) {
4396 notebook_tab_set_visibility();
4400 if (show_tabs
== 1) {
4402 notebook_tab_set_visibility();
4405 case XT_TAB_NEXTSTYLE
:
4406 if (tab_style
== XT_TABS_NORMAL
) {
4407 tab_style
= XT_TABS_COMPACT
;
4408 recolor_compact_tabs();
4411 tab_style
= XT_TABS_NORMAL
;
4412 notebook_tab_set_visibility();
4414 case XT_TAB_UNDO_CLOSE
:
4415 if (undo_count
== 0) {
4416 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4421 u
= TAILQ_FIRST(&undos
);
4422 create_new_tab(u
->uri
, u
, 1, -1);
4424 TAILQ_REMOVE(&undos
, u
, entry
);
4426 /* u->history is freed in create_new_tab() */
4431 rv
= XT_CB_PASSTHROUGH
;
4445 resizetab(struct tab
*t
, struct karg
*args
)
4447 if (t
== NULL
|| args
== NULL
) {
4448 show_oops(NULL
, "resizetab invalid parameters");
4449 return (XT_CB_PASSTHROUGH
);
4452 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4453 t
->tab_id
, args
->i
);
4455 setzoom_webkit(t
, args
->i
);
4457 return (XT_CB_HANDLED
);
4461 movetab(struct tab
*t
, struct karg
*args
)
4465 if (t
== NULL
|| args
== NULL
) {
4466 show_oops(NULL
, "movetab invalid parameters");
4467 return (XT_CB_PASSTHROUGH
);
4470 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4471 t
->tab_id
, args
->i
);
4473 if (args
->i
>= XT_TAB_INVALID
)
4474 return (XT_CB_PASSTHROUGH
);
4476 if (TAILQ_EMPTY(&tabs
))
4477 return (XT_CB_PASSTHROUGH
);
4479 n
= gtk_notebook_get_n_pages(notebook
);
4480 dest
= gtk_notebook_get_current_page(notebook
);
4484 if (args
->precount
< 0)
4485 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4487 dest
= args
->precount
- 1;
4491 if (args
->precount
< 0)
4494 dest
-= args
->precount
% n
;
4507 return (XT_CB_PASSTHROUGH
);
4510 if (dest
< 0 || dest
>= n
)
4511 return (XT_CB_PASSTHROUGH
);
4512 if (t
->tab_id
== dest
) {
4513 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4514 return (XT_CB_HANDLED
);
4517 set_current_tab(dest
);
4519 return (XT_CB_HANDLED
);
4525 command(struct tab
*t
, struct karg
*args
)
4527 char *s
= NULL
, *ss
= NULL
;
4531 if (t
== NULL
|| args
== NULL
) {
4532 show_oops(NULL
, "command invalid parameters");
4533 return (XT_CB_PASSTHROUGH
);
4544 if (cmd_prefix
== 0)
4547 ss
= g_strdup_printf(":%d", cmd_prefix
);
4558 case XT_CMD_OPEN_CURRENT
:
4561 case XT_CMD_TABNEW_CURRENT
:
4562 if (!s
) /* FALL THROUGH? */
4564 if ((uri
= get_uri(t
)) != NULL
) {
4565 ss
= g_strdup_printf("%s%s", s
, uri
);
4570 show_oops(t
, "command: invalid opcode %d", args
->i
);
4571 return (XT_CB_PASSTHROUGH
);
4574 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4576 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4577 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4578 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4580 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4581 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4586 return (XT_CB_HANDLED
);
4590 * Return a new string with a download row (in html)
4591 * appended. Old string is freed.
4594 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4597 WebKitDownloadStatus stat
;
4598 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4600 char cur_sz
[FMT_SCALED_STRSIZE
];
4601 char tot_sz
[FMT_SCALED_STRSIZE
];
4604 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4606 /* All actions wil take this form:
4607 * xxxt://class/seskey
4609 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4610 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4612 stat
= webkit_download_get_status(dl
->download
);
4615 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4616 status_html
= g_strdup_printf("Finished");
4617 cmd_html
= g_strdup_printf(
4618 "<a href='%s%d/%d'>Remove</a>",
4619 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4621 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4622 /* gather size info */
4623 progress
= 100 * webkit_download_get_progress(dl
->download
);
4626 webkit_download_get_current_size(dl
->download
), cur_sz
);
4628 webkit_download_get_total_size(dl
->download
), tot_sz
);
4630 status_html
= g_strdup_printf(
4631 "<div style='width: 100%%' align='center'>"
4632 "<div class='progress-outer'>"
4633 "<div class='progress-inner' style='width: %.2f%%'>"
4634 "</div></div></div>"
4635 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4636 progress
, cur_sz
, tot_sz
, progress
);
4638 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4639 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4643 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4644 status_html
= g_strdup_printf("Cancelled");
4645 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4646 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4648 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4649 status_html
= g_strdup_printf("Error!");
4650 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4651 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4653 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4654 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4655 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4656 status_html
= g_strdup_printf("Starting");
4659 show_oops(t
, "%s: unknown download status", __func__
);
4662 new_html
= g_strdup_printf(
4663 "%s\n<tr><td>%s</td><td>%s</td>"
4664 "<td style='text-align:center'>%s</td></tr>\n",
4665 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4666 status_html
, cmd_html
);
4670 g_free(status_html
);
4681 * update all download tabs apart from one. Pass NULL if
4682 * you want to update all.
4685 update_download_tabs(struct tab
*apart_from
)
4688 if (!updating_dl_tabs
) {
4689 updating_dl_tabs
= 1; /* stop infinite recursion */
4690 TAILQ_FOREACH(t
, &tabs
, entry
)
4691 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4692 && (t
!= apart_from
))
4693 xtp_page_dl(t
, NULL
);
4694 updating_dl_tabs
= 0;
4699 * update all cookie tabs apart from one. Pass NULL if
4700 * you want to update all.
4703 update_cookie_tabs(struct tab
*apart_from
)
4706 if (!updating_cl_tabs
) {
4707 updating_cl_tabs
= 1; /* stop infinite recursion */
4708 TAILQ_FOREACH(t
, &tabs
, entry
)
4709 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4710 && (t
!= apart_from
))
4711 xtp_page_cl(t
, NULL
);
4712 updating_cl_tabs
= 0;
4717 * update all history tabs apart from one. Pass NULL if
4718 * you want to update all.
4721 update_history_tabs(struct tab
*apart_from
)
4725 if (!updating_hl_tabs
) {
4726 updating_hl_tabs
= 1; /* stop infinite recursion */
4727 TAILQ_FOREACH(t
, &tabs
, entry
)
4728 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4729 && (t
!= apart_from
))
4730 xtp_page_hl(t
, NULL
);
4731 updating_hl_tabs
= 0;
4735 /* cookie management XTP page */
4737 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4739 char *body
, *page
, *tmp
;
4740 int i
= 1; /* all ids start 1 */
4741 GSList
*sc
, *pc
, *pc_start
;
4743 char *type
, *table_headers
, *last_domain
;
4745 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4748 show_oops(NULL
, "%s invalid parameters", __func__
);
4752 /* Generate a new session key */
4753 if (!updating_cl_tabs
)
4754 generate_xtp_session_key(&cl_session_key
);
4757 table_headers
= g_strdup_printf("<table><tr>"
4760 "<th style='width:200px'>Value</th>"
4764 "<th>HTTP<br />only</th>"
4765 "<th style='width:40px'>Rm</th></tr>\n");
4767 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4768 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4772 last_domain
= strdup("");
4773 for (; sc
; sc
= sc
->next
) {
4776 if (strcmp(last_domain
, c
->domain
) != 0) {
4779 last_domain
= strdup(c
->domain
);
4783 body
= g_strdup_printf("%s</table>"
4785 body
, c
->domain
, table_headers
);
4789 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4790 c
->domain
, table_headers
);
4795 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4796 if (soup_cookie_equal(pc
->data
, c
)) {
4797 type
= "Session + Persistent";
4802 body
= g_strdup_printf(
4805 "<td style='word-wrap:normal'>%s</td>"
4807 " <textarea rows='4'>%s</textarea>"
4813 "<td style='text-align:center'>"
4814 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4821 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4836 soup_cookies_free(sc
);
4837 soup_cookies_free(pc
);
4839 /* small message if there are none */
4841 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4842 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4845 body
= g_strdup_printf("%s</table>", body
);
4848 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4850 g_free(table_headers
);
4851 g_free(last_domain
);
4853 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4854 update_cookie_tabs(t
);
4862 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4864 char *body
, *page
, *tmp
;
4866 int i
= 1; /* all ids start 1 */
4868 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4871 show_oops(NULL
, "%s invalid parameters", __func__
);
4875 /* Generate a new session key */
4876 if (!updating_hl_tabs
)
4877 generate_xtp_session_key(&hl_session_key
);
4880 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4881 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4883 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4885 body
= g_strdup_printf(
4887 "<td><a href='%s'>%s</a></td>"
4889 "<td style='text-align: center'>"
4890 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4891 body
, h
->uri
, h
->uri
, h
->title
,
4892 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4893 XT_XTP_HL_REMOVE
, i
);
4899 /* small message if there are none */
4902 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4903 "colspan='3'>No History</td></tr>\n", body
);
4908 body
= g_strdup_printf("%s</table>", body
);
4911 page
= get_html_page("History", body
, "", TRUE
);
4915 * update all history manager tabs as the xtp session
4916 * key has now changed. No need to update the current tab.
4917 * Already did that above.
4919 update_history_tabs(t
);
4921 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4928 * Generate a web page detailing the status of any downloads
4931 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4933 struct download
*dl
;
4934 char *body
, *page
, *tmp
;
4938 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4941 show_oops(NULL
, "%s invalid parameters", __func__
);
4946 * Generate a new session key for next page instance.
4947 * This only happens for the top level call to xtp_page_dl()
4948 * in which case updating_dl_tabs is 0.
4950 if (!updating_dl_tabs
)
4951 generate_xtp_session_key(&dl_session_key
);
4953 /* header - with refresh so as to update */
4954 if (refresh_interval
>= 1)
4955 ref
= g_strdup_printf(
4956 "<meta http-equiv='refresh' content='%u"
4957 ";url=%s%d/%s/%d' />\n",
4966 body
= g_strdup_printf("<div align='center'>"
4967 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4968 "</p><table><tr><th style='width: 60%%'>"
4969 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4970 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4972 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4973 body
= xtp_page_dl_row(t
, body
, dl
);
4977 /* message if no downloads in list */
4980 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4981 " style='text-align: center'>"
4982 "No downloads</td></tr>\n", body
);
4987 body
= g_strdup_printf("%s</table></div>", body
);
4990 page
= get_html_page("Downloads", body
, ref
, 1);
4995 * update all download manager tabs as the xtp session
4996 * key has now changed. No need to update the current tab.
4997 * Already did that above.
4999 update_download_tabs(t
);
5001 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5008 search(struct tab
*t
, struct karg
*args
)
5012 if (t
== NULL
|| args
== NULL
) {
5013 show_oops(NULL
, "search invalid parameters");
5016 if (t
->search_text
== NULL
) {
5017 if (global_search
== NULL
)
5018 return (XT_CB_PASSTHROUGH
);
5020 t
->search_text
= g_strdup(global_search
);
5021 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5022 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5026 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5027 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5030 case XT_SEARCH_NEXT
:
5031 d
= t
->search_forward
;
5033 case XT_SEARCH_PREV
:
5034 d
= !t
->search_forward
;
5037 return (XT_CB_PASSTHROUGH
);
5040 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5042 return (XT_CB_HANDLED
);
5045 struct settings_args
{
5051 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5054 struct settings_args
*sa
= cb_args
;
5059 if (s
->flags
& XT_SF_RUNTIME
)
5065 *sa
->body
= g_strdup_printf(
5067 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5068 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5080 set_show(struct tab
*t
, struct karg
*args
)
5082 char *body
, *page
, *tmp
;
5084 struct settings_args sa
;
5086 bzero(&sa
, sizeof sa
);
5090 body
= g_strdup_printf("<div align='center'><table><tr>"
5091 "<th align='left'>Setting</th>"
5092 "<th align='left'>Value</th></tr>\n");
5094 settings_walk(print_setting
, &sa
);
5097 /* small message if there are none */
5100 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5101 "colspan='2'>No settings</td></tr>\n", body
);
5106 body
= g_strdup_printf("%s</table></div>", body
);
5109 page
= get_html_page("Settings", body
, "", 0);
5113 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5117 return (XT_CB_PASSTHROUGH
);
5121 set(struct tab
*t
, struct karg
*args
)
5126 if (args
== NULL
|| args
->s
== NULL
)
5127 return (set_show(t
, args
));
5130 p
= g_strstrip(args
->s
);
5133 return (set_show(t
, args
));
5135 /* we got some sort of string */
5136 val
= g_strrstr(p
, "=");
5139 val
= g_strchomp(val
);
5142 for (i
= 0; i
< LENGTH(rs
); i
++) {
5143 if (strcmp(rs
[i
].name
, p
))
5146 if (rs
[i
].activate
) {
5147 if (rs
[i
].activate(val
))
5148 show_oops(t
, "%s invalid value %s",
5151 show_oops(t
, ":set %s = %s", p
, val
);
5154 show_oops(t
, "not a runtime option: %s", p
);
5158 show_oops(t
, "unknown option: %s", p
);
5162 for (i
= 0; i
< LENGTH(rs
); i
++) {
5163 if (strcmp(rs
[i
].name
, p
))
5166 /* XXX this could use some cleanup */
5167 switch (rs
[i
].type
) {
5170 show_oops(t
, "%s = %d",
5171 rs
[i
].name
, *rs
[i
].ival
);
5172 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5173 show_oops(t
, "%s = %s",
5175 rs
[i
].s
->get(&rs
[i
]));
5176 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5177 show_oops(t
, "%s = ...", rs
[i
].name
);
5179 show_oops(t
, "%s = ", rs
[i
].name
);
5183 show_oops(t
, "%s = %f",
5184 rs
[i
].name
, *rs
[i
].fval
);
5185 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5186 show_oops(t
, "%s = %s",
5188 rs
[i
].s
->get(&rs
[i
]));
5189 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5190 show_oops(t
, "%s = ...", rs
[i
].name
);
5192 show_oops(t
, "%s = ", rs
[i
].name
);
5195 if (rs
[i
].sval
&& *rs
[i
].sval
)
5196 show_oops(t
, "%s = %s",
5197 rs
[i
].name
, *rs
[i
].sval
);
5198 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5199 show_oops(t
, "%s = %s",
5201 rs
[i
].s
->get(&rs
[i
]));
5202 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5203 show_oops(t
, "%s = ...", rs
[i
].name
);
5205 show_oops(t
, "%s = ", rs
[i
].name
);
5208 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5214 show_oops(t
, "unknown option: %s", p
);
5217 return (XT_CB_PASSTHROUGH
);
5221 session_save(struct tab
*t
, char *filename
)
5226 if (strlen(filename
) == 0)
5229 if (filename
[0] == '.' || filename
[0] == '/')
5233 if (save_tabs(t
, &a
))
5235 strlcpy(named_session
, filename
, sizeof named_session
);
5243 session_open(struct tab
*t
, char *filename
)
5248 if (strlen(filename
) == 0)
5251 if (filename
[0] == '.' || filename
[0] == '/')
5255 a
.i
= XT_SES_CLOSETABS
;
5256 if (open_tabs(t
, &a
))
5259 strlcpy(named_session
, filename
, sizeof named_session
);
5267 session_delete(struct tab
*t
, char *filename
)
5269 char file
[PATH_MAX
];
5272 if (strlen(filename
) == 0)
5275 if (filename
[0] == '.' || filename
[0] == '/')
5278 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5282 if (!strcmp(filename
, named_session
))
5283 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5284 sizeof named_session
);
5292 session_cmd(struct tab
*t
, struct karg
*args
)
5294 char *filename
= args
->s
;
5299 if (args
->i
& XT_SHOW
)
5300 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5301 XT_SAVED_TABS_FILE
: named_session
);
5302 else if (args
->i
& XT_SAVE
) {
5303 if (session_save(t
, filename
)) {
5304 show_oops(t
, "Can't save session: %s",
5305 filename
? filename
: "INVALID");
5308 } else if (args
->i
& XT_OPEN
) {
5309 if (session_open(t
, filename
)) {
5310 show_oops(t
, "Can't open session: %s",
5311 filename
? filename
: "INVALID");
5314 } else if (args
->i
& XT_DELETE
) {
5315 if (session_delete(t
, filename
)) {
5316 show_oops(t
, "Can't delete session: %s",
5317 filename
? filename
: "INVALID");
5322 return (XT_CB_PASSTHROUGH
);
5326 * Make a hardcopy of the page
5329 print_page(struct tab
*t
, struct karg
*args
)
5331 WebKitWebFrame
*frame
;
5333 GtkPrintOperation
*op
;
5334 GtkPrintOperationAction action
;
5335 GtkPrintOperationResult print_res
;
5336 GError
*g_err
= NULL
;
5337 int marg_l
, marg_r
, marg_t
, marg_b
;
5339 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5341 ps
= gtk_page_setup_new();
5342 op
= gtk_print_operation_new();
5343 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5344 frame
= webkit_web_view_get_main_frame(t
->wv
);
5346 /* the default margins are too small, so we will bump them */
5347 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5348 XT_PRINT_EXTRA_MARGIN
;
5349 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5350 XT_PRINT_EXTRA_MARGIN
;
5351 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5352 XT_PRINT_EXTRA_MARGIN
;
5353 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5354 XT_PRINT_EXTRA_MARGIN
;
5357 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5358 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5359 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5360 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5362 gtk_print_operation_set_default_page_setup(op
, ps
);
5364 /* this appears to free 'op' and 'ps' */
5365 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5367 /* check it worked */
5368 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5369 show_oops(NULL
, "can't print: %s", g_err
->message
);
5370 g_error_free (g_err
);
5378 go_home(struct tab
*t
, struct karg
*args
)
5385 restart(struct tab
*t
, struct karg
*args
)
5389 a
.s
= XT_RESTART_TABS_FILE
;
5391 execvp(start_argv
[0], start_argv
);
5397 #define CTRL GDK_CONTROL_MASK
5398 #define MOD1 GDK_MOD1_MASK
5399 #define SHFT GDK_SHIFT_MASK
5401 /* inherent to GTK not all keys will be caught at all times */
5402 /* XXX sort key bindings */
5403 struct key_binding
{
5408 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5410 { "cookiejar", MOD1
, 0, GDK_j
},
5411 { "downloadmgr", MOD1
, 0, GDK_d
},
5412 { "history", MOD1
, 0, GDK_h
},
5413 { "print", CTRL
, 0, GDK_p
},
5414 { "search", 0, 0, GDK_slash
},
5415 { "searchb", 0, 0, GDK_question
},
5416 { "statustoggle", CTRL
, 0, GDK_n
},
5417 { "command", 0, 0, GDK_colon
},
5418 { "qa", CTRL
, 0, GDK_q
},
5419 { "restart", MOD1
, 0, GDK_q
},
5420 { "js toggle", CTRL
, 0, GDK_j
},
5421 { "cookie toggle", MOD1
, 0, GDK_c
},
5422 { "togglesrc", CTRL
, 0, GDK_s
},
5423 { "yankuri", 0, 0, GDK_y
},
5424 { "pasteuricur", 0, 0, GDK_p
},
5425 { "pasteurinew", 0, 0, GDK_P
},
5426 { "toplevel toggle", 0, 0, GDK_F4
},
5427 { "help", 0, 0, GDK_F1
},
5428 { "run_script", MOD1
, 0, GDK_r
},
5431 { "searchnext", 0, 0, GDK_n
},
5432 { "searchprevious", 0, 0, GDK_N
},
5435 { "focusaddress", 0, 0, GDK_F6
},
5436 { "focussearch", 0, 0, GDK_F7
},
5439 { "hinting", 0, 0, GDK_f
},
5441 /* custom stylesheet */
5442 { "userstyle", 0, 0, GDK_i
},
5445 { "goback", 0, 0, GDK_BackSpace
},
5446 { "goback", MOD1
, 0, GDK_Left
},
5447 { "goforward", SHFT
, 0, GDK_BackSpace
},
5448 { "goforward", MOD1
, 0, GDK_Right
},
5449 { "reload", 0, 0, GDK_F5
},
5450 { "reload", CTRL
, 0, GDK_r
},
5451 { "reload", CTRL
, 0, GDK_l
},
5452 { "favorites", MOD1
, 1, GDK_f
},
5454 /* vertical movement */
5455 { "scrolldown", 0, 0, GDK_j
},
5456 { "scrolldown", 0, 0, GDK_Down
},
5457 { "scrollup", 0, 0, GDK_Up
},
5458 { "scrollup", 0, 0, GDK_k
},
5459 { "scrollbottom", 0, 0, GDK_G
},
5460 { "scrollbottom", 0, 0, GDK_End
},
5461 { "scrolltop", 0, 0, GDK_Home
},
5462 { "scrollpagedown", 0, 0, GDK_space
},
5463 { "scrollpagedown", CTRL
, 0, GDK_f
},
5464 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5465 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5466 { "scrollpageup", 0, 0, GDK_Page_Up
},
5467 { "scrollpageup", CTRL
, 0, GDK_b
},
5468 { "scrollhalfup", CTRL
, 0, GDK_u
},
5469 /* horizontal movement */
5470 { "scrollright", 0, 0, GDK_l
},
5471 { "scrollright", 0, 0, GDK_Right
},
5472 { "scrollleft", 0, 0, GDK_Left
},
5473 { "scrollleft", 0, 0, GDK_h
},
5474 { "scrollfarright", 0, 0, GDK_dollar
},
5475 { "scrollfarleft", 0, 0, GDK_0
},
5478 { "tabnew", CTRL
, 0, GDK_t
},
5479 { "999tabnew", CTRL
, 0, GDK_T
},
5480 { "tabclose", CTRL
, 1, GDK_w
},
5481 { "tabundoclose", 0, 0, GDK_U
},
5482 { "tabnext 1", CTRL
, 0, GDK_1
},
5483 { "tabnext 2", CTRL
, 0, GDK_2
},
5484 { "tabnext 3", CTRL
, 0, GDK_3
},
5485 { "tabnext 4", CTRL
, 0, GDK_4
},
5486 { "tabnext 5", CTRL
, 0, GDK_5
},
5487 { "tabnext 6", CTRL
, 0, GDK_6
},
5488 { "tabnext 7", CTRL
, 0, GDK_7
},
5489 { "tabnext 8", CTRL
, 0, GDK_8
},
5490 { "tabnext 9", CTRL
, 0, GDK_9
},
5491 { "tabfirst", CTRL
, 0, GDK_less
},
5492 { "tablast", CTRL
, 0, GDK_greater
},
5493 { "tabprevious", CTRL
, 0, GDK_Left
},
5494 { "tabnext", CTRL
, 0, GDK_Right
},
5495 { "focusout", CTRL
, 0, GDK_minus
},
5496 { "focusin", CTRL
, 0, GDK_plus
},
5497 { "focusin", CTRL
, 0, GDK_equal
},
5498 { "focusreset", CTRL
, 0, GDK_0
},
5500 /* command aliases (handy when -S flag is used) */
5501 { "promptopen", 0, 0, GDK_F9
},
5502 { "promptopencurrent", 0, 0, GDK_F10
},
5503 { "prompttabnew", 0, 0, GDK_F11
},
5504 { "prompttabnewcurrent",0, 0, GDK_F12
},
5506 TAILQ_HEAD(keybinding_list
, key_binding
);
5509 walk_kb(struct settings
*s
,
5510 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5512 struct key_binding
*k
;
5515 if (s
== NULL
|| cb
== NULL
) {
5516 show_oops(NULL
, "walk_kb invalid parameters");
5520 TAILQ_FOREACH(k
, &kbl
, entry
) {
5526 if (gdk_keyval_name(k
->key
) == NULL
)
5529 strlcat(str
, k
->cmd
, sizeof str
);
5530 strlcat(str
, ",", sizeof str
);
5532 if (k
->mask
& GDK_SHIFT_MASK
)
5533 strlcat(str
, "S-", sizeof str
);
5534 if (k
->mask
& GDK_CONTROL_MASK
)
5535 strlcat(str
, "C-", sizeof str
);
5536 if (k
->mask
& GDK_MOD1_MASK
)
5537 strlcat(str
, "M1-", sizeof str
);
5538 if (k
->mask
& GDK_MOD2_MASK
)
5539 strlcat(str
, "M2-", sizeof str
);
5540 if (k
->mask
& GDK_MOD3_MASK
)
5541 strlcat(str
, "M3-", sizeof str
);
5542 if (k
->mask
& GDK_MOD4_MASK
)
5543 strlcat(str
, "M4-", sizeof str
);
5544 if (k
->mask
& GDK_MOD5_MASK
)
5545 strlcat(str
, "M5-", sizeof str
);
5547 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5548 cb(s
, str
, cb_args
);
5553 init_keybindings(void)
5556 struct key_binding
*k
;
5558 for (i
= 0; i
< LENGTH(keys
); i
++) {
5559 k
= g_malloc0(sizeof *k
);
5560 k
->cmd
= keys
[i
].cmd
;
5561 k
->mask
= keys
[i
].mask
;
5562 k
->use_in_entry
= keys
[i
].use_in_entry
;
5563 k
->key
= keys
[i
].key
;
5564 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5566 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5567 k
->cmd
? k
->cmd
: "unnamed key");
5572 keybinding_clearall(void)
5574 struct key_binding
*k
, *next
;
5576 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5577 next
= TAILQ_NEXT(k
, entry
);
5581 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5582 k
->cmd
? k
->cmd
: "unnamed key");
5583 TAILQ_REMOVE(&kbl
, k
, entry
);
5589 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5591 struct key_binding
*k
;
5592 guint keyval
, mask
= 0;
5595 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5597 /* Keys which are to be used in entry have been prefixed with an
5598 * exclamation mark. */
5602 /* find modifier keys */
5603 if (strstr(key
, "S-"))
5604 mask
|= GDK_SHIFT_MASK
;
5605 if (strstr(key
, "C-"))
5606 mask
|= GDK_CONTROL_MASK
;
5607 if (strstr(key
, "M1-"))
5608 mask
|= GDK_MOD1_MASK
;
5609 if (strstr(key
, "M2-"))
5610 mask
|= GDK_MOD2_MASK
;
5611 if (strstr(key
, "M3-"))
5612 mask
|= GDK_MOD3_MASK
;
5613 if (strstr(key
, "M4-"))
5614 mask
|= GDK_MOD4_MASK
;
5615 if (strstr(key
, "M5-"))
5616 mask
|= GDK_MOD5_MASK
;
5619 for (i
= strlen(key
) - 1; i
> 0; i
--)
5623 /* validate keyname */
5624 keyval
= gdk_keyval_from_name(key
);
5625 if (keyval
== GDK_VoidSymbol
) {
5626 warnx("invalid keybinding name %s", key
);
5629 /* must run this test too, gtk+ doesn't handle 10 for example */
5630 if (gdk_keyval_name(keyval
) == NULL
) {
5631 warnx("invalid keybinding name %s", key
);
5635 /* Remove eventual dupes. */
5636 TAILQ_FOREACH(k
, &kbl
, entry
)
5637 if (k
->key
== keyval
&& k
->mask
== mask
) {
5638 TAILQ_REMOVE(&kbl
, k
, entry
);
5644 k
= g_malloc0(sizeof *k
);
5645 k
->cmd
= g_strdup(cmd
);
5647 k
->use_in_entry
= use_in_entry
;
5650 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5655 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5656 k
->cmd
, gdk_keyval_name(keyval
));
5658 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5664 add_kb(struct settings
*s
, char *entry
)
5668 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5670 /* clearall is special */
5671 if (!strcmp(entry
, "clearall")) {
5672 keybinding_clearall();
5676 kb
= strstr(entry
, ",");
5682 return (keybinding_add(entry
, key
, key
[0] == '!'));
5688 int (*func
)(struct tab
*, struct karg
*);
5692 { "command", 0, command
, ':', 0 },
5693 { "search", 0, command
, '/', 0 },
5694 { "searchb", 0, command
, '?', 0 },
5695 { "togglesrc", 0, toggle_src
, 0, 0 },
5697 /* yanking and pasting */
5698 { "yankuri", 0, yank_uri
, 0, 0 },
5699 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5700 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5701 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5704 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5705 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5708 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5709 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5712 { "hinting", 0, hint
, 0, 0 },
5714 /* custom stylesheet */
5715 { "userstyle", 0, userstyle
, 0, 0 },
5718 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5719 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5720 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5722 /* vertical movement */
5723 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5724 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5725 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5726 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5727 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5728 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5729 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5730 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5731 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5732 /* horizontal movement */
5733 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5734 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5735 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5736 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5738 { "favorites", 0, xtp_page_fl
, 0, 0 },
5739 { "fav", 0, xtp_page_fl
, 0, 0 },
5740 { "favadd", 0, add_favorite
, 0, 0 },
5742 { "qall", 0, quit
, 0, 0 },
5743 { "quitall", 0, quit
, 0, 0 },
5744 { "w", 0, save_tabs
, 0, 0 },
5745 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5746 { "help", 0, help
, 0, 0 },
5747 { "about", 0, about
, 0, 0 },
5748 { "stats", 0, stats
, 0, 0 },
5749 { "version", 0, about
, 0, 0 },
5752 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5753 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5754 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5755 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5756 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5757 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5758 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5759 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5760 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5761 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5762 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5764 /* cookie command */
5765 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5766 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5767 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5768 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5769 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5770 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5771 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5772 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5773 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5774 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5775 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5777 /* toplevel (domain) command */
5778 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5779 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5782 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5785 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5786 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5787 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5789 { "ca", 0, ca_cmd
, 0, 0 },
5790 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5791 { "dl", 0, xtp_page_dl
, 0, 0 },
5792 { "h", 0, xtp_page_hl
, 0, 0 },
5793 { "history", 0, xtp_page_hl
, 0, 0 },
5794 { "home", 0, go_home
, 0, 0 },
5795 { "restart", 0, restart
, 0, 0 },
5796 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5797 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5798 { "statustoggle", 0, statustoggle
, 0, 0 },
5799 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5801 { "print", 0, print_page
, 0, 0 },
5804 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5805 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5806 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5807 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5808 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5809 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5810 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5811 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5812 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5813 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5814 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5815 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5816 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5817 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5818 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5819 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5820 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5821 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5822 { "buffers", 0, buffers
, 0, 0 },
5823 { "ls", 0, buffers
, 0, 0 },
5824 { "tabs", 0, buffers
, 0, 0 },
5826 /* command aliases (handy when -S flag is used) */
5827 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5828 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5829 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5830 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5833 { "set", 0, set
, 0, XT_USERARG
},
5835 { "fullscreen", 0, fullscreen
, 0, 0 },
5836 { "f", 0, fullscreen
, 0, 0 },
5839 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5840 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5841 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5842 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5843 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5850 } cmd_status
= {-1, 0};
5853 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5856 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5863 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5870 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5872 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5878 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5880 a
.i
= XT_NAV_FORWARD
;
5890 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5892 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5894 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5901 * cancel, remove, etc. downloads
5904 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5906 struct download find
, *d
= NULL
;
5908 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5910 /* some commands require a valid download id */
5911 if (cmd
!= XT_XTP_DL_LIST
) {
5912 /* lookup download in question */
5914 d
= RB_FIND(download_list
, &downloads
, &find
);
5917 show_oops(t
, "%s: no such download", __func__
);
5922 /* decide what to do */
5924 case XT_XTP_DL_CANCEL
:
5925 webkit_download_cancel(d
->download
);
5927 case XT_XTP_DL_REMOVE
:
5928 webkit_download_cancel(d
->download
); /* just incase */
5929 g_object_unref(d
->download
);
5930 RB_REMOVE(download_list
, &downloads
, d
);
5932 case XT_XTP_DL_LIST
:
5936 show_oops(t
, "%s: unknown command", __func__
);
5939 xtp_page_dl(t
, NULL
);
5943 * Actions on history, only does one thing for now, but
5944 * we provide the function for future actions
5947 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5949 struct history
*h
, *next
;
5953 case XT_XTP_HL_REMOVE
:
5954 /* walk backwards, as listed in reverse */
5955 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5956 next
= RB_PREV(history_list
, &hl
, h
);
5958 RB_REMOVE(history_list
, &hl
, h
);
5959 g_free((gpointer
) h
->title
);
5960 g_free((gpointer
) h
->uri
);
5967 case XT_XTP_HL_LIST
:
5968 /* Nothing - just xtp_page_hl() below */
5971 show_oops(t
, "%s: unknown command", __func__
);
5975 xtp_page_hl(t
, NULL
);
5978 /* remove a favorite */
5980 remove_favorite(struct tab
*t
, int index
)
5982 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5983 char *new_favs
, *tmp
;
5988 /* open favorites */
5989 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5991 if ((f
= fopen(file
, "r")) == NULL
) {
5992 show_oops(t
, "%s: can't open favorites: %s",
5993 __func__
, strerror(errno
));
5997 /* build a string which will become the new favroites file */
5998 new_favs
= g_strdup("");
6001 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6002 if (feof(f
) || ferror(f
))
6004 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6011 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6012 if (feof(f
) || ferror(f
)) {
6013 show_oops(t
, "%s: can't parse favorites %s",
6014 __func__
, strerror(errno
));
6019 /* as long as this isn't the one we are deleting add to file */
6022 new_favs
= g_strdup_printf("%s%s\n%s\n",
6023 new_favs
, title
, uri
);
6035 /* write back new favorites file */
6036 if ((f
= fopen(file
, "w")) == NULL
) {
6037 show_oops(t
, "%s: can't open favorites: %s",
6038 __func__
, strerror(errno
));
6042 fwrite(new_favs
, strlen(new_favs
), 1, f
);
6055 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6058 case XT_XTP_FL_LIST
:
6059 /* nothing, just the below call to xtp_page_fl() */
6061 case XT_XTP_FL_REMOVE
:
6062 remove_favorite(t
, arg
);
6065 show_oops(t
, "%s: invalid favorites command", __func__
);
6069 xtp_page_fl(t
, NULL
);
6073 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6076 case XT_XTP_CL_LIST
:
6077 /* nothing, just xtp_page_cl() */
6079 case XT_XTP_CL_REMOVE
:
6083 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6087 xtp_page_cl(t
, NULL
);
6090 /* link an XTP class to it's session key and handler function */
6091 struct xtp_despatch
{
6094 void (*handle_func
)(struct tab
*, uint8_t, int);
6097 struct xtp_despatch xtp_despatches
[] = {
6098 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6099 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6100 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6101 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6102 { XT_XTP_INVALID
, NULL
, NULL
}
6106 * is the url xtp protocol? (xxxt://)
6107 * if so, parse and despatch correct bahvior
6110 parse_xtp_url(struct tab
*t
, const char *url
)
6112 char *dup
= NULL
, *p
, *last
;
6113 uint8_t n_tokens
= 0;
6114 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6115 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6120 * tokens array meaning:
6122 * tokens[1] = session key
6123 * tokens[2] = action
6124 * tokens[3] = optional argument
6127 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6129 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6132 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6134 /* split out the url */
6135 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6136 (p
= strtok_r(NULL
, "/", &last
))) {
6138 tokens
[n_tokens
++] = p
;
6141 /* should be atleast three fields 'class/seskey/command/arg' */
6145 dsp
= xtp_despatches
;
6146 req_class
= atoi(tokens
[0]);
6147 while (dsp
->xtp_class
) {
6148 if (dsp
->xtp_class
== req_class
) {
6155 /* did we find one atall? */
6156 if (dsp_match
== NULL
) {
6157 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6161 /* check session key and call despatch function */
6162 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6163 ret
= TRUE
; /* all is well, this was a valid xtp request */
6164 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6177 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6179 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6181 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6184 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6189 show_oops(t
, "activate_uri_entry_cb no uri");
6193 uri
+= strspn(uri
, "\t ");
6195 /* if xxxt:// treat specially */
6196 if (parse_xtp_url(t
, uri
))
6199 /* otherwise continue to load page normally */
6200 load_uri(t
, (gchar
*)uri
);
6205 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6207 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6208 char *newuri
= NULL
;
6211 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6214 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6218 if (search_string
== NULL
) {
6219 show_oops(t
, "no search_string");
6223 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6225 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6226 newuri
= g_strdup_printf(search_string
, enc_search
);
6230 webkit_web_view_load_uri(t
->wv
, newuri
);
6238 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6240 struct domain
*d
= NULL
;
6243 if (uri
== NULL
|| t
== NULL
)
6246 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6251 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6252 es
? "enable" : "disable", uri
);
6254 g_object_set(G_OBJECT(t
->settings
),
6255 "enable-html5-local-storage", es
, (char *)NULL
);
6256 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6260 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6262 struct domain
*d
= NULL
;
6265 if (uri
== NULL
|| t
== NULL
)
6268 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6273 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6274 es
? "enable" : "disable", uri
);
6276 g_object_set(G_OBJECT(t
->settings
),
6277 "enable-scripts", es
, (char *)NULL
);
6278 g_object_set(G_OBJECT(t
->settings
),
6279 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6280 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6282 button_set_stockid(t
->js_toggle
,
6283 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6287 color_address_bar(gpointer p
)
6290 struct tab
*tt
, *t
= p
;
6291 gchar
*col_str
= XT_COLOR_YELLOW
;
6293 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6295 /* make sure t still exists */
6298 TAILQ_FOREACH(tt
, &tabs
, entry
)
6304 switch (load_compare_cert(t
, NULL
)) {
6306 col_str
= XT_COLOR_BLUE
;
6309 col_str
= XT_COLOR_GREEN
;
6311 case CERT_UNTRUSTED
:
6312 col_str
= XT_COLOR_YELLOW
;
6315 col_str
= XT_COLOR_RED
;
6319 gdk_color_parse(col_str
, &color
);
6320 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6322 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6323 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6325 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6329 return (FALSE
/* kill thread */);
6333 show_ca_status(struct tab
*t
, const char *uri
)
6336 gchar
*col_str
= XT_COLOR_WHITE
;
6338 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6339 ssl_strict_certs
, ssl_ca_file
, uri
);
6346 if (ssl_ca_file
== NULL
) {
6347 if (g_str_has_prefix(uri
, "http://"))
6349 if (g_str_has_prefix(uri
, "https://")) {
6350 col_str
= XT_COLOR_RED
;
6355 if (g_str_has_prefix(uri
, "http://") ||
6356 !g_str_has_prefix(uri
, "https://"))
6359 /* thread the coloring of the address bar */
6360 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
,
6361 color_address_bar
, t
, NULL
);
6366 gdk_color_parse(col_str
, &color
);
6367 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6369 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6370 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6372 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6377 free_favicon(struct tab
*t
)
6379 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6380 __func__
, t
->icon_download
, t
->icon_request
);
6382 if (t
->icon_request
)
6383 g_object_unref(t
->icon_request
);
6384 if (t
->icon_dest_uri
)
6385 g_free(t
->icon_dest_uri
);
6387 t
->icon_request
= NULL
;
6388 t
->icon_dest_uri
= NULL
;
6392 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6394 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6395 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6397 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6398 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6400 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6401 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6405 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6407 GdkPixbuf
*pb_scaled
;
6409 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6410 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6411 GDK_INTERP_BILINEAR
);
6415 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6416 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6418 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6419 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6421 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6422 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6424 if (pb_scaled
!= pb
)
6425 g_object_unref(pb_scaled
);
6429 xt_icon_from_file(struct tab
*t
, char *file
)
6433 if (g_str_has_prefix(file
, "file://"))
6434 file
+= strlen("file://");
6436 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6438 xt_icon_from_pixbuf(t
, pb
);
6441 xt_icon_from_name(t
, "text-html");
6445 is_valid_icon(char *file
)
6448 const char *mime_type
;
6452 gf
= g_file_new_for_path(file
);
6453 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6455 mime_type
= g_file_info_get_content_type(fi
);
6456 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6457 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6458 g_strcmp0(mime_type
, "image/png") == 0 ||
6459 g_strcmp0(mime_type
, "image/gif") == 0 ||
6460 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6468 set_favicon_from_file(struct tab
*t
, char *file
)
6472 if (t
== NULL
|| file
== NULL
)
6475 if (g_str_has_prefix(file
, "file://"))
6476 file
+= strlen("file://");
6477 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6479 if (!stat(file
, &sb
)) {
6480 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6481 /* corrupt icon so trash it */
6482 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6485 /* no need to set icon to default here */
6489 xt_icon_from_file(t
, file
);
6493 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6496 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6497 struct tab
*tt
= NULL
, *t
= NULL
;
6500 * find the webview instead of passing in the tab as it could have been
6501 * deleted from underneath us.
6503 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6512 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6513 __func__
, t
->tab_id
, status
);
6516 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6518 t
->icon_download
= NULL
;
6521 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6524 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6527 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6529 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6530 __func__
, t
->tab_id
);
6531 t
->icon_download
= NULL
;
6534 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6537 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6538 __func__
, t
->icon_dest_uri
);
6539 set_favicon_from_file(t
, t
->icon_dest_uri
);
6540 /* these will be freed post callback */
6541 t
->icon_request
= NULL
;
6542 t
->icon_download
= NULL
;
6550 abort_favicon_download(struct tab
*t
)
6552 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6554 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6555 if (t
->icon_download
) {
6556 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6557 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6558 webkit_download_cancel(t
->icon_download
);
6559 t
->icon_download
= NULL
;
6564 xt_icon_from_name(t
, "text-html");
6568 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6570 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6572 if (uri
== NULL
|| t
== NULL
)
6575 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6576 /* take icon from WebKitIconDatabase */
6579 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6581 xt_icon_from_pixbuf(t
, pb
);
6584 xt_icon_from_name(t
, "text-html");
6585 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6586 /* download icon to cache dir */
6587 gchar
*name_hash
, file
[PATH_MAX
];
6590 if (t
->icon_request
) {
6591 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6595 /* check to see if we got the icon in cache */
6596 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6597 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6600 if (!stat(file
, &sb
)) {
6601 if (sb
.st_size
> 0) {
6602 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6604 set_favicon_from_file(t
, file
);
6608 /* corrupt icon so trash it */
6609 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6614 /* create download for icon */
6615 t
->icon_request
= webkit_network_request_new(uri
);
6616 if (t
->icon_request
== NULL
) {
6617 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6622 t
->icon_download
= webkit_download_new(t
->icon_request
);
6623 if (t
->icon_download
== NULL
)
6626 /* we have to free icon_dest_uri later */
6627 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6628 webkit_download_set_destination_uri(t
->icon_download
,
6631 if (webkit_download_get_status(t
->icon_download
) ==
6632 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6633 g_object_unref(t
->icon_request
);
6634 g_free(t
->icon_dest_uri
);
6635 t
->icon_request
= NULL
;
6636 t
->icon_dest_uri
= NULL
;
6640 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6641 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6643 webkit_download_start(t
->icon_download
);
6648 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6650 const gchar
*uri
= NULL
, *title
= NULL
;
6651 struct history
*h
, find
;
6655 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6656 webkit_web_view_get_load_status(wview
),
6657 get_uri(t
) ? get_uri(t
) : "NOTHING");
6660 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6664 switch (webkit_web_view_get_load_status(wview
)) {
6665 case WEBKIT_LOAD_PROVISIONAL
:
6667 abort_favicon_download(t
);
6668 #if GTK_CHECK_VERSION(2, 20, 0)
6669 gtk_widget_show(t
->spinner
);
6670 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6672 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6674 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6676 /* assume we are a new address */
6677 gdk_color_parse("white", &color
);
6678 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6679 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6681 /* take focus if we are visible */
6687 case WEBKIT_LOAD_COMMITTED
:
6692 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6698 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6700 /* check if js white listing is enabled */
6701 if (enable_cookie_whitelist
)
6702 check_and_set_cookie(uri
, t
);
6703 if (enable_js_whitelist
)
6704 check_and_set_js(uri
, t
);
6710 /* we know enough to autosave the session */
6711 if (session_autosave
) {
6716 show_ca_status(t
, uri
);
6719 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6723 case WEBKIT_LOAD_FINISHED
:
6729 if (!strncmp(uri
, "http://", strlen("http://")) ||
6730 !strncmp(uri
, "https://", strlen("https://")) ||
6731 !strncmp(uri
, "file://", strlen("file://"))) {
6733 h
= RB_FIND(history_list
, &hl
, &find
);
6735 title
= get_title(t
, FALSE
);
6736 h
= g_malloc(sizeof *h
);
6737 h
->uri
= g_strdup(uri
);
6738 h
->title
= g_strdup(title
);
6739 RB_INSERT(history_list
, &hl
, h
);
6740 completion_add_uri(h
->uri
);
6741 update_history_tabs(NULL
);
6745 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6746 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6747 case WEBKIT_LOAD_FAILED
:
6750 #if GTK_CHECK_VERSION(2, 20, 0)
6751 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6752 gtk_widget_hide(t
->spinner
);
6755 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6760 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6762 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6763 can_go_back_for_real(t
));
6765 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6766 can_go_forward_for_real(t
));
6771 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6772 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6775 * XXX this function is wrong
6776 * it overwrites perfectly good urls with garbage on load errors
6777 * those happen often when popups fail to resolve dns
6781 t
->tmp_uri
= g_strdup(uri
);
6782 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6783 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6784 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6785 set_status(t
, uri
, XT_STATUS_NOTHING
);
6792 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6794 const gchar
*title
= NULL
, *win_title
= NULL
;
6796 title
= get_title(t
, FALSE
);
6797 win_title
= get_title(t
, TRUE
);
6798 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6799 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6800 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6801 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6805 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6807 run_script(t
, JS_HINTING
);
6811 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6813 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6814 progress
== 100 ? 0 : (double)progress
/ 100);
6815 if (show_url
== 0) {
6816 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6817 progress
== 100 ? 0 : (double)progress
/ 100);
6820 update_statusbar_position(NULL
, NULL
);
6824 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6825 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6826 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6829 WebKitWebNavigationReason reason
;
6830 struct domain
*d
= NULL
;
6833 show_oops(NULL
, "webview_npd_cb invalid parameters");
6837 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6839 webkit_network_request_get_uri(request
));
6841 uri
= (char *)webkit_network_request_get_uri(request
);
6843 /* if this is an xtp url, we don't load anything else */
6844 if (parse_xtp_url(t
, uri
))
6847 if (t
->ctrl_click
) {
6849 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6850 webkit_web_policy_decision_ignore(pd
);
6851 return (TRUE
); /* we made the decission */
6855 * This is a little hairy but it comes down to this:
6856 * when we run in whitelist mode we have to assist the browser in
6857 * opening the URL that it would have opened in a new tab.
6859 reason
= webkit_web_navigation_action_get_reason(na
);
6860 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6861 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6862 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6863 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6865 webkit_web_policy_decision_use(pd
);
6866 return (TRUE
); /* we made the decision */
6873 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6876 struct domain
*d
= NULL
;
6878 WebKitWebView
*webview
= NULL
;
6880 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6881 webkit_web_view_get_uri(wv
));
6884 /* open in current tab */
6886 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6887 uri
= webkit_web_view_get_uri(wv
);
6888 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6891 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6893 } else if (enable_scripts
== 1) {
6894 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6902 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6905 struct domain
*d
= NULL
;
6907 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6909 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6910 uri
= webkit_web_view_get_uri(wv
);
6911 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6915 } else if (enable_scripts
== 1)
6922 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6924 /* we can not eat the event without throwing gtk off so defer it */
6926 /* catch middle click */
6927 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6932 /* catch ctrl click */
6933 if (e
->type
== GDK_BUTTON_RELEASE
&&
6934 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6939 return (XT_CB_PASSTHROUGH
);
6943 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6945 struct mime_type
*m
;
6947 m
= find_mime_type(mime_type
);
6955 show_oops(t
, "can't fork mime handler");
6965 execlp(m
->mt_action
, m
->mt_action
,
6966 webkit_network_request_get_uri(request
), (void *)NULL
);
6975 get_mime_type(char *file
)
6977 const char *mime_type
;
6981 if (g_str_has_prefix(file
, "file://"))
6982 file
+= strlen("file://");
6984 gf
= g_file_new_for_path(file
);
6985 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6987 mime_type
= g_file_info_get_content_type(fi
);
6995 run_download_mimehandler(char *mime_type
, char *file
)
6997 struct mime_type
*m
;
6999 m
= find_mime_type(mime_type
);
7005 show_oops(NULL
, "can't fork download mime handler");
7015 if (g_str_has_prefix(file
, "file://"))
7016 file
+= strlen("file://");
7017 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7026 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7029 WebKitDownloadStatus status
;
7030 const gchar
*file
= NULL
, *mime
= NULL
;
7032 if (download
== NULL
)
7034 status
= webkit_download_get_status(download
);
7035 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7038 file
= webkit_download_get_destination_uri(download
);
7041 mime
= get_mime_type((char *)file
);
7045 run_download_mimehandler((char *)mime
, (char *)file
);
7049 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7050 WebKitNetworkRequest
*request
, char *mime_type
,
7051 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7054 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7058 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7059 t
->tab_id
, mime_type
);
7061 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7062 webkit_web_policy_decision_ignore(decision
);
7067 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7068 webkit_web_policy_decision_download(decision
);
7076 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7080 const gchar
*suggested_name
;
7081 gchar
*filename
= NULL
;
7083 struct download
*download_entry
;
7086 if (wk_download
== NULL
|| t
== NULL
) {
7087 show_oops(NULL
, "%s invalid parameters", __func__
);
7091 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7092 if (suggested_name
== NULL
)
7093 return (FALSE
); /* abort download */
7104 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7106 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7107 filename
: suggested_name
);
7109 } while (!stat(uri
+ strlen("file://"), &sb
));
7111 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7112 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7114 webkit_download_set_destination_uri(wk_download
, uri
);
7116 if (webkit_download_get_status(wk_download
) ==
7117 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7118 show_oops(t
, "%s: download failed to start", __func__
);
7120 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7122 /* connect "download first" mime handler */
7123 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7124 G_CALLBACK(download_status_changed_cb
), NULL
);
7126 download_entry
= g_malloc(sizeof(struct download
));
7127 download_entry
->download
= wk_download
;
7128 download_entry
->tab
= t
;
7129 download_entry
->id
= next_download_id
++;
7130 RB_INSERT(download_list
, &downloads
, download_entry
);
7131 /* get from history */
7132 g_object_ref(wk_download
);
7133 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7134 show_oops(t
, "Download of '%s' started...",
7135 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7144 /* sync other download manager tabs */
7145 update_download_tabs(NULL
);
7148 * NOTE: never redirect/render the current tab before this
7149 * function returns. This will cause the download to never start.
7151 return (ret
); /* start download */
7155 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7157 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7160 show_oops(NULL
, "webview_hover_cb");
7165 set_status(t
, uri
, XT_STATUS_LINK
);
7168 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7173 mark(struct tab
*t
, struct karg
*arg
)
7179 if ((index
= marktoindex(mark
)) == -1)
7182 if (arg
->i
== XT_MARK_SET
)
7183 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7184 else if (arg
->i
== XT_MARK_GOTO
) {
7185 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7186 show_oops(t
, "mark '%c' does not exist", mark
);
7189 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7190 something changes the document size */
7191 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7198 marks_clear(struct tab
*t
)
7202 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7203 t
->mark
[i
] = XT_INVALID_MARK
;
7209 char file
[PATH_MAX
];
7210 char *line
= NULL
, *p
, mark
;
7215 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7216 if ((f
= fopen(file
, "r+")) == NULL
) {
7217 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7221 for (i
= 1; ; i
++) {
7222 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7223 if (feof(f
) || ferror(f
))
7225 if (strlen(line
) == 0 || line
[0] == '#') {
7231 p
= strtok(line
, " \t");
7233 if (p
== NULL
|| strlen(p
) != 1 ||
7234 (index
= marktoindex(*p
)) == -1) {
7235 warnx("corrupt quickmarks file, line %d", i
);
7240 p
= strtok(NULL
, " \t");
7241 if (qmarks
[index
] != NULL
)
7242 g_free(qmarks
[index
]);
7243 qmarks
[index
] = g_strdup(p
);
7254 char file
[PATH_MAX
];
7258 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7259 if ((f
= fopen(file
, "r+")) == NULL
) {
7260 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7264 for (i
= 0; i
< XT_NOMARKS
; i
++)
7265 if (qmarks
[i
] != NULL
)
7266 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7274 qmark(struct tab
*t
, struct karg
*arg
)
7279 mark
= arg
->s
[strlen(arg
->s
)-1];
7280 index
= marktoindex(mark
);
7286 if (qmarks
[index
] != NULL
)
7287 g_free(qmarks
[index
]);
7289 qmarks_load(); /* sync if multiple instances */
7290 qmarks
[index
] = g_strdup(get_uri(t
));
7294 if (qmarks
[index
] != NULL
)
7295 load_uri(t
, qmarks
[index
]);
7297 show_oops(t
, "quickmark \"%c\" does not exist",
7303 if (qmarks
[index
] != NULL
)
7304 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7306 show_oops(t
, "quickmark \"%c\" does not exist",
7317 go_up(struct tab
*t
, struct karg
*args
)
7323 levels
= atoi(args
->s
);
7327 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7328 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7330 tmp
+= strlen(XT_PROTO_DELIM
);
7332 /* if an uri starts with a slash, leave it alone (for file:///) */
7339 p
= strrchr(tmp
, '/');
7353 gototab(struct tab
*t
, struct karg
*args
)
7356 struct karg arg
= {0, NULL
, -1};
7358 tab
= atoi(args
->s
);
7360 arg
.i
= XT_TAB_NEXT
;
7369 zoom_amount(struct tab
*t
, struct karg
*arg
)
7371 struct karg narg
= {0, NULL
, -1};
7373 narg
.i
= atoi(arg
->s
);
7374 resizetab(t
, &narg
);
7380 flip_colon(struct tab
*t
, struct karg
*arg
)
7382 struct karg narg
= {0, NULL
, -1};
7385 if (t
== NULL
|| arg
== NULL
)
7388 p
= strstr(arg
->s
, ":");
7400 /* buffer commands receive the regex that triggered them in arg.s */
7401 char bcmd
[XT_BUFCMD_SZ
];
7405 #define XT_PRE_NO (0)
7406 #define XT_PRE_YES (1)
7407 #define XT_PRE_MAYBE (2)
7409 int (*func
)(struct tab
*, struct karg
*);
7413 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7414 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7415 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7416 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7417 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7418 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7419 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7420 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7421 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7422 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7423 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7424 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7425 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7426 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7427 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7428 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7429 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7430 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7434 buffercmd_init(void)
7438 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7439 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7440 REG_EXTENDED
| REG_NOSUB
))
7441 startpage_add("invalid buffercmd regex %s",
7442 buffercmds
[i
].regex
);
7446 buffercmd_abort(struct tab
*t
)
7450 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7451 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7454 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7455 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7459 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7461 struct karg arg
= {0, NULL
, -1};
7464 arg
.s
= g_strdup(bcmd
);
7466 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7467 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7477 buffercmd_addkey(struct tab
*t
, guint keyval
)
7480 char s
[XT_BUFCMD_SZ
];
7482 if (keyval
== GDK_Escape
) {
7484 return (XT_CB_HANDLED
);
7487 /* key with modifier or non-ascii character */
7488 if (!isascii(keyval
))
7489 return (XT_CB_PASSTHROUGH
);
7491 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7492 "to buffer \"%s\"\n", keyval
, bcmd
);
7494 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7495 if (bcmd
[i
] == '\0') {
7500 /* buffer full, ignore input */
7501 if (i
>= LENGTH(bcmd
) -1) {
7502 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7504 return (XT_CB_HANDLED
);
7507 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7509 /* find exact match */
7510 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7511 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7512 (size_t) 0, NULL
, 0) == 0) {
7513 buffercmd_execute(t
, &buffercmds
[i
]);
7517 /* find non exact matches to see if we need to abort ot not */
7518 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7519 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7522 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7523 if (isdigit(bcmd
[0])) {
7524 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7528 if (sscanf(bcmd
, "%s", s
) == 0)
7531 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7532 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7535 if (sscanf(bcmd
, "%s", s
) == 0)
7538 if (c
== -1 && buffercmds
[i
].precount
)
7540 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7543 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7544 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7547 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7552 return (XT_CB_HANDLED
);
7556 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7558 struct key_binding
*k
;
7560 /* handle keybindings if buffercmd is empty.
7561 if not empty, allow commands like C-n */
7562 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7563 TAILQ_FOREACH(k
, &kbl
, entry
)
7564 if (e
->keyval
== k
->key
7565 && (entry
? k
->use_in_entry
: 1)) {
7567 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7568 return (cmd_execute(t
, k
->cmd
));
7569 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7570 return (cmd_execute(t
, k
->cmd
));
7574 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7575 return buffercmd_addkey(t
, e
->keyval
);
7577 return (XT_CB_PASSTHROUGH
);
7581 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7583 char s
[2], buf
[128];
7584 const char *errstr
= NULL
;
7586 /* don't use w directly; use t->whatever instead */
7589 show_oops(NULL
, "wv_keypress_after_cb");
7590 return (XT_CB_PASSTHROUGH
);
7593 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7594 e
->keyval
, e
->state
, t
);
7598 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7600 return (XT_CB_HANDLED
);
7604 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7606 /* we have a string */
7608 /* we have a number */
7609 snprintf(buf
, sizeof buf
,
7610 "vimprobable_fire(%s)", t
->hint_num
);
7617 /* XXX unfuck this */
7618 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7619 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7620 /* last input was numerical */
7622 l
= strlen(t
->hint_num
);
7629 t
->hint_num
[l
] = '\0';
7633 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7634 /* last input was alphanumerical */
7636 l
= strlen(t
->hint_buf
);
7643 t
->hint_buf
[l
] = '\0';
7653 /* numerical input */
7654 if (CLEAN(e
->state
) == 0 &&
7655 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7656 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7657 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7658 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7659 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7663 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7664 "invalid link number\n");
7667 snprintf(buf
, sizeof buf
,
7668 "vimprobable_update_hints(%s)",
7670 t
->hint_mode
= XT_HINT_NUMERICAL
;
7674 /* empty the counter buffer */
7675 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7676 return (XT_CB_HANDLED
);
7679 /* alphanumerical input */
7680 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7681 e
->keyval
<= GDK_z
) ||
7682 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7683 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7684 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7685 e
->keyval
<= GDK_9
) ||
7686 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7687 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7688 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7689 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7690 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7691 " %s\n", t
->hint_buf
);
7693 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7696 snprintf(buf
, sizeof buf
,
7697 "vimprobable_show_hints('%s')", t
->hint_buf
);
7698 t
->hint_mode
= XT_HINT_ALPHANUM
;
7701 /* empty the counter buffer */
7702 bzero(t
->hint_num
, sizeof t
->hint_num
);
7703 return (XT_CB_HANDLED
);
7706 return (XT_CB_HANDLED
);
7709 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7710 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7711 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7714 return (handle_keypress(t
, e
, 0));
7718 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7722 /* Hide buffers, if they are visible, with escape. */
7723 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7724 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7725 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7727 return (XT_CB_HANDLED
);
7730 return (XT_CB_PASSTHROUGH
);
7734 search_continue(struct tab
*t
)
7736 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7737 gboolean rv
= FALSE
;
7741 if (strlen(c
) == 1) {
7742 webkit_web_view_unmark_text_matches(t
->wv
);
7747 t
->search_forward
= TRUE
;
7748 else if (c
[0] == '?')
7749 t
->search_forward
= FALSE
;
7759 search_cb(struct tab
*t
)
7761 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7764 if (search_continue(t
) == FALSE
)
7768 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7770 /* not found, mark red */
7771 gdk_color_parse(XT_COLOR_RED
, &color
);
7772 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7773 /* unmark and remove selection */
7774 webkit_web_view_unmark_text_matches(t
->wv
);
7775 /* my kingdom for a way to unselect text in webview */
7777 /* found, highlight all */
7778 webkit_web_view_unmark_text_matches(t
->wv
);
7779 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7780 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7781 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7782 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7790 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7792 const gchar
*c
= gtk_entry_get_text(w
);
7795 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7796 return (XT_CB_PASSTHROUGH
);
7799 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7800 e
->keyval
, e
->state
, t
);
7802 if (search_continue(t
) == FALSE
)
7805 /* if search length is > 4 then no longer play timeout games */
7806 if (strlen(c
) > 4) {
7808 g_source_remove(t
->search_id
);
7815 /* reestablish a new timer if the user types fast */
7817 g_source_remove(t
->search_id
);
7818 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7821 return (XT_CB_PASSTHROUGH
);
7825 match_uri(const gchar
*uri
, const gchar
*key
) {
7828 gboolean match
= FALSE
;
7832 if (!strncmp(key
, uri
, len
))
7835 voffset
= strstr(uri
, "/") + 2;
7836 if (!strncmp(key
, voffset
, len
))
7838 else if (g_str_has_prefix(voffset
, "www.")) {
7839 voffset
= voffset
+ strlen("www.");
7840 if (!strncmp(key
, voffset
, len
))
7849 cmd_getlist(int id
, char *key
)
7854 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7855 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7856 if (match_uri(h
->uri
, key
)) {
7857 cmd_status
.list
[c
] = (char *)h
->uri
;
7866 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7868 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7869 if (cmds
[i
].level
< dep
)
7871 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7873 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7881 cmd_getnext(int dir
)
7883 cmd_status
.index
+= dir
;
7885 if (cmd_status
.index
< 0)
7886 cmd_status
.index
= cmd_status
.len
- 1;
7887 else if (cmd_status
.index
>= cmd_status
.len
)
7888 cmd_status
.index
= 0;
7890 return cmd_status
.list
[cmd_status
.index
];
7894 cmd_tokenize(char *s
, char *tokens
[])
7898 size_t len
= strlen(s
);
7901 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7902 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7903 tok
= strtok_r(NULL
, " ", &last
), i
++)
7913 cmd_complete(struct tab
*t
, char *str
, int dir
)
7915 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7916 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7918 char *tok
, *match
, *s
= g_strdup(str
);
7920 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7923 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7926 for (i
= 0; isdigit(s
[i
]); i
++)
7929 for (; isspace(s
[i
]); i
++)
7934 levels
= cmd_tokenize(s
, tokens
);
7936 for (i
= 0; i
< levels
- 1; i
++) {
7939 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7940 if (cmds
[j
].level
< dep
)
7942 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7946 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7953 if (matchcount
== 1) {
7954 strlcat(res
, tok
, sizeof res
);
7955 strlcat(res
, " ", sizeof res
);
7965 if (cmd_status
.index
== -1)
7966 cmd_getlist(parent
, tokens
[i
]);
7968 if (cmd_status
.len
> 0) {
7969 match
= cmd_getnext(dir
);
7970 strlcat(res
, match
, sizeof res
);
7971 gtk_entry_set_text(w
, res
);
7972 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7979 cmd_execute(struct tab
*t
, char *str
)
7981 struct cmd
*cmd
= NULL
;
7982 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7984 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7985 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7986 struct karg arg
= {0, NULL
, -1};
7991 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7997 while (isspace(s
[0]))
8000 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8001 prefix
= atoi(prefixstr
);
8005 for (tok
= strtok_r(s
, " ", &last
); tok
;
8006 tok
= strtok_r(NULL
, " ", &last
)) {
8008 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8009 if (cmds
[j
].level
< dep
)
8011 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8013 if (cmds
[j
].level
== dep
&&
8014 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8018 if (len
== strlen(cmds
[j
].cmd
)) {
8024 if (matchcount
== 1) {
8029 show_oops(t
, "Invalid command: %s", str
);
8037 arg
.precount
= prefix
;
8038 else if (cmd_prefix
> 0)
8039 arg
.precount
= cmd_prefix
;
8041 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8042 show_oops(t
, "No prefix allowed: %s", str
);
8046 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8047 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8048 arg
.precount
= atoi(arg
.s
);
8049 if (arg
.precount
<= 0) {
8050 if (arg
.s
[0] == '0')
8051 show_oops(t
, "Zero count");
8053 show_oops(t
, "Trailing characters");
8058 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8059 __func__
, arg
.precount
, arg
.s
);
8075 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8078 show_oops(NULL
, "entry_key_cb invalid parameters");
8079 return (XT_CB_PASSTHROUGH
);
8082 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8083 e
->keyval
, e
->state
, t
);
8087 if (e
->keyval
== GDK_Escape
) {
8088 /* don't use focus_webview(t) because we want to type :cmds */
8089 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8092 return (handle_keypress(t
, e
, 1));
8095 struct command_entry
*
8096 history_prev(struct command_list
*l
, struct command_entry
*at
)
8099 at
= TAILQ_LAST(l
, command_list
);
8101 at
= TAILQ_PREV(at
, command_list
, entry
);
8103 at
= TAILQ_LAST(l
, command_list
);
8109 struct command_entry
*
8110 history_next(struct command_list
*l
, struct command_entry
*at
)
8113 at
= TAILQ_FIRST(l
);
8115 at
= TAILQ_NEXT(at
, entry
);
8117 at
= TAILQ_FIRST(l
);
8124 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8126 int rv
= XT_CB_HANDLED
;
8127 const gchar
*c
= gtk_entry_get_text(w
);
8130 show_oops(NULL
, "cmd_keypress_cb parameters");
8131 return (XT_CB_PASSTHROUGH
);
8134 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8135 e
->keyval
, e
->state
, t
);
8139 e
->keyval
= GDK_Escape
;
8140 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8141 e
->keyval
= GDK_Escape
;
8143 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8144 e
->keyval
!= GDK_ISO_Left_Tab
)
8145 cmd_status
.index
= -1;
8147 switch (e
->keyval
) {
8150 cmd_complete(t
, (char *)&c
[1], 1);
8152 case GDK_ISO_Left_Tab
:
8154 cmd_complete(t
, (char *)&c
[1], -1);
8159 if ((search_at
= history_next(&shl
, search_at
))) {
8160 search_at
->line
[0] = c
[0];
8161 gtk_entry_set_text(w
, search_at
->line
);
8162 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8165 if ((history_at
= history_prev(&chl
, history_at
))) {
8166 history_at
->line
[0] = c
[0];
8167 gtk_entry_set_text(w
, history_at
->line
);
8168 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8175 if ((search_at
= history_next(&shl
, search_at
))) {
8176 search_at
->line
[0] = c
[0];
8177 gtk_entry_set_text(w
, search_at
->line
);
8178 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8181 if ((history_at
= history_next(&chl
, history_at
))) {
8182 history_at
->line
[0] = c
[0];
8183 gtk_entry_set_text(w
, history_at
->line
);
8184 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8190 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8198 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8199 webkit_web_view_unmark_text_matches(t
->wv
);
8203 rv
= XT_CB_PASSTHROUGH
;
8209 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8211 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8215 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8217 /* popup menu enabled */
8222 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8225 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8226 return (XT_CB_PASSTHROUGH
);
8229 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8230 t
->tab_id
, t
->popup
);
8232 /* if popup is enabled don't lose focus */
8235 return (XT_CB_PASSTHROUGH
);
8241 if (show_url
== 0 || t
->focus_wv
)
8244 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8246 return (XT_CB_PASSTHROUGH
);
8250 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8253 const gchar
*c
= gtk_entry_get_text(entry
);
8256 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8260 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8267 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8273 if (c
[0] == '/' || c
[0] == '?') {
8274 /* see if there is a timer pending */
8276 g_source_remove(t
->search_id
);
8281 if (t
->search_text
) {
8282 g_free(t
->search_text
);
8283 t
->search_text
= NULL
;
8286 t
->search_text
= g_strdup(s
);
8288 g_free(global_search
);
8289 global_search
= g_strdup(s
);
8290 t
->search_forward
= c
[0] == '/';
8292 history_add(&shl
, search_file
, s
, &search_history_count
);
8298 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8304 backward_cb(GtkWidget
*w
, struct tab
*t
)
8309 show_oops(NULL
, "backward_cb invalid parameters");
8313 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8320 forward_cb(GtkWidget
*w
, struct tab
*t
)
8325 show_oops(NULL
, "forward_cb invalid parameters");
8329 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8331 a
.i
= XT_NAV_FORWARD
;
8336 home_cb(GtkWidget
*w
, struct tab
*t
)
8339 show_oops(NULL
, "home_cb invalid parameters");
8343 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8349 stop_cb(GtkWidget
*w
, struct tab
*t
)
8351 WebKitWebFrame
*frame
;
8354 show_oops(NULL
, "stop_cb invalid parameters");
8358 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8360 frame
= webkit_web_view_get_main_frame(t
->wv
);
8361 if (frame
== NULL
) {
8362 show_oops(t
, "stop_cb: no frame");
8366 webkit_web_frame_stop_loading(frame
);
8367 abort_favicon_download(t
);
8371 setup_webkit(struct tab
*t
)
8373 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8374 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8375 FALSE
, (char *)NULL
);
8377 warnx("webkit does not have \"enable-dns-prefetching\" property");
8378 g_object_set(G_OBJECT(t
->settings
),
8379 "user-agent", t
->user_agent
, (char *)NULL
);
8380 g_object_set(G_OBJECT(t
->settings
),
8381 "enable-scripts", enable_scripts
, (char *)NULL
);
8382 g_object_set(G_OBJECT(t
->settings
),
8383 "enable-plugins", enable_plugins
, (char *)NULL
);
8384 g_object_set(G_OBJECT(t
->settings
),
8385 "javascript-can-open-windows-automatically", enable_scripts
,
8387 g_object_set(G_OBJECT(t
->settings
),
8388 "enable-html5-database", FALSE
, (char *)NULL
);
8389 g_object_set(G_OBJECT(t
->settings
),
8390 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8391 g_object_set(G_OBJECT(t
->settings
),
8392 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8393 g_object_set(G_OBJECT(t
->settings
),
8394 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8395 g_object_set(G_OBJECT(t
->wv
),
8396 "full-content-zoom", TRUE
, (char *)NULL
);
8398 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8402 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8404 struct tab
*ti
, *t
= NULL
;
8405 gdouble view_size
, value
, max
;
8408 TAILQ_FOREACH(ti
, &tabs
, entry
)
8409 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8417 if (adjustment
== NULL
)
8418 adjustment
= gtk_scrolled_window_get_vadjustment(
8419 GTK_SCROLLED_WINDOW(t
->browser_win
));
8421 view_size
= gtk_adjustment_get_page_size(adjustment
);
8422 value
= gtk_adjustment_get_value(adjustment
);
8423 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8426 position
= g_strdup("All");
8427 else if (value
== max
)
8428 position
= g_strdup("Bot");
8429 else if (value
== 0)
8430 position
= g_strdup("Top");
8432 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8434 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8441 create_browser(struct tab
*t
)
8445 GtkAdjustment
*adjustment
;
8448 show_oops(NULL
, "create_browser invalid parameters");
8452 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8453 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8454 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8455 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8457 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8458 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8459 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8461 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8462 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8465 t
->settings
= webkit_web_settings_new();
8467 if (user_agent
== NULL
) {
8468 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8470 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8473 t
->user_agent
= g_strdup(user_agent
);
8475 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8478 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8479 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8480 G_CALLBACK(update_statusbar_position
), NULL
);
8492 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8493 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8494 gtk_widget_set_name(w
, "xxxterm");
8495 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8496 g_signal_connect(G_OBJECT(w
), "delete_event",
8497 G_CALLBACK (gtk_main_quit
), NULL
);
8503 create_kiosk_toolbar(struct tab
*t
)
8505 GtkWidget
*toolbar
= NULL
, *b
;
8507 b
= gtk_hbox_new(FALSE
, 0);
8509 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8511 /* backward button */
8512 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8513 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8514 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8515 G_CALLBACK(backward_cb
), t
);
8516 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8518 /* forward button */
8519 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8520 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8521 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8522 G_CALLBACK(forward_cb
), t
);
8523 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8526 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8527 gtk_widget_set_sensitive(t
->gohome
, true);
8528 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8529 G_CALLBACK(home_cb
), t
);
8530 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8532 /* create widgets but don't use them */
8533 t
->uri_entry
= gtk_entry_new();
8534 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8535 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8536 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8542 create_toolbar(struct tab
*t
)
8544 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8546 b
= gtk_hbox_new(FALSE
, 0);
8548 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8550 /* backward button */
8551 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8552 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8553 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8554 G_CALLBACK(backward_cb
), t
);
8555 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8557 /* forward button */
8558 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8559 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8560 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8561 G_CALLBACK(forward_cb
), t
);
8562 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8566 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8567 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8568 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8569 G_CALLBACK(stop_cb
), t
);
8570 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8574 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8575 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8576 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8577 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8578 G_CALLBACK(js_toggle_cb
), t
);
8579 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8581 t
->uri_entry
= gtk_entry_new();
8582 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8583 G_CALLBACK(activate_uri_entry_cb
), t
);
8584 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8585 G_CALLBACK(entry_key_cb
), t
);
8587 eb1
= gtk_hbox_new(FALSE
, 0);
8588 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8589 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8590 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8593 if (search_string
) {
8595 t
->search_entry
= gtk_entry_new();
8596 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8597 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8598 G_CALLBACK(activate_search_entry_cb
), t
);
8599 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8600 G_CALLBACK(entry_key_cb
), t
);
8601 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8602 eb2
= gtk_hbox_new(FALSE
, 0);
8603 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8604 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8606 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8613 create_buffers(struct tab
*t
)
8615 GtkCellRenderer
*renderer
;
8618 view
= gtk_tree_view_new();
8620 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8622 renderer
= gtk_cell_renderer_text_new();
8623 gtk_tree_view_insert_column_with_attributes
8624 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
8626 renderer
= gtk_cell_renderer_text_new();
8627 gtk_tree_view_insert_column_with_attributes
8628 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8631 gtk_tree_view_set_model
8632 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8638 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8639 GtkTreeViewColumn
*col
, struct tab
*t
)
8644 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8646 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8649 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8650 set_current_tab(id
- 1);
8656 /* after tab reordering/creation/removal */
8663 TAILQ_FOREACH(t
, &tabs
, entry
) {
8664 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8665 if (t
->tab_id
> maxid
)
8668 gtk_widget_show(t
->tab_elems
.sep
);
8671 TAILQ_FOREACH(t
, &tabs
, entry
) {
8672 if (t
->tab_id
== maxid
) {
8673 gtk_widget_hide(t
->tab_elems
.sep
);
8679 /* after active tab change */
8681 recolor_compact_tabs(void)
8687 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8688 TAILQ_FOREACH(t
, &tabs
, entry
)
8689 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8692 curid
= gtk_notebook_get_current_page(notebook
);
8693 TAILQ_FOREACH(t
, &tabs
, entry
)
8694 if (t
->tab_id
== curid
) {
8695 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8696 gtk_widget_modify_fg(t
->tab_elems
.label
,
8697 GTK_STATE_NORMAL
, &color
);
8703 set_current_tab(int page_num
)
8705 buffercmd_abort(get_current_tab());
8706 gtk_notebook_set_current_page(notebook
, page_num
);
8707 recolor_compact_tabs();
8711 undo_close_tab_save(struct tab
*t
)
8715 struct undo
*u1
, *u2
;
8717 WebKitWebHistoryItem
*item
;
8719 if ((uri
= get_uri(t
)) == NULL
)
8722 u1
= g_malloc0(sizeof(struct undo
));
8723 u1
->uri
= g_strdup(uri
);
8725 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8727 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8728 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8731 /* forward history */
8732 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8736 u1
->history
= g_list_prepend(u1
->history
,
8737 webkit_web_history_item_copy(item
));
8738 items
= g_list_next(items
);
8743 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8744 u1
->history
= g_list_prepend(u1
->history
,
8745 webkit_web_history_item_copy(item
));
8749 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8753 u1
->history
= g_list_prepend(u1
->history
,
8754 webkit_web_history_item_copy(item
));
8755 items
= g_list_next(items
);
8758 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8760 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8761 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8762 TAILQ_REMOVE(&undos
, u2
, entry
);
8764 g_list_free(u2
->history
);
8773 delete_tab(struct tab
*t
)
8777 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8783 TAILQ_REMOVE(&tabs
, t
, entry
);
8785 /* Halt all webkit activity. */
8786 abort_favicon_download(t
);
8787 webkit_web_view_stop_loading(t
->wv
);
8789 /* Save the tab, so we can undo the close. */
8790 undo_close_tab_save(t
);
8792 if (browser_mode
== XT_BM_KIOSK
) {
8793 gtk_widget_destroy(t
->uri_entry
);
8794 gtk_widget_destroy(t
->stop
);
8795 gtk_widget_destroy(t
->js_toggle
);
8798 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8799 gtk_widget_destroy(t
->vbox
);
8803 g_source_remove(t
->search_id
);
8805 g_free(t
->user_agent
);
8806 g_free(t
->stylesheet
);
8810 if (TAILQ_EMPTY(&tabs
)) {
8811 if (browser_mode
== XT_BM_KIOSK
)
8812 create_new_tab(home
, NULL
, 1, -1);
8814 create_new_tab(NULL
, NULL
, 1, -1);
8817 /* recreate session */
8818 if (session_autosave
) {
8824 recolor_compact_tabs();
8828 update_statusbar_zoom(struct tab
*t
)
8831 char s
[16] = { '\0' };
8833 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8834 if ((zoom
<= 0.99 || zoom
>= 1.01))
8835 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8836 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8840 setzoom_webkit(struct tab
*t
, int adjust
)
8842 #define XT_ZOOMPERCENT 0.04
8847 show_oops(NULL
, "setzoom_webkit invalid parameters");
8851 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8852 if (adjust
== XT_ZOOM_IN
)
8853 zoom
+= XT_ZOOMPERCENT
;
8854 else if (adjust
== XT_ZOOM_OUT
)
8855 zoom
-= XT_ZOOMPERCENT
;
8856 else if (adjust
> 0)
8857 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8859 show_oops(t
, "setzoom_webkit invalid zoom value");
8863 if (zoom
< XT_ZOOMPERCENT
)
8864 zoom
= XT_ZOOMPERCENT
;
8865 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8866 update_statusbar_zoom(t
);
8870 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8872 struct tab
*t
= (struct tab
*) data
;
8874 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8876 switch (event
->button
) {
8878 set_current_tab(t
->tab_id
);
8889 append_tab(struct tab
*t
)
8894 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8895 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8899 create_sbe(int width
)
8903 sbe
= gtk_entry_new();
8904 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
8905 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
8906 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
8907 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
8908 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
8909 gtk_widget_set_size_request(sbe
, width
, -1);
8915 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8920 WebKitWebHistoryItem
*item
;
8924 int sbe_p
= 0, sbe_b
= 0,
8927 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8929 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8930 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8934 t
= g_malloc0(sizeof *t
);
8936 if (title
== NULL
) {
8937 title
= "(untitled)";
8941 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8943 /* label + button for tab */
8944 b
= gtk_hbox_new(FALSE
, 0);
8947 #if GTK_CHECK_VERSION(2, 20, 0)
8948 t
->spinner
= gtk_spinner_new();
8950 t
->label
= gtk_label_new(title
);
8951 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8952 gtk_widget_set_size_request(t
->label
, 100, 0);
8953 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8954 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8955 gtk_widget_set_size_request(b
, 130, 0);
8957 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8958 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8959 #if GTK_CHECK_VERSION(2, 20, 0)
8960 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8964 if (browser_mode
== XT_BM_KIOSK
) {
8965 t
->toolbar
= create_kiosk_toolbar(t
);
8966 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8969 t
->toolbar
= create_toolbar(t
);
8971 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8979 t
->browser_win
= create_browser(t
);
8980 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8982 /* oops message for user feedback */
8983 t
->oops
= gtk_entry_new();
8984 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8985 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8986 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8987 gdk_color_parse(XT_COLOR_RED
, &color
);
8988 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8989 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8990 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8993 t
->cmd
= gtk_entry_new();
8994 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8995 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8996 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8997 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9000 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9002 t
->sbe
.statusbar
= gtk_entry_new();
9003 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9004 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9005 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9006 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9008 /* create these widgets only if specified in statusbar_elems */
9010 t
->sbe
.position
= create_sbe(40);
9011 t
->sbe
.zoom
= create_sbe(40);
9012 t
->sbe
.buffercmd
= create_sbe(60);
9014 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9016 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9019 /* gtk widgets cannot be added to a box twice. sbe_* variables
9020 make sure of this */
9021 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9025 GtkWidget
*sep
= gtk_vseparator_new();
9027 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9028 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9029 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9030 FALSE
, FALSE
, FALSE
);
9035 warnx("flag \"%c\" specified more than "
9036 "once in statusbar_elems\n", *p
);
9040 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9041 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9045 warnx("flag \"%c\" specified more than "
9046 "once in statusbar_elems\n", *p
);
9050 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9051 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9055 warnx("flag \"%c\" specified more than "
9056 "once in statusbar_elems\n", *p
);
9060 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9061 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9064 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9069 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9072 t
->buffers
= create_buffers(t
);
9073 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9075 /* xtp meaning is normal by default */
9076 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9078 /* set empty favicon */
9079 xt_icon_from_name(t
, "text-html");
9081 /* and show it all */
9082 gtk_widget_show_all(b
);
9083 gtk_widget_show_all(t
->vbox
);
9085 /* compact tab bar */
9086 t
->tab_elems
.label
= gtk_label_new(title
);
9087 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9088 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9089 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9090 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9092 t
->tab_elems
.eventbox
= gtk_event_box_new();
9093 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9094 t
->tab_elems
.sep
= gtk_vseparator_new();
9096 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9097 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9098 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9099 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9100 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9101 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9103 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9105 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9107 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9110 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9112 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9114 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9117 id
= position
>= 0 ? position
:
9118 gtk_notebook_get_current_page(notebook
) + 1;
9119 if (id
> gtk_notebook_get_n_pages(notebook
))
9122 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9123 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9124 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9125 t
->tab_elems
.eventbox
, id
);
9130 #if GTK_CHECK_VERSION(2, 20, 0)
9131 /* turn spinner off if we are a new tab without uri */
9133 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9134 gtk_widget_hide(t
->spinner
);
9137 /* make notebook tabs reorderable */
9138 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9140 /* compact tabs clickable */
9141 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9142 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9144 g_object_connect(G_OBJECT(t
->cmd
),
9145 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9146 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9147 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9148 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9149 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9152 /* reuse wv_button_cb to hide oops */
9153 g_object_connect(G_OBJECT(t
->oops
),
9154 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9157 g_signal_connect(t
->buffers
,
9158 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9159 g_object_connect(G_OBJECT(t
->buffers
),
9160 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
9162 g_object_connect(G_OBJECT(t
->wv
),
9163 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9164 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9165 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9166 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9167 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9168 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9169 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9170 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9171 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9172 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9173 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9174 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9175 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9176 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9177 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9178 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9180 g_signal_connect(t
->wv
,
9181 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9183 * XXX this puts invalid url in uri_entry and that is undesirable
9186 g_signal_connect(t
->wv
,
9187 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
9189 g_signal_connect(t
->wv
,
9190 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9192 /* hijack the unused keys as if we were the browser */
9193 g_object_connect(G_OBJECT(t
->toolbar
),
9194 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9197 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9198 G_CALLBACK(tab_close_cb
), t
);
9201 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9202 /* restore the tab's history */
9203 if (u
&& u
->history
) {
9207 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9208 items
= g_list_next(items
);
9211 item
= g_list_nth_data(u
->history
, u
->back
);
9213 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9216 g_list_free(u
->history
);
9218 webkit_web_back_forward_list_clear(t
->bfl
);
9224 url_set_visibility();
9225 statusbar_set_visibility();
9228 set_current_tab(t
->tab_id
);
9229 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9234 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9238 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9243 recolor_compact_tabs();
9244 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9249 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9255 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9257 if (gtk_notebook_get_current_page(notebook
) == -1)
9260 TAILQ_FOREACH(t
, &tabs
, entry
) {
9261 if (t
->tab_id
== pn
) {
9262 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9265 uri
= get_title(t
, TRUE
);
9266 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9272 /* can't use focus_webview here */
9273 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9280 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9283 struct tab
*t
= NULL
, *tt
;
9287 TAILQ_FOREACH(tt
, &tabs
, entry
)
9288 if (tt
->tab_id
== pn
) {
9293 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9295 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9300 menuitem_response(struct tab
*t
)
9302 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9306 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9308 GtkWidget
*menu
, *menu_items
;
9309 GdkEventButton
*bevent
;
9313 if (event
->type
== GDK_BUTTON_PRESS
) {
9314 bevent
= (GdkEventButton
*) event
;
9315 menu
= gtk_menu_new();
9317 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9318 if ((uri
= get_uri(ti
)) == NULL
)
9319 /* XXX make sure there is something to print */
9320 /* XXX add gui pages in here to look purdy */
9322 menu_items
= gtk_menu_item_new_with_label(uri
);
9323 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9324 gtk_widget_show(menu_items
);
9326 g_signal_connect_swapped((menu_items
),
9327 "activate", G_CALLBACK(menuitem_response
),
9331 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9332 bevent
->button
, bevent
->time
);
9334 /* unref object so it'll free itself when popped down */
9335 #if !GTK_CHECK_VERSION(3, 0, 0)
9336 /* XXX does not need unref with gtk+3? */
9337 g_object_ref_sink(menu
);
9338 g_object_unref(menu
);
9341 return (TRUE
/* eat event */);
9344 return (FALSE
/* propagate */);
9348 icon_size_map(int icon_size
)
9350 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9351 icon_size
> GTK_ICON_SIZE_DIALOG
)
9352 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9358 create_button(char *name
, char *stockid
, int size
)
9360 GtkWidget
*button
, *image
;
9364 rcstring
= g_strdup_printf(
9365 "style \"%s-style\"\n"
9367 " GtkWidget::focus-padding = 0\n"
9368 " GtkWidget::focus-line-width = 0\n"
9372 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9373 gtk_rc_parse_string(rcstring
);
9375 button
= gtk_button_new();
9376 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9377 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9379 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9380 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9381 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9382 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9383 gtk_widget_set_name(button
, name
);
9384 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9390 button_set_stockid(GtkWidget
*button
, char *stockid
)
9394 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9395 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9396 gtk_button_set_image(GTK_BUTTON(button
), image
);
9400 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9403 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9406 if (xterm_workaround
== 0)
9410 * xterm doesn't play nice with clipboards because it clears the
9411 * primary when clicked. We rely on primary being set to properly
9412 * handle middle mouse button clicks (paste). So when someone clears
9413 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9414 * other application behavior (as in DON'T clear primary).
9417 p
= gtk_clipboard_wait_for_text(primary
);
9419 if (gdk_property_get(gdk_get_default_root_window(),
9421 gdk_atom_intern("STRING", FALSE
),
9423 1024 * 1024 /* picked out of my butt */,
9429 /* yes sir, we need to NUL the string */
9431 gtk_clipboard_set_text(primary
, p
, -1);
9445 char file
[PATH_MAX
];
9448 vbox
= gtk_vbox_new(FALSE
, 0);
9449 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9450 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9451 #if !GTK_CHECK_VERSION(3, 0, 0)
9452 /* XXX seems to be needed with gtk+2 */
9453 gtk_notebook_set_tab_hborder(notebook
, 0);
9454 gtk_notebook_set_tab_vborder(notebook
, 0);
9456 gtk_notebook_set_scrollable(notebook
, TRUE
);
9457 gtk_notebook_set_show_border(notebook
, FALSE
);
9458 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9460 abtn
= gtk_button_new();
9461 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9462 gtk_widget_set_size_request(arrow
, -1, -1);
9463 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9464 gtk_widget_set_size_request(abtn
, -1, 20);
9466 #if GTK_CHECK_VERSION(2, 20, 0)
9467 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9469 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9471 /* compact tab bar */
9472 tab_bar
= gtk_hbox_new(TRUE
, 0);
9474 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9475 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9476 gtk_widget_set_size_request(vbox
, -1, -1);
9478 g_object_connect(G_OBJECT(notebook
),
9479 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9481 g_object_connect(G_OBJECT(notebook
),
9482 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9483 NULL
, (char *)NULL
);
9484 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9485 G_CALLBACK(arrow_cb
), NULL
);
9487 main_window
= create_window();
9488 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9491 for (i
= 0; i
< LENGTH(icons
); i
++) {
9492 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9493 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9494 l
= g_list_append(l
, pb
);
9496 gtk_window_set_default_icon_list(l
);
9498 /* clipboard work around */
9499 if (xterm_workaround
)
9501 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9502 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9504 gtk_widget_show_all(abtn
);
9505 gtk_widget_show_all(main_window
);
9506 notebook_tab_set_visibility();
9510 set_hook(void **hook
, char *name
)
9513 errx(1, "set_hook");
9515 if (*hook
== NULL
) {
9516 *hook
= dlsym(RTLD_NEXT
, name
);
9518 errx(1, "can't hook %s", name
);
9522 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9524 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9526 g_return_val_if_fail(cookie1
, FALSE
);
9527 g_return_val_if_fail(cookie2
, FALSE
);
9529 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9530 !strcmp (cookie1
->value
, cookie2
->value
) &&
9531 !strcmp (cookie1
->path
, cookie2
->path
) &&
9532 !strcmp (cookie1
->domain
, cookie2
->domain
));
9536 transfer_cookies(void)
9539 SoupCookie
*sc
, *pc
;
9541 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9543 for (;cf
; cf
= cf
->next
) {
9545 sc
= soup_cookie_copy(pc
);
9546 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9549 soup_cookies_free(cf
);
9553 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9558 print_cookie("soup_cookie_jar_delete_cookie", c
);
9560 if (cookies_enabled
== 0)
9563 if (jar
== NULL
|| c
== NULL
)
9566 /* find and remove from persistent jar */
9567 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9569 for (;cf
; cf
= cf
->next
) {
9571 if (soup_cookie_equal(ci
, c
)) {
9572 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9577 soup_cookies_free(cf
);
9579 /* delete from session jar */
9580 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9584 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9586 struct domain
*d
= NULL
;
9590 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9591 jar
, p_cookiejar
, s_cookiejar
);
9593 if (cookies_enabled
== 0)
9596 /* see if we are up and running */
9597 if (p_cookiejar
== NULL
) {
9598 _soup_cookie_jar_add_cookie(jar
, cookie
);
9601 /* disallow p_cookiejar adds, shouldn't happen */
9602 if (jar
== p_cookiejar
)
9606 if (jar
== NULL
|| cookie
== NULL
)
9609 if (enable_cookie_whitelist
&&
9610 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9612 DNPRINTF(XT_D_COOKIE
,
9613 "soup_cookie_jar_add_cookie: reject %s\n",
9615 if (save_rejected_cookies
) {
9616 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9617 show_oops(NULL
, "can't open reject cookie file");
9620 fseek(r_cookie_f
, 0, SEEK_END
);
9621 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9622 cookie
->http_only
? "#HttpOnly_" : "",
9624 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9626 cookie
->secure
? "TRUE" : "FALSE",
9628 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9635 if (!allow_volatile_cookies
)
9639 if (cookie
->expires
== NULL
&& session_timeout
) {
9640 soup_cookie_set_expires(cookie
,
9641 soup_date_new_from_now(session_timeout
));
9642 print_cookie("modified add cookie", cookie
);
9645 /* see if we are white listed for persistence */
9646 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9647 /* add to persistent jar */
9648 c
= soup_cookie_copy(cookie
);
9649 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9650 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9653 /* add to session jar */
9654 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9655 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9661 char file
[PATH_MAX
];
9663 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9664 "soup_cookie_jar_add_cookie");
9665 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9666 "soup_cookie_jar_delete_cookie");
9668 if (cookies_enabled
== 0)
9672 * the following code is intricate due to overriding several libsoup
9674 * do not alter order of these operations.
9677 /* rejected cookies */
9678 if (save_rejected_cookies
)
9679 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9682 /* persistent cookies */
9683 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9684 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9686 /* session cookies */
9687 s_cookiejar
= soup_cookie_jar_new();
9688 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9689 cookie_policy
, (void *)NULL
);
9692 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9696 setup_proxy(char *uri
)
9699 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9700 soup_uri_free(proxy_uri
);
9704 if (http_proxy
!= uri
) {
9711 http_proxy
= g_strdup(uri
);
9712 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9713 proxy_uri
= soup_uri_new(http_proxy
);
9714 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
9715 g_object_set(session
, "proxy-uri", proxy_uri
,
9721 set_http_proxy(char *proxy
)
9728 /* see if we need to clear it instead */
9729 if (strlen(proxy
) == 0) {
9734 uri
= soup_uri_new(proxy
);
9735 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9746 send_cmd_to_socket(char *cmd
)
9749 struct sockaddr_un sa
;
9751 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9752 warnx("%s: socket", __func__
);
9756 sa
.sun_family
= AF_UNIX
;
9757 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9758 work_dir
, XT_SOCKET_FILE
);
9761 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9762 warnx("%s: connect", __func__
);
9766 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9767 warnx("%s: send", __func__
);
9778 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9781 char str
[XT_MAX_URL_LENGTH
];
9782 socklen_t t
= sizeof(struct sockaddr_un
);
9783 struct sockaddr_un sa
;
9788 gint fd
= g_io_channel_unix_get_fd(source
);
9790 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9795 if (getpeereid(s
, &uid
, &gid
) == -1) {
9799 if (uid
!= getuid() || gid
!= getgid()) {
9800 warnx("unauthorized user");
9806 warnx("not a valid user");
9810 n
= recv(s
, str
, sizeof(str
), 0);
9814 tt
= TAILQ_LAST(&tabs
, tab_list
);
9815 cmd_execute(tt
, str
);
9823 struct sockaddr_un sa
;
9825 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9826 warn("is_running: socket");
9830 sa
.sun_family
= AF_UNIX
;
9831 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9832 work_dir
, XT_SOCKET_FILE
);
9835 /* connect to see if there is a listener */
9836 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9837 rv
= 0; /* not running */
9839 rv
= 1; /* already running */
9850 struct sockaddr_un sa
;
9852 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9853 warn("build_socket: socket");
9857 sa
.sun_family
= AF_UNIX
;
9858 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9859 work_dir
, XT_SOCKET_FILE
);
9862 /* connect to see if there is a listener */
9863 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9864 /* no listener so we will */
9865 unlink(sa
.sun_path
);
9867 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9868 warn("build_socket: bind");
9872 if (listen(s
, 1) == -1) {
9873 warn("build_socket: listen");
9886 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9887 GtkTreeIter
*iter
, struct tab
*t
)
9891 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9899 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9900 GtkTreeIter
*iter
, struct tab
*t
)
9904 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9905 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9906 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9913 completion_add_uri(const gchar
*uri
)
9917 /* add uri to list_store */
9918 gtk_list_store_append(completion_model
, &iter
);
9919 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9923 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9924 GtkTreeIter
*iter
, gpointer user_data
)
9927 gboolean match
= FALSE
;
9929 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9935 match
= match_uri(value
, key
);
9942 completion_add(struct tab
*t
)
9944 /* enable completion for tab */
9945 t
->completion
= gtk_entry_completion_new();
9946 gtk_entry_completion_set_text_column(t
->completion
, 0);
9947 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9948 gtk_entry_completion_set_model(t
->completion
,
9949 GTK_TREE_MODEL(completion_model
));
9950 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9952 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9953 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9954 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9955 G_CALLBACK(completion_select_cb
), t
);
9956 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9957 G_CALLBACK(completion_hover_cb
), t
);
9965 if (stat(dir
, &sb
)) {
9966 if (mkdir(dir
, S_IRWXU
) == -1)
9967 err(1, "mkdir %s", dir
);
9969 err(1, "stat %s", dir
);
9971 if (S_ISDIR(sb
.st_mode
) == 0)
9972 errx(1, "%s not a dir", dir
);
9973 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9974 warnx("fixing invalid permissions on %s", dir
);
9975 if (chmod(dir
, S_IRWXU
) == -1)
9976 err(1, "chmod %s", dir
);
9984 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9990 main(int argc
, char *argv
[])
9993 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9994 char conf
[PATH_MAX
] = { '\0' };
9995 char file
[PATH_MAX
];
9996 char *env_proxy
= NULL
;
10000 struct sigaction sact
;
10001 GIOChannel
*channel
;
10007 if (!g_thread_supported()) {
10008 g_thread_init(NULL
);
10009 gdk_threads_init();
10011 gtk_init(&argc
, &argv
);
10013 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10017 RB_INIT(&downloads
);
10021 TAILQ_INIT(&aliases
);
10022 TAILQ_INIT(&undos
);
10028 /* fiddle with ulimits */
10029 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10032 /* just use them all */
10033 rlp
.rlim_cur
= rlp
.rlim_max
;
10034 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10036 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10038 else if (rlp
.rlim_cur
<= 256)
10039 startpage_add("%s requires at least 256 file "
10040 "descriptors, currently it has up to %d available",
10041 __progname
, rlp
.rlim_cur
);
10044 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10053 errx(0 , "Version: %s", version
);
10056 strlcpy(conf
, optarg
, sizeof(conf
));
10059 strlcpy(named_session
, optarg
, sizeof(named_session
));
10078 init_keybindings();
10080 gnutls_global_init();
10082 /* generate session keys for xtp pages */
10083 generate_xtp_session_key(&dl_session_key
);
10084 generate_xtp_session_key(&hl_session_key
);
10085 generate_xtp_session_key(&cl_session_key
);
10086 generate_xtp_session_key(&fl_session_key
);
10089 bzero(&sact
, sizeof(sact
));
10090 sigemptyset(&sact
.sa_mask
);
10091 sact
.sa_handler
= sigchild
;
10092 sact
.sa_flags
= SA_NOCLDSTOP
;
10093 sigaction(SIGCHLD
, &sact
, NULL
);
10095 /* set download dir */
10096 pwd
= getpwuid(getuid());
10098 errx(1, "invalid user %d", getuid());
10099 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10101 /* compile buffer command regexes */
10104 /* set default string settings */
10105 home
= g_strdup("https://www.cyphertite.com");
10106 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10107 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10108 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10109 cmd_font_name
= g_strdup("monospace normal 9");
10110 oops_font_name
= g_strdup("monospace normal 9");
10111 statusbar_font_name
= g_strdup("monospace normal 9");
10112 tabbar_font_name
= g_strdup("monospace normal 9");
10113 statusbar_elems
= g_strdup("BP");
10115 /* read config file */
10116 if (strlen(conf
) == 0)
10117 snprintf(conf
, sizeof conf
, "%s/.%s",
10118 pwd
->pw_dir
, XT_CONF_FILE
);
10119 config_parse(conf
, 0);
10122 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10123 oops_font
= pango_font_description_from_string(oops_font_name
);
10124 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10125 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10127 /* working directory */
10128 if (strlen(work_dir
) == 0)
10129 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10130 pwd
->pw_dir
, XT_DIR
);
10133 /* icon cache dir */
10134 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10135 xxx_dir(cache_dir
);
10138 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10139 xxx_dir(certs_dir
);
10142 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10143 work_dir
, XT_SESSIONS_DIR
);
10144 xxx_dir(sessions_dir
);
10146 /* runtime settings that can override config file */
10147 if (runtime_settings
[0] != '\0')
10148 config_parse(runtime_settings
, 1);
10151 if (!strcmp(download_dir
, pwd
->pw_dir
))
10152 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10153 xxx_dir(download_dir
);
10155 /* favorites file */
10156 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10157 if (stat(file
, &sb
)) {
10158 warnx("favorites file doesn't exist, creating it");
10159 if ((f
= fopen(file
, "w")) == NULL
)
10160 err(1, "favorites");
10164 /* quickmarks file */
10165 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10166 if (stat(file
, &sb
)) {
10167 warnx("quickmarks file doesn't exist, creating it");
10168 if ((f
= fopen(file
, "w")) == NULL
)
10169 err(1, "quickmarks");
10173 /* search history */
10174 if (history_autosave
) {
10175 snprintf(search_file
, sizeof search_file
, "%s/%s",
10176 work_dir
, XT_SEARCH_FILE
);
10177 if (stat(search_file
, &sb
)) {
10178 warnx("search history file doesn't exist, creating it");
10179 if ((f
= fopen(search_file
, "w")) == NULL
)
10180 err(1, "search_history");
10183 history_read(&shl
, search_file
, &search_history_count
);
10186 /* command history */
10187 if (history_autosave
) {
10188 snprintf(command_file
, sizeof command_file
, "%s/%s",
10189 work_dir
, XT_COMMAND_FILE
);
10190 if (stat(command_file
, &sb
)) {
10191 warnx("command history file doesn't exist, creating it");
10192 if ((f
= fopen(command_file
, "w")) == NULL
)
10193 err(1, "command_history");
10196 history_read(&chl
, command_file
, &cmd_history_count
);
10200 session
= webkit_get_default_session();
10205 if (stat(ssl_ca_file
, &sb
)) {
10206 warnx("no CA file: %s", ssl_ca_file
);
10207 g_free(ssl_ca_file
);
10208 ssl_ca_file
= NULL
;
10210 g_object_set(session
,
10211 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10212 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10216 /* guess_search regex */
10217 if (url_regex
== NULL
)
10218 url_regex
= g_strdup(XT_URL_REGEX
);
10220 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10221 startpage_add("invalid url regex %s", url_regex
);
10224 env_proxy
= getenv("http_proxy");
10226 setup_proxy(env_proxy
);
10228 setup_proxy(http_proxy
);
10231 send_cmd_to_socket(argv
[0]);
10235 /* set some connection parameters */
10236 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10237 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10240 /* see if there is already an xxxterm running */
10241 if (single_instance
&& is_running()) {
10243 warnx("already running");
10248 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10249 send_cmd_to_socket(cmd
);
10259 /* uri completion */
10260 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10263 buffers_store
= gtk_list_store_new
10264 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10270 notebook_tab_set_visibility();
10272 if (save_global_history
)
10273 restore_global_history();
10275 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10276 restore_saved_tabs();
10278 a
.s
= named_session
;
10279 a
.i
= XT_SES_DONOTHING
;
10280 open_tabs(NULL
, &a
);
10283 /* see if we have an exception */
10284 if (!TAILQ_EMPTY(&spl
)) {
10285 create_new_tab("about:startpage", NULL
, focus
, -1);
10290 create_new_tab(argv
[0], NULL
, focus
, -1);
10297 if (TAILQ_EMPTY(&tabs
))
10298 create_new_tab(home
, NULL
, 1, -1);
10301 if ((s
= build_socket()) != -1) {
10302 channel
= g_io_channel_unix_new(s
);
10303 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10308 gnutls_global_deinit();