3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
8 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * create privacy browsing
26 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #include <bsd/stdlib.h>
49 # if !defined(sane_libbsd_headers)
50 void arc4random_buf(void *, size_t);
52 #elif defined(__FreeBSD__)
54 #include "freebsd/util.h"
60 #include <sys/queue.h>
61 #include <sys/resource.h>
62 #include <sys/socket.h>
68 #include <gdk/gdkkeysyms.h>
70 #if GTK_CHECK_VERSION(3,0,0)
71 /* we still use GDK_* instead of GDK_KEY_* */
72 #include <gdk/gdkkeysyms-compat.h>
75 #include <webkit/webkit.h>
76 #include <libsoup/soup.h>
77 #include <gnutls/gnutls.h>
78 #include <JavaScriptCore/JavaScript.h>
79 #include <gnutls/x509.h>
82 #include "javascript.h"
85 javascript.h borrowed from vimprobable2 under the following license:
87 Copyright (c) 2009 Leon Winter
88 Copyright (c) 2009 Hannes Schueller
89 Copyright (c) 2009 Matto Fransen
91 Permission is hereby granted, free of charge, to any person obtaining a copy
92 of this software and associated documentation files (the "Software"), to deal
93 in the Software without restriction, including without limitation the rights
94 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
95 copies of the Software, and to permit persons to whom the Software is
96 furnished to do so, subject to the following conditions:
98 The above copyright notice and this permission notice shall be included in
99 all copies or substantial portions of the Software.
101 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
102 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
103 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
104 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
105 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
106 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
110 static char *version
= XXXTERM_VERSION
;
112 /* hooked functions */
113 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
114 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
119 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
120 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
121 #define XT_D_MOVE 0x0001
122 #define XT_D_KEY 0x0002
123 #define XT_D_TAB 0x0004
124 #define XT_D_URL 0x0008
125 #define XT_D_CMD 0x0010
126 #define XT_D_NAV 0x0020
127 #define XT_D_DOWNLOAD 0x0040
128 #define XT_D_CONFIG 0x0080
129 #define XT_D_JS 0x0100
130 #define XT_D_FAVORITE 0x0200
131 #define XT_D_PRINTING 0x0400
132 #define XT_D_COOKIE 0x0800
133 #define XT_D_KEYBINDING 0x1000
134 #define XT_D_CLIP 0x2000
135 #define XT_D_BUFFERCMD 0x4000
136 u_int32_t swm_debug
= 0
154 #define DPRINTF(x...)
155 #define DNPRINTF(n,x...)
158 #define LENGTH(x) (sizeof x / sizeof x[0])
159 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
160 ~(GDK_BUTTON1_MASK) & \
161 ~(GDK_BUTTON2_MASK) & \
162 ~(GDK_BUTTON3_MASK) & \
163 ~(GDK_BUTTON4_MASK) & \
166 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
177 TAILQ_ENTRY(tab
) entry
;
179 GtkWidget
*tab_content
;
188 GtkWidget
*uri_entry
;
189 GtkWidget
*search_entry
;
191 GtkWidget
*browser_win
;
192 GtkWidget
*statusbar_box
;
194 GtkWidget
*statusbar
;
195 GtkWidget
*buffercmd
;
206 GtkWidget
*js_toggle
;
207 GtkEntryCompletion
*completion
;
211 WebKitWebHistoryItem
*item
;
212 WebKitWebBackForwardList
*bfl
;
215 WebKitNetworkRequest
*icon_request
;
216 WebKitDownload
*icon_download
;
217 gchar
*icon_dest_uri
;
219 /* adjustments for browser */
222 GtkAdjustment
*adjust_h
;
223 GtkAdjustment
*adjust_v
;
229 int xtp_meaning
; /* identifies dls/favorites */
231 int popup
; /* 1 if cmd_entry has popup visible */
233 /* https thread stuff */
239 #define XT_HINT_NONE (0)
240 #define XT_HINT_NUMERICAL (1)
241 #define XT_HINT_ALPHANUM (2)
245 /* custom stylesheet */
255 WebKitWebSettings
*settings
;
259 double mark
[XT_NOMARKS
];
261 TAILQ_HEAD(tab_list
, tab
);
264 RB_ENTRY(history
) entry
;
268 RB_HEAD(history_list
, history
);
271 TAILQ_ENTRY(session
) entry
;
274 TAILQ_HEAD(session_list
, session
);
277 RB_ENTRY(download
) entry
;
279 WebKitDownload
*download
;
282 RB_HEAD(download_list
, download
);
285 RB_ENTRY(domain
) entry
;
287 int handy
; /* app use */
289 RB_HEAD(domain_list
, domain
);
292 TAILQ_ENTRY(undo
) entry
;
295 int back
; /* Keeps track of how many back
296 * history items there are. */
298 TAILQ_HEAD(undo_tailq
, undo
);
302 TAILQ_ENTRY(sp
) entry
;
304 TAILQ_HEAD(sp_list
, sp
);
306 struct command_entry
{
308 TAILQ_ENTRY(command_entry
) entry
;
310 TAILQ_HEAD(command_list
, command_entry
);
312 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
313 int next_download_id
= 1;
322 #define XT_NAME ("XXXTerm")
323 #define XT_DIR (".xxxterm")
324 #define XT_CACHE_DIR ("cache")
325 #define XT_CERT_DIR ("certs/")
326 #define XT_SESSIONS_DIR ("sessions/")
327 #define XT_CONF_FILE ("xxxterm.conf")
328 #define XT_FAVS_FILE ("favorites")
329 #define XT_QMARKS_FILE ("quickmarks")
330 #define XT_SAVED_TABS_FILE ("main_session")
331 #define XT_RESTART_TABS_FILE ("restart_tabs")
332 #define XT_SOCKET_FILE ("socket")
333 #define XT_HISTORY_FILE ("history")
334 #define XT_REJECT_FILE ("rejected.txt")
335 #define XT_COOKIE_FILE ("cookies.txt")
336 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
337 #define XT_SEARCH_FILE ("search_history")
338 #define XT_COMMAND_FILE ("command_history")
339 #define XT_CB_HANDLED (TRUE)
340 #define XT_CB_PASSTHROUGH (FALSE)
341 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
342 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
343 #define XT_DLMAN_REFRESH "10"
344 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
345 "td{overflow: hidden;" \
346 " padding: 2px 2px 2px 2px;" \
347 " border: 1px solid black;" \
348 " vertical-align:top;" \
349 " word-wrap: break-word}\n" \
350 "tr:hover{background: #ffff99}\n" \
351 "th{background-color: #cccccc;" \
352 " border: 1px solid black}\n" \
353 "table{width: 100%%;" \
354 " border: 1px black solid;" \
355 " border-collapse:collapse}\n" \
357 "border: 1px solid black;" \
360 ".progress-inner{float: left;" \
362 " background: green}\n" \
363 ".dlstatus{font-size: small;" \
364 " text-align: center}\n" \
366 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
367 #define XT_MAX_UNDO_CLOSE_TAB (32)
368 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
369 #define XT_PRINT_EXTRA_MARGIN 10
370 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
371 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
374 #define XT_COLOR_RED "#cc0000"
375 #define XT_COLOR_YELLOW "#ffff66"
376 #define XT_COLOR_BLUE "lightblue"
377 #define XT_COLOR_GREEN "#99ff66"
378 #define XT_COLOR_WHITE "white"
379 #define XT_COLOR_BLACK "black"
381 #define XT_COLOR_CT_BACKGROUND "#000000"
382 #define XT_COLOR_CT_INACTIVE "#dddddd"
383 #define XT_COLOR_CT_ACTIVE "#bbbb00"
384 #define XT_COLOR_CT_SEPARATOR "#555555"
386 #define XT_COLOR_SB_SEPARATOR "#555555"
388 #define XT_PROTO_DELIM "://"
391 * xxxterm "protocol" (xtp)
392 * We use this for managing stuff like downloads and favorites. They
393 * make magical HTML pages in memory which have xxxt:// links in order
394 * to communicate with xxxterm's internals. These links take the format:
395 * xxxt://class/session_key/action/arg
397 * Don't begin xtp class/actions as 0. atoi returns that on error.
399 * Typically we have not put addition of items in this framework, as
400 * adding items is either done via an ex-command or via a keybinding instead.
403 #define XT_XTP_STR "xxxt://"
405 /* XTP classes (xxxt://<class>) */
406 #define XT_XTP_INVALID 0 /* invalid */
407 #define XT_XTP_DL 1 /* downloads */
408 #define XT_XTP_HL 2 /* history */
409 #define XT_XTP_CL 3 /* cookies */
410 #define XT_XTP_FL 4 /* favorites */
412 /* XTP download actions */
413 #define XT_XTP_DL_LIST 1
414 #define XT_XTP_DL_CANCEL 2
415 #define XT_XTP_DL_REMOVE 3
417 /* XTP history actions */
418 #define XT_XTP_HL_LIST 1
419 #define XT_XTP_HL_REMOVE 2
421 /* XTP cookie actions */
422 #define XT_XTP_CL_LIST 1
423 #define XT_XTP_CL_REMOVE 2
425 /* XTP cookie actions */
426 #define XT_XTP_FL_LIST 1
427 #define XT_XTP_FL_REMOVE 2
430 #define XT_MOVE_INVALID (0)
431 #define XT_MOVE_DOWN (1)
432 #define XT_MOVE_UP (2)
433 #define XT_MOVE_BOTTOM (3)
434 #define XT_MOVE_TOP (4)
435 #define XT_MOVE_PAGEDOWN (5)
436 #define XT_MOVE_PAGEUP (6)
437 #define XT_MOVE_HALFDOWN (7)
438 #define XT_MOVE_HALFUP (8)
439 #define XT_MOVE_LEFT (9)
440 #define XT_MOVE_FARLEFT (10)
441 #define XT_MOVE_RIGHT (11)
442 #define XT_MOVE_FARRIGHT (12)
443 #define XT_MOVE_PERCENT (13)
445 #define XT_QMARK_SET (0)
446 #define XT_QMARK_OPEN (1)
447 #define XT_QMARK_TAB (2)
449 #define XT_MARK_SET (0)
450 #define XT_MARK_GOTO (1)
452 #define XT_TAB_LAST (-4)
453 #define XT_TAB_FIRST (-3)
454 #define XT_TAB_PREV (-2)
455 #define XT_TAB_NEXT (-1)
456 #define XT_TAB_INVALID (0)
457 #define XT_TAB_NEW (1)
458 #define XT_TAB_DELETE (2)
459 #define XT_TAB_DELQUIT (3)
460 #define XT_TAB_OPEN (4)
461 #define XT_TAB_UNDO_CLOSE (5)
462 #define XT_TAB_SHOW (6)
463 #define XT_TAB_HIDE (7)
464 #define XT_TAB_NEXTSTYLE (8)
466 #define XT_NAV_INVALID (0)
467 #define XT_NAV_BACK (1)
468 #define XT_NAV_FORWARD (2)
469 #define XT_NAV_RELOAD (3)
471 #define XT_FOCUS_INVALID (0)
472 #define XT_FOCUS_URI (1)
473 #define XT_FOCUS_SEARCH (2)
475 #define XT_SEARCH_INVALID (0)
476 #define XT_SEARCH_NEXT (1)
477 #define XT_SEARCH_PREV (2)
479 #define XT_PASTE_CURRENT_TAB (0)
480 #define XT_PASTE_NEW_TAB (1)
482 #define XT_ZOOM_IN (-1)
483 #define XT_ZOOM_OUT (-2)
484 #define XT_ZOOM_NORMAL (100)
486 #define XT_URL_SHOW (1)
487 #define XT_URL_HIDE (2)
489 #define XT_WL_TOGGLE (1<<0)
490 #define XT_WL_ENABLE (1<<1)
491 #define XT_WL_DISABLE (1<<2)
492 #define XT_WL_FQDN (1<<3) /* default */
493 #define XT_WL_TOPLEVEL (1<<4)
494 #define XT_WL_PERSISTENT (1<<5)
495 #define XT_WL_SESSION (1<<6)
496 #define XT_WL_RELOAD (1<<7)
498 #define XT_SHOW (1<<7)
499 #define XT_DELETE (1<<8)
500 #define XT_SAVE (1<<9)
501 #define XT_OPEN (1<<10)
503 #define XT_CMD_OPEN (0)
504 #define XT_CMD_OPEN_CURRENT (1)
505 #define XT_CMD_TABNEW (2)
506 #define XT_CMD_TABNEW_CURRENT (3)
508 #define XT_STATUS_NOTHING (0)
509 #define XT_STATUS_LINK (1)
510 #define XT_STATUS_URI (2)
511 #define XT_STATUS_LOADING (3)
513 #define XT_SES_DONOTHING (0)
514 #define XT_SES_CLOSETABS (1)
516 #define XT_BM_NORMAL (0)
517 #define XT_BM_WHITELIST (1)
518 #define XT_BM_KIOSK (2)
520 #define XT_PREFIX (1<<0)
521 #define XT_USERARG (1<<1)
522 #define XT_URLARG (1<<2)
523 #define XT_INTARG (1<<3)
524 #define XT_SESSARG (1<<4)
525 #define XT_SETARG (1<<5)
527 #define XT_TABS_NORMAL 0
528 #define XT_TABS_COMPACT 1
530 #define XT_BUFCMD_SZ (8)
538 TAILQ_ENTRY(mime_type
) entry
;
540 TAILQ_HEAD(mime_type_list
, mime_type
);
546 TAILQ_ENTRY(alias
) entry
;
548 TAILQ_HEAD(alias_list
, alias
);
550 /* settings that require restart */
551 int tabless
= 0; /* allow only 1 tab */
552 int enable_socket
= 0;
553 int single_instance
= 0; /* only allow one xxxterm to run */
554 int fancy_bar
= 1; /* fancy toolbar */
555 int browser_mode
= XT_BM_NORMAL
;
556 int enable_localstorage
= 1;
557 char *statusbar_elems
= NULL
;
559 /* runtime settings */
560 int show_tabs
= 1; /* show tabs on notebook */
561 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
562 int show_url
= 1; /* show url toolbar on notebook */
563 int show_statusbar
= 0; /* vimperator style status bar */
564 int ctrl_click_focus
= 0; /* ctrl click gets focus */
565 int cookies_enabled
= 1; /* enable cookies */
566 int read_only_cookies
= 0; /* enable to not write cookies */
567 int enable_scripts
= 1;
568 int enable_plugins
= 0;
569 gfloat default_zoom_level
= 1.0;
570 char default_script
[PATH_MAX
];
571 int window_height
= 768;
572 int window_width
= 1024;
573 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
574 int refresh_interval
= 10; /* download refresh interval */
575 int enable_cookie_whitelist
= 0;
576 int enable_js_whitelist
= 0;
577 int session_timeout
= 3600; /* cookie session timeout */
578 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
579 char *ssl_ca_file
= NULL
;
580 char *resource_dir
= NULL
;
581 gboolean ssl_strict_certs
= FALSE
;
582 int append_next
= 1; /* append tab after current tab */
584 char *search_string
= NULL
;
585 char *http_proxy
= NULL
;
586 char download_dir
[PATH_MAX
];
587 char runtime_settings
[PATH_MAX
]; /* override of settings */
588 int allow_volatile_cookies
= 0;
589 int save_global_history
= 0; /* save global history to disk */
590 char *user_agent
= NULL
;
591 int save_rejected_cookies
= 0;
592 int session_autosave
= 0;
593 int guess_search
= 0;
594 int dns_prefetch
= FALSE
;
595 gint max_connections
= 25;
596 gint max_host_connections
= 5;
597 gint enable_spell_checking
= 0;
598 char *spell_check_languages
= NULL
;
599 int xterm_workaround
= 0;
600 char *url_regex
= NULL
;
601 int history_autosave
= 0;
602 char search_file
[PATH_MAX
];
603 char command_file
[PATH_MAX
];
604 char *encoding
= NULL
;
606 char *cmd_font_name
= NULL
;
607 char *oops_font_name
= NULL
;
608 char *statusbar_font_name
= NULL
;
609 char *tabbar_font_name
= NULL
;
610 PangoFontDescription
*cmd_font
;
611 PangoFontDescription
*oops_font
;
612 PangoFontDescription
*statusbar_font
;
613 PangoFontDescription
*tabbar_font
;
614 char *qmarks
[XT_NOMARKS
];
616 int btn_down
; /* M1 down in any wv */
617 regex_t url_re
; /* guess_search regex */
621 int set_browser_mode(struct settings
*, char *);
622 int set_cookie_policy(struct settings
*, char *);
623 int set_download_dir(struct settings
*, char *);
624 int set_default_script(struct settings
*, char *);
625 int set_runtime_dir(struct settings
*, char *);
626 int set_tab_style(struct settings
*, char *);
627 int set_work_dir(struct settings
*, char *);
628 int add_alias(struct settings
*, char *);
629 int add_mime_type(struct settings
*, char *);
630 int add_cookie_wl(struct settings
*, char *);
631 int add_js_wl(struct settings
*, char *);
632 int add_kb(struct settings
*, char *);
633 void button_set_stockid(GtkWidget
*, char *);
634 GtkWidget
* create_button(char *, char *, int);
636 char *get_browser_mode(struct settings
*);
637 char *get_cookie_policy(struct settings
*);
638 char *get_download_dir(struct settings
*);
639 char *get_default_script(struct settings
*);
640 char *get_runtime_dir(struct settings
*);
641 char *get_tab_style(struct settings
*);
642 char *get_work_dir(struct settings
*);
643 void startpage_add(const char *, ...);
645 void walk_alias(struct settings
*, void (*)(struct settings
*,
646 char *, void *), void *);
647 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*,
648 char *, void *), void *);
649 void walk_js_wl(struct settings
*, void (*)(struct settings
*,
650 char *, void *), void *);
651 void walk_kb(struct settings
*, void (*)(struct settings
*, char *,
653 void walk_mime_type(struct settings
*, void (*)(struct settings
*,
654 char *, void *), void *);
656 void recalc_tabs(void);
657 void recolor_compact_tabs(void);
658 void set_current_tab(int page_num
);
659 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
660 void marks_clear(struct tab
*t
);
662 int set_http_proxy(char *);
665 int (*set
)(struct settings
*, char *);
666 char *(*get
)(struct settings
*);
667 void (*walk
)(struct settings
*,
668 void (*cb
)(struct settings
*, char *, void *),
672 struct special s_browser_mode
= {
678 struct special s_cookie
= {
684 struct special s_alias
= {
690 struct special s_mime
= {
696 struct special s_js
= {
702 struct special s_kb
= {
708 struct special s_cookie_wl
= {
714 struct special s_default_script
= {
720 struct special s_download_dir
= {
726 struct special s_work_dir
= {
732 struct special s_tab_style
= {
741 #define XT_S_INVALID (0)
744 #define XT_S_FLOAT (3)
746 #define XT_SF_RESTART (1<<0)
747 #define XT_SF_RUNTIME (1<<1)
752 int (*activate
)(char *);
754 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
755 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
756 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
757 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
758 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
759 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
760 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
761 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
762 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
763 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
764 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
765 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
766 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
767 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
768 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
769 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
770 { "encoding", XT_S_STR
, 0, NULL
, &encoding
, NULL
},
771 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
772 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
773 { "history_autosave", XT_S_INT
, 0, &history_autosave
, NULL
, NULL
},
774 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
775 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
, NULL
, set_http_proxy
},
776 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
777 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
778 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
779 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
780 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
781 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
782 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
783 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
784 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
785 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
786 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
787 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
788 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
789 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
790 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
791 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
792 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
793 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
794 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
795 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
796 { "url_regex", XT_S_STR
, 0, NULL
, &url_regex
, NULL
},
797 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
798 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
799 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
800 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
801 { "xterm_workaround", XT_S_INT
, 0, &xterm_workaround
, NULL
, NULL
},
804 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
805 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
806 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
807 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
809 /* runtime settings */
810 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
811 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
812 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
813 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
814 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
817 int about(struct tab
*, struct karg
*);
818 int blank(struct tab
*, struct karg
*);
819 int ca_cmd(struct tab
*, struct karg
*);
820 int cookie_show_wl(struct tab
*, struct karg
*);
821 int js_show_wl(struct tab
*, struct karg
*);
822 int help(struct tab
*, struct karg
*);
823 int set(struct tab
*, struct karg
*);
824 int stats(struct tab
*, struct karg
*);
825 int marco(struct tab
*, struct karg
*);
826 int startpage(struct tab
*, struct karg
*);
827 const char * marco_message(int *);
828 int xtp_page_cl(struct tab
*, struct karg
*);
829 int xtp_page_dl(struct tab
*, struct karg
*);
830 int xtp_page_fl(struct tab
*, struct karg
*);
831 int xtp_page_hl(struct tab
*, struct karg
*);
832 void xt_icon_from_file(struct tab
*, char *);
833 const gchar
*get_uri(struct tab
*);
834 const gchar
*get_title(struct tab
*, bool);
836 #define XT_URI_ABOUT ("about:")
837 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
838 #define XT_URI_ABOUT_ABOUT ("about")
839 #define XT_URI_ABOUT_BLANK ("blank")
840 #define XT_URI_ABOUT_CERTS ("certs")
841 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
842 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
843 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
844 #define XT_URI_ABOUT_FAVORITES ("favorites")
845 #define XT_URI_ABOUT_HELP ("help")
846 #define XT_URI_ABOUT_HISTORY ("history")
847 #define XT_URI_ABOUT_JSWL ("jswl")
848 #define XT_URI_ABOUT_SET ("set")
849 #define XT_URI_ABOUT_STATS ("stats")
850 #define XT_URI_ABOUT_MARCO ("marco")
851 #define XT_URI_ABOUT_STARTPAGE ("startpage")
855 int (*func
)(struct tab
*, struct karg
*);
857 { XT_URI_ABOUT_ABOUT
, about
},
858 { XT_URI_ABOUT_BLANK
, blank
},
859 { XT_URI_ABOUT_CERTS
, ca_cmd
},
860 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
861 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
862 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
863 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
864 { XT_URI_ABOUT_HELP
, help
},
865 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
866 { XT_URI_ABOUT_JSWL
, js_show_wl
},
867 { XT_URI_ABOUT_SET
, set
},
868 { XT_URI_ABOUT_STATS
, stats
},
869 { XT_URI_ABOUT_MARCO
, marco
},
870 { XT_URI_ABOUT_STARTPAGE
, startpage
},
873 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
874 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
875 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
876 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
877 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
878 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
879 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
882 extern char *__progname
;
885 GtkWidget
*main_window
;
886 GtkNotebook
*notebook
;
888 GtkWidget
*arrow
, *abtn
;
889 struct tab_list tabs
;
890 struct history_list hl
;
891 struct session_list sessions
;
892 struct download_list downloads
;
893 struct domain_list c_wl
;
894 struct domain_list js_wl
;
895 struct undo_tailq undos
;
896 struct keybinding_list kbl
;
898 struct command_list chl
;
899 struct command_list shl
;
900 struct command_entry
*history_at
;
901 struct command_entry
*search_at
;
903 int updating_dl_tabs
= 0;
904 int updating_hl_tabs
= 0;
905 int updating_cl_tabs
= 0;
906 int updating_fl_tabs
= 0;
907 int cmd_history_count
= 0;
908 int search_history_count
= 0;
910 uint64_t blocked_cookies
= 0;
911 char named_session
[PATH_MAX
];
912 GtkListStore
*completion_model
;
913 GtkListStore
*buffers_store
;
915 void xxx_dir(char *);
916 int icon_size_map(int);
917 void completion_add(struct tab
*);
918 void completion_add_uri(const gchar
*);
919 void show_oops(struct tab
*, const char *, ...);
922 history_delete(struct command_list
*l
, int *counter
)
924 struct command_entry
*c
;
926 if (l
== NULL
|| counter
== NULL
)
929 c
= TAILQ_LAST(l
, command_list
);
933 TAILQ_REMOVE(l
, c
, entry
);
940 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
942 struct command_entry
*c
;
945 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
948 /* don't add the same line */
949 c
= TAILQ_FIRST(list
);
951 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
954 c
= g_malloc0(sizeof *c
);
955 c
->line
= g_strdup_printf(" %s", l
);
958 TAILQ_INSERT_HEAD(list
, c
, entry
);
961 history_delete(list
, counter
);
963 if (history_autosave
&& file
) {
964 f
= fopen(file
, "w");
966 show_oops(NULL
, "couldn't write history %s", file
);
970 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
972 fprintf(f
, "%s\n", c
->line
);
980 history_read(struct command_list
*list
, char *file
, int *counter
)
983 char *s
, line
[65536];
985 if (list
== NULL
|| file
== NULL
)
988 f
= fopen(file
, "r");
990 startpage_add("couldn't open history file %s", file
);
995 s
= fgets(line
, sizeof line
, f
);
996 if (s
== NULL
|| feof(f
) || ferror(f
))
998 if ((s
= strchr(line
, '\n')) == NULL
) {
999 startpage_add("invalid history file %s", file
);
1005 history_add(list
, NULL
, line
+ 1, counter
);
1013 /* marks and quickmarks array storage.
1014 * first a-z, then A-Z, then 0-9 */
1021 if (i
>= 0 && i
<= 'z' - 'a')
1025 if (i
>= 0 && i
<= 'Z' - 'A')
1040 if (m
>= 'a' && m
<= 'z')
1041 return ret
+ m
- 'a';
1043 ret
+= 'z' - 'a' + 1;
1044 if (m
>= 'A' && m
<= 'Z')
1045 return ret
+ m
- 'A';
1047 ret
+= 'Z' - 'A' + 1;
1048 if (m
>= '0' && m
<= '9')
1049 return ret
+ m
- '0';
1058 int saved_errno
, status
;
1061 saved_errno
= errno
;
1063 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
1067 if (errno
!= ECHILD
) {
1069 clog_warn("sigchild: waitpid:");
1075 if (WIFEXITED(status
)) {
1076 if (WEXITSTATUS(status
) != 0) {
1078 clog_warnx("sigchild: child exit status: %d",
1079 WEXITSTATUS(status));
1084 clog_warnx("sigchild: child is terminated abnormally");
1089 errno
= saved_errno
;
1093 is_g_object_setting(GObject
*o
, char *str
)
1095 guint n_props
= 0, i
;
1096 GParamSpec
**proplist
;
1098 if (! G_IS_OBJECT(o
))
1101 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
1104 for (i
=0; i
< n_props
; i
++) {
1105 if (! strcmp(proplist
[i
]->name
, str
))
1112 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
1116 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
1118 "<title>%s</title>\n"
1127 addstyles
? XT_PAGE_STYLE
: "",
1136 * Display a web page from a HTML string in memory, rather than from a URL
1139 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
1141 char file
[PATH_MAX
];
1144 /* we set this to indicate we want to manually do navaction */
1146 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
1148 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1150 /* set t->xtp_meaning */
1151 for (i
= 0; i
< LENGTH(about_list
); i
++)
1152 if (!strcmp(title
, about_list
[i
].name
)) {
1157 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
1159 #if GTK_CHECK_VERSION(2, 20, 0)
1160 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1161 gtk_widget_hide(t
->spinner
);
1163 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1164 xt_icon_from_file(t
, file
);
1169 get_current_tab(void)
1173 TAILQ_FOREACH(t
, &tabs
, entry
) {
1174 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1178 warnx("%s: no current tab", __func__
);
1184 set_status(struct tab
*t
, gchar
*s
, int status
)
1192 case XT_STATUS_LOADING
:
1193 type
= g_strdup_printf("Loading: %s", s
);
1196 case XT_STATUS_LINK
:
1197 type
= g_strdup_printf("Link: %s", s
);
1199 t
->status
= g_strdup(gtk_entry_get_text(
1200 GTK_ENTRY(t
->sbe
.statusbar
)));
1204 type
= g_strdup_printf("%s", s
);
1206 t
->status
= g_strdup(type
);
1210 t
->status
= g_strdup(s
);
1212 case XT_STATUS_NOTHING
:
1217 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1223 hide_cmd(struct tab
*t
)
1225 history_at
= NULL
; /* just in case */
1226 search_at
= NULL
; /* just in case */
1227 gtk_widget_hide(t
->cmd
);
1231 show_cmd(struct tab
*t
)
1235 gtk_widget_hide(t
->oops
);
1236 gtk_widget_show(t
->cmd
);
1240 hide_buffers(struct tab
*t
)
1242 gtk_widget_hide(t
->buffers
);
1243 gtk_list_store_clear(buffers_store
);
1253 sort_tabs_by_page_num(struct tab
***stabs
)
1258 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1260 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1262 TAILQ_FOREACH(t
, &tabs
, entry
)
1263 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1269 buffers_make_list(void)
1272 const gchar
*title
= NULL
;
1274 struct tab
**stabs
= NULL
;
1276 num_tabs
= sort_tabs_by_page_num(&stabs
);
1278 for (i
= 0; i
< num_tabs
; i
++)
1280 gtk_list_store_append(buffers_store
, &iter
);
1281 title
= get_title(stabs
[i
], FALSE
);
1282 gtk_list_store_set(buffers_store
, &iter
,
1283 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1293 show_buffers(struct tab
*t
)
1295 buffers_make_list();
1296 gtk_widget_show(t
->buffers
);
1297 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1301 toggle_buffers(struct tab
*t
)
1303 if (gtk_widget_get_visible(t
->buffers
))
1310 buffers(struct tab
*t
, struct karg
*args
)
1318 hide_oops(struct tab
*t
)
1320 gtk_widget_hide(t
->oops
);
1324 show_oops(struct tab
*at
, const char *fmt
, ...)
1328 struct tab
*t
= NULL
;
1334 if ((t
= get_current_tab()) == NULL
)
1340 if (vasprintf(&msg
, fmt
, ap
) == -1)
1341 errx(1, "show_oops failed");
1344 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1345 gtk_widget_hide(t
->cmd
);
1346 gtk_widget_show(t
->oops
);
1353 get_as_string(struct settings
*s
)
1364 warnx("get_as_string skip %s\n", s
->name
);
1365 } else if (s
->type
== XT_S_INT
)
1366 r
= g_strdup_printf("%d", *s
->ival
);
1367 else if (s
->type
== XT_S_STR
)
1368 r
= g_strdup(*s
->sval
);
1369 else if (s
->type
== XT_S_FLOAT
)
1370 r
= g_strdup_printf("%f", *s
->fval
);
1372 r
= g_strdup_printf("INVALID TYPE");
1378 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1383 for (i
= 0; i
< LENGTH(rs
); i
++) {
1384 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1385 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1387 s
= get_as_string(&rs
[i
]);
1388 cb(&rs
[i
], s
, cb_args
);
1395 set_browser_mode(struct settings
*s
, char *val
)
1397 if (!strcmp(val
, "whitelist")) {
1398 browser_mode
= XT_BM_WHITELIST
;
1399 allow_volatile_cookies
= 0;
1400 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1401 cookies_enabled
= 1;
1402 enable_cookie_whitelist
= 1;
1403 read_only_cookies
= 0;
1404 save_rejected_cookies
= 0;
1405 session_timeout
= 3600;
1407 enable_js_whitelist
= 1;
1408 enable_localstorage
= 0;
1409 } else if (!strcmp(val
, "normal")) {
1410 browser_mode
= XT_BM_NORMAL
;
1411 allow_volatile_cookies
= 0;
1412 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1413 cookies_enabled
= 1;
1414 enable_cookie_whitelist
= 0;
1415 read_only_cookies
= 0;
1416 save_rejected_cookies
= 0;
1417 session_timeout
= 3600;
1419 enable_js_whitelist
= 0;
1420 enable_localstorage
= 1;
1421 } else if (!strcmp(val
, "kiosk")) {
1422 browser_mode
= XT_BM_KIOSK
;
1423 allow_volatile_cookies
= 0;
1424 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1425 cookies_enabled
= 1;
1426 enable_cookie_whitelist
= 0;
1427 read_only_cookies
= 0;
1428 save_rejected_cookies
= 0;
1429 session_timeout
= 3600;
1431 enable_js_whitelist
= 0;
1432 enable_localstorage
= 1;
1442 get_browser_mode(struct settings
*s
)
1446 if (browser_mode
== XT_BM_WHITELIST
)
1447 r
= g_strdup("whitelist");
1448 else if (browser_mode
== XT_BM_NORMAL
)
1449 r
= g_strdup("normal");
1450 else if (browser_mode
== XT_BM_KIOSK
)
1451 r
= g_strdup("kiosk");
1459 set_cookie_policy(struct settings
*s
, char *val
)
1461 if (!strcmp(val
, "no3rdparty"))
1462 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1463 else if (!strcmp(val
, "accept"))
1464 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1465 else if (!strcmp(val
, "reject"))
1466 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1474 get_cookie_policy(struct settings
*s
)
1478 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1479 r
= g_strdup("no3rdparty");
1480 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1481 r
= g_strdup("accept");
1482 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1483 r
= g_strdup("reject");
1491 get_default_script(struct settings
*s
)
1493 if (default_script
[0] == '\0')
1495 return (g_strdup(default_script
));
1499 set_default_script(struct settings
*s
, char *val
)
1502 snprintf(default_script
, sizeof default_script
, "%s/%s",
1503 pwd
->pw_dir
, &val
[1]);
1505 strlcpy(default_script
, val
, sizeof default_script
);
1511 get_download_dir(struct settings
*s
)
1513 if (download_dir
[0] == '\0')
1515 return (g_strdup(download_dir
));
1519 set_download_dir(struct settings
*s
, char *val
)
1522 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1523 pwd
->pw_dir
, &val
[1]);
1525 strlcpy(download_dir
, val
, sizeof download_dir
);
1532 * We use these to prevent people putting xxxt:// URLs on
1533 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1535 #define XT_XTP_SES_KEY_SZ 8
1536 #define XT_XTP_SES_KEY_HEX_FMT \
1537 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1538 char *dl_session_key
; /* downloads */
1539 char *hl_session_key
; /* history list */
1540 char *cl_session_key
; /* cookie list */
1541 char *fl_session_key
; /* favorites list */
1543 char work_dir
[PATH_MAX
];
1544 char certs_dir
[PATH_MAX
];
1545 char cache_dir
[PATH_MAX
];
1546 char sessions_dir
[PATH_MAX
];
1547 char cookie_file
[PATH_MAX
];
1548 SoupURI
*proxy_uri
= NULL
;
1549 SoupSession
*session
;
1550 SoupCookieJar
*s_cookiejar
;
1551 SoupCookieJar
*p_cookiejar
;
1552 char rc_fname
[PATH_MAX
];
1554 struct mime_type_list mtl
;
1555 struct alias_list aliases
;
1558 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1559 void delete_tab(struct tab
*);
1560 void setzoom_webkit(struct tab
*, int);
1561 int run_script(struct tab
*, char *);
1562 int download_rb_cmp(struct download
*, struct download
*);
1563 gboolean
cmd_execute(struct tab
*t
, char *str
);
1566 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1568 return (strcmp(h1
->uri
, h2
->uri
));
1570 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1573 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1575 return (strcmp(d1
->d
, d2
->d
));
1577 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1580 get_work_dir(struct settings
*s
)
1582 if (work_dir
[0] == '\0')
1584 return (g_strdup(work_dir
));
1588 set_work_dir(struct settings
*s
, char *val
)
1591 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1592 pwd
->pw_dir
, &val
[1]);
1594 strlcpy(work_dir
, val
, sizeof work_dir
);
1600 get_tab_style(struct settings
*s
)
1602 if (tab_style
== XT_TABS_NORMAL
)
1603 return (g_strdup("normal"));
1605 return (g_strdup("compact"));
1609 set_tab_style(struct settings
*s
, char *val
)
1611 if (!strcmp(val
, "normal"))
1612 tab_style
= XT_TABS_NORMAL
;
1613 else if (!strcmp(val
, "compact"))
1614 tab_style
= XT_TABS_COMPACT
;
1622 * generate a session key to secure xtp commands.
1623 * pass in a ptr to the key in question and it will
1624 * be modified in place.
1627 generate_xtp_session_key(char **key
)
1629 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1635 /* make a new one */
1636 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1637 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1638 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1639 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1641 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1645 * validate a xtp session key.
1649 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1651 if (strcmp(trusted
, untrusted
) != 0) {
1652 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1661 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1663 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1665 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1667 struct valid_url_types
{
1678 valid_url_type(char *url
)
1682 for (i
= 0; i
< LENGTH(vut
); i
++)
1683 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1690 print_cookie(char *msg
, SoupCookie
*c
)
1696 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1697 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1698 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1699 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1700 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1701 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1702 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1703 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1704 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1705 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1709 walk_alias(struct settings
*s
,
1710 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1715 if (s
== NULL
|| cb
== NULL
) {
1716 show_oops(NULL
, "walk_alias invalid parameters");
1720 TAILQ_FOREACH(a
, &aliases
, entry
) {
1721 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1722 cb(s
, str
, cb_args
);
1728 match_alias(char *url_in
)
1732 char *url_out
= NULL
, *search
, *enc_arg
;
1734 search
= g_strdup(url_in
);
1736 if (strsep(&arg
, " \t") == NULL
) {
1737 show_oops(NULL
, "match_alias: NULL URL");
1741 TAILQ_FOREACH(a
, &aliases
, entry
) {
1742 if (!strcmp(search
, a
->a_name
))
1747 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1750 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1751 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1754 url_out
= g_strdup_printf(a
->a_uri
, "");
1762 guess_url_type(char *url_in
)
1765 char *url_out
= NULL
, *enc_search
= NULL
;
1768 /* substitute aliases */
1769 url_out
= match_alias(url_in
);
1770 if (url_out
!= NULL
)
1773 /* see if we are an about page */
1774 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
1775 for (i
= 0; i
< LENGTH(about_list
); i
++)
1776 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
1777 about_list
[i
].name
)) {
1778 url_out
= g_strdup(url_in
);
1782 if (guess_search
&& url_regex
&&
1783 !(g_str_has_prefix(url_in
, "http://") ||
1784 g_str_has_prefix(url_in
, "https://"))) {
1785 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
1786 /* invalid URI so search instead */
1787 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1788 url_out
= g_strdup_printf(search_string
, enc_search
);
1794 /* XXX not sure about this heuristic */
1795 if (stat(url_in
, &sb
) == 0)
1796 url_out
= g_strdup_printf("file://%s", url_in
);
1798 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1800 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1806 load_uri(struct tab
*t
, gchar
*uri
)
1809 gchar
*newuri
= NULL
;
1815 /* Strip leading spaces. */
1816 while (*uri
&& isspace(*uri
))
1819 if (strlen(uri
) == 0) {
1824 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1826 if (valid_url_type(uri
)) {
1827 newuri
= guess_url_type(uri
);
1831 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1832 for (i
= 0; i
< LENGTH(about_list
); i
++)
1833 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1834 bzero(&args
, sizeof args
);
1835 about_list
[i
].func(t
, &args
);
1836 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1840 show_oops(t
, "invalid about page");
1844 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1846 webkit_web_view_load_uri(t
->wv
, uri
);
1853 get_uri(struct tab
*t
)
1855 const gchar
*uri
= NULL
;
1857 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1859 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1860 uri
= webkit_web_view_get_uri(t
->wv
);
1862 /* use tmp_uri to make sure it is g_freed */
1865 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1866 about_list
[t
->xtp_meaning
].name
);
1873 get_title(struct tab
*t
, bool window
)
1875 const gchar
*set
= NULL
, *title
= NULL
;
1876 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1878 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1879 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1882 title
= webkit_web_view_get_title(t
->wv
);
1883 if ((set
= title
? title
: get_uri(t
)))
1887 set
= window
? XT_NAME
: "(untitled)";
1893 add_alias(struct settings
*s
, char *line
)
1896 struct alias
*a
= NULL
;
1898 if (s
== NULL
|| line
== NULL
) {
1899 show_oops(NULL
, "add_alias invalid parameters");
1904 a
= g_malloc(sizeof(*a
));
1906 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1907 show_oops(NULL
, "add_alias: incomplete alias definition");
1910 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1911 show_oops(NULL
, "add_alias: invalid alias definition");
1915 a
->a_name
= g_strdup(alias
);
1916 a
->a_uri
= g_strdup(l
);
1918 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1920 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1930 add_mime_type(struct settings
*s
, char *line
)
1934 struct mime_type
*m
= NULL
;
1935 int downloadfirst
= 0;
1937 /* XXX this could be smarter */
1939 if (line
== NULL
|| strlen(line
) == 0) {
1940 show_oops(NULL
, "add_mime_type invalid parameters");
1949 m
= g_malloc(sizeof(*m
));
1951 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1952 show_oops(NULL
, "add_mime_type: invalid mime_type");
1955 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1956 mime_type
[strlen(mime_type
) - 1] = '\0';
1961 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1962 show_oops(NULL
, "add_mime_type: invalid mime_type");
1966 m
->mt_type
= g_strdup(mime_type
);
1967 m
->mt_action
= g_strdup(l
);
1968 m
->mt_download
= downloadfirst
;
1970 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1971 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1973 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1983 find_mime_type(char *mime_type
)
1985 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1987 TAILQ_FOREACH(m
, &mtl
, entry
) {
1988 if (m
->mt_default
&&
1989 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1992 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
2005 walk_mime_type(struct settings
*s
,
2006 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2008 struct mime_type
*m
;
2011 if (s
== NULL
|| cb
== NULL
) {
2012 show_oops(NULL
, "walk_mime_type invalid parameters");
2016 TAILQ_FOREACH(m
, &mtl
, entry
) {
2017 str
= g_strdup_printf("%s%s --> %s",
2019 m
->mt_default
? "*" : "",
2021 cb(s
, str
, cb_args
);
2027 wl_add(char *str
, struct domain_list
*wl
, int handy
)
2033 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
2036 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
2038 /* treat *.moo.com the same as .moo.com */
2039 if (str
[0] == '*' && str
[1] == '.')
2041 else if (str
[0] == '.')
2046 /* slice off port number */
2047 p
= g_strrstr(str
, ":");
2051 d
= g_malloc(sizeof *d
);
2053 d
->d
= g_strdup_printf(".%s", str
);
2055 d
->d
= g_strdup(str
);
2058 if (RB_INSERT(domain_list
, wl
, d
))
2061 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
2072 add_cookie_wl(struct settings
*s
, char *entry
)
2074 wl_add(entry
, &c_wl
, 1);
2079 walk_cookie_wl(struct settings
*s
,
2080 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2084 if (s
== NULL
|| cb
== NULL
) {
2085 show_oops(NULL
, "walk_cookie_wl invalid parameters");
2089 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
2090 cb(s
, d
->d
, cb_args
);
2094 walk_js_wl(struct settings
*s
,
2095 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
2099 if (s
== NULL
|| cb
== NULL
) {
2100 show_oops(NULL
, "walk_js_wl invalid parameters");
2104 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
2105 cb(s
, d
->d
, cb_args
);
2109 add_js_wl(struct settings
*s
, char *entry
)
2111 wl_add(entry
, &js_wl
, 1 /* persistent */);
2116 wl_find(const gchar
*search
, struct domain_list
*wl
)
2119 struct domain
*d
= NULL
, dfind
;
2122 if (search
== NULL
|| wl
== NULL
)
2124 if (strlen(search
) < 2)
2127 if (search
[0] != '.')
2128 s
= g_strdup_printf(".%s", search
);
2130 s
= g_strdup(search
);
2132 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
2135 d
= RB_FIND(domain_list
, wl
, &dfind
);
2149 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
2155 if (s
== NULL
|| wl
== NULL
)
2158 if (!strncmp(s
, "http://", strlen("http://")))
2159 s
= &s
[strlen("http://")];
2160 else if (!strncmp(s
, "https://", strlen("https://")))
2161 s
= &s
[strlen("https://")];
2166 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
2167 /* chop string at first slash */
2168 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
2171 r
= wl_find(ss
, wl
);
2180 settings_add(char *var
, char *val
)
2187 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2188 if (strcmp(var
, rs
[i
].name
))
2192 if (rs
[i
].s
->set(&rs
[i
], val
))
2193 errx(1, "invalid value for %s: %s", var
, val
);
2197 switch (rs
[i
].type
) {
2206 errx(1, "invalid sval for %s",
2220 errx(1, "invalid type for %s", var
);
2229 config_parse(char *filename
, int runtime
)
2232 char *line
, *cp
, *var
, *val
;
2233 size_t len
, lineno
= 0;
2235 char file
[PATH_MAX
];
2238 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2240 if (filename
== NULL
)
2243 if (runtime
&& runtime_settings
[0] != '\0') {
2244 snprintf(file
, sizeof file
, "%s/%s",
2245 work_dir
, runtime_settings
);
2246 if (stat(file
, &sb
)) {
2247 warnx("runtime file doesn't exist, creating it");
2248 if ((f
= fopen(file
, "w")) == NULL
)
2250 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2254 strlcpy(file
, filename
, sizeof file
);
2256 if ((config
= fopen(file
, "r")) == NULL
) {
2257 warn("config_parse: cannot open %s", filename
);
2262 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2263 if (feof(config
) || ferror(config
))
2267 cp
+= (long)strspn(cp
, WS
);
2268 if (cp
[0] == '\0') {
2274 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2275 startpage_add("invalid configuration file entry: %s",
2278 cp
+= (long)strspn(cp
, WS
);
2280 if ((val
= strsep(&cp
, "\0")) == NULL
)
2283 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2284 handled
= settings_add(var
, val
);
2286 startpage_add("invalid configuration file entry: %s=%s",
2296 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2302 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2306 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2309 JSStringGetUTF8CString(jsref
, s
, l
);
2310 JSStringRelease(jsref
);
2316 disable_hints(struct tab
*t
)
2318 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2319 bzero(t
->hint_num
, sizeof t
->hint_num
);
2320 run_script(t
, "vimprobable_clear()");
2322 t
->hint_mode
= XT_HINT_NONE
;
2326 enable_hints(struct tab
*t
)
2328 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2329 run_script(t
, "vimprobable_show_hints()");
2331 t
->hint_mode
= XT_HINT_NONE
;
2334 #define XT_JS_OPEN ("open;")
2335 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2336 #define XT_JS_FIRE ("fire;")
2337 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2338 #define XT_JS_FOUND ("found;")
2339 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2342 run_script(struct tab
*t
, char *s
)
2344 JSGlobalContextRef ctx
;
2345 WebKitWebFrame
*frame
;
2347 JSValueRef val
, exception
;
2350 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2351 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2353 frame
= webkit_web_view_get_main_frame(t
->wv
);
2354 ctx
= webkit_web_frame_get_global_context(frame
);
2356 str
= JSStringCreateWithUTF8CString(s
);
2357 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2358 NULL
, 0, &exception
);
2359 JSStringRelease(str
);
2361 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2363 es
= js_ref_to_string(ctx
, exception
);
2364 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2368 es
= js_ref_to_string(ctx
, val
);
2369 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2371 /* handle return value right here */
2372 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2375 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2378 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2379 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2380 &es
[XT_JS_FIRE_LEN
]);
2385 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2386 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2397 hint(struct tab
*t
, struct karg
*args
)
2400 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2402 if (t
->hints_on
== 0)
2411 apply_style(struct tab
*t
)
2413 g_object_set(G_OBJECT(t
->settings
),
2414 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2418 userstyle(struct tab
*t
, struct karg
*args
)
2420 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2424 g_object_set(G_OBJECT(t
->settings
),
2425 "user-stylesheet-uri", NULL
, (char *)NULL
);
2434 * Doesn't work fully, due to the following bug:
2435 * https://bugs.webkit.org/show_bug.cgi?id=51747
2438 restore_global_history(void)
2440 char file
[PATH_MAX
];
2445 const char delim
[3] = {'\\', '\\', '\0'};
2447 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2449 if ((f
= fopen(file
, "r")) == NULL
) {
2450 warnx("%s: fopen", __func__
);
2455 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2456 if (feof(f
) || ferror(f
))
2459 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2460 if (feof(f
) || ferror(f
)) {
2462 warnx("%s: broken history file\n", __func__
);
2466 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2467 webkit_web_history_item_new_with_data(uri
, title
);
2468 h
= g_malloc(sizeof(struct history
));
2469 h
->uri
= g_strdup(uri
);
2470 h
->title
= g_strdup(title
);
2471 RB_INSERT(history_list
, &hl
, h
);
2472 completion_add_uri(h
->uri
);
2474 warnx("%s: failed to restore history\n", __func__
);
2490 save_global_history_to_disk(struct tab
*t
)
2492 char file
[PATH_MAX
];
2496 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2498 if ((f
= fopen(file
, "w")) == NULL
) {
2499 show_oops(t
, "%s: global history file: %s",
2500 __func__
, strerror(errno
));
2504 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2505 if (h
->uri
&& h
->title
)
2506 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2515 quit(struct tab
*t
, struct karg
*args
)
2517 if (save_global_history
)
2518 save_global_history_to_disk(t
);
2526 restore_sessions_list(void)
2529 struct dirent
*dp
= NULL
;
2532 sdir
= opendir(sessions_dir
);
2534 while ((dp
= readdir(sdir
)) != NULL
)
2535 if (dp
->d_type
== DT_REG
) {
2536 s
= g_malloc(sizeof(struct session
));
2537 s
->name
= g_strdup(dp
->d_name
);
2538 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2545 open_tabs(struct tab
*t
, struct karg
*a
)
2547 char file
[PATH_MAX
];
2551 struct tab
*ti
, *tt
;
2556 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2557 if ((f
= fopen(file
, "r")) == NULL
)
2560 ti
= TAILQ_LAST(&tabs
, tab_list
);
2563 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
2564 if (feof(f
) || ferror(f
))
2567 /* retrieve session name */
2568 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2569 strlcpy(named_session
,
2570 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2571 sizeof named_session
);
2575 if (uri
&& strlen(uri
))
2576 create_new_tab(uri
, NULL
, 1, -1);
2582 /* close open tabs */
2583 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2585 tt
= TAILQ_FIRST(&tabs
);
2605 restore_saved_tabs(void)
2607 char file
[PATH_MAX
];
2608 int unlink_file
= 0;
2613 snprintf(file
, sizeof file
, "%s/%s",
2614 sessions_dir
, XT_RESTART_TABS_FILE
);
2615 if (stat(file
, &sb
) == -1)
2616 a
.s
= XT_SAVED_TABS_FILE
;
2619 a
.s
= XT_RESTART_TABS_FILE
;
2622 a
.i
= XT_SES_DONOTHING
;
2623 rv
= open_tabs(NULL
, &a
);
2632 save_tabs(struct tab
*t
, struct karg
*a
)
2634 char file
[PATH_MAX
];
2636 int num_tabs
= 0, i
;
2637 struct tab
**stabs
= NULL
;
2642 snprintf(file
, sizeof file
, "%s/%s",
2643 sessions_dir
, named_session
);
2645 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2647 if ((f
= fopen(file
, "w")) == NULL
) {
2648 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2652 /* save session name */
2653 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2655 /* Save tabs, in the order they are arranged in the notebook. */
2656 num_tabs
= sort_tabs_by_page_num(&stabs
);
2658 for (i
= 0; i
< num_tabs
; i
++)
2660 if (get_uri(stabs
[i
]) != NULL
)
2661 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2662 else if (gtk_entry_get_text(GTK_ENTRY(
2663 stabs
[i
]->uri_entry
)))
2664 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
2665 stabs
[i
]->uri_entry
)));
2670 /* try and make sure this gets to disk NOW. XXX Backup first? */
2671 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2672 show_oops(t
, "May not have managed to save session: %s",
2682 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2694 run_page_script(struct tab
*t
, struct karg
*args
)
2697 char *tmp
, script
[PATH_MAX
];
2699 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2700 if (tmp
[0] == '\0') {
2701 show_oops(t
, "no script specified");
2705 if ((uri
= get_uri(t
)) == NULL
) {
2706 show_oops(t
, "tab is empty, not running script");
2711 snprintf(script
, sizeof script
, "%s/%s",
2712 pwd
->pw_dir
, &tmp
[1]);
2714 strlcpy(script
, tmp
, sizeof script
);
2718 show_oops(t
, "can't fork to run script");
2728 execlp(script
, script
, uri
, (void *)NULL
);
2738 yank_uri(struct tab
*t
, struct karg
*args
)
2741 GtkClipboard
*clipboard
;
2743 if ((uri
= get_uri(t
)) == NULL
)
2746 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2747 gtk_clipboard_set_text(clipboard
, uri
, -1);
2753 paste_uri(struct tab
*t
, struct karg
*args
)
2755 GtkClipboard
*clipboard
;
2756 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2758 gchar
*p
= NULL
, *uri
;
2760 /* try primary clipboard first */
2761 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2762 p
= gtk_clipboard_wait_for_text(clipboard
);
2764 /* if it failed get whatever text is in cut_buffer0 */
2765 if (p
== NULL
&& xterm_workaround
)
2766 if (gdk_property_get(gdk_get_default_root_window(),
2768 gdk_atom_intern("STRING", FALSE
),
2770 1024 * 1024 /* picked out of my butt */,
2776 /* yes sir, we need to NUL the string */
2782 while (*uri
&& isspace(*uri
))
2784 if (strlen(uri
) == 0) {
2785 show_oops(t
, "empty paste buffer");
2788 if (guess_search
== 0 && valid_url_type(uri
)) {
2789 /* we can be clever and paste this in search box */
2790 show_oops(t
, "not a valid URL");
2794 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2796 else if (args
->i
== XT_PASTE_NEW_TAB
)
2797 create_new_tab(uri
, NULL
, 1, -1);
2808 find_domain(const gchar
*s
, int toplevel
)
2816 uri
= soup_uri_new(s
);
2818 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2822 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2823 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2824 while(--p
>= uri
->host
&& *p
!= '.');
2831 ret
= g_strdup_printf(".%s", p
);
2839 toggle_cwl(struct tab
*t
, struct karg
*args
)
2850 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2852 if (uri
== NULL
|| dom
== NULL
||
2853 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2854 show_oops(t
, "Can't toggle domain in cookie white list");
2857 d
= wl_find(dom
, &c_wl
);
2864 if (args
->i
& XT_WL_TOGGLE
)
2866 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2868 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2872 /* enable cookies for domain */
2873 wl_add(dom
, &c_wl
, 0);
2875 /* disable cookies for domain */
2876 RB_REMOVE(domain_list
, &c_wl
, d
);
2878 if (args
->i
& XT_WL_RELOAD
)
2879 webkit_web_view_reload(t
->wv
);
2887 toggle_js(struct tab
*t
, struct karg
*args
)
2897 g_object_get(G_OBJECT(t
->settings
),
2898 "enable-scripts", &es
, (char *)NULL
);
2899 if (args
->i
& XT_WL_TOGGLE
)
2901 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2903 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2909 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2911 if (uri
== NULL
|| dom
== NULL
||
2912 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2913 show_oops(t
, "Can't toggle domain in JavaScript white list");
2918 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2919 wl_add(dom
, &js_wl
, 0 /* session */);
2921 d
= wl_find(dom
, &js_wl
);
2923 RB_REMOVE(domain_list
, &js_wl
, d
);
2924 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2926 g_object_set(G_OBJECT(t
->settings
),
2927 "enable-scripts", es
, (char *)NULL
);
2928 g_object_set(G_OBJECT(t
->settings
),
2929 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2930 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2932 if (args
->i
& XT_WL_RELOAD
)
2933 webkit_web_view_reload(t
->wv
);
2941 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2945 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2948 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2953 toggle_src(struct tab
*t
, struct karg
*args
)
2960 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2961 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2962 webkit_web_view_reload(t
->wv
);
2968 focus_webview(struct tab
*t
)
2973 /* only grab focus if we are visible */
2974 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2975 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2979 focus(struct tab
*t
, struct karg
*args
)
2981 if (t
== NULL
|| args
== NULL
)
2987 if (args
->i
== XT_FOCUS_URI
)
2988 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2989 else if (args
->i
== XT_FOCUS_SEARCH
)
2990 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2996 stats(struct tab
*t
, struct karg
*args
)
2998 char *page
, *body
, *s
, line
[64 * 1024];
2999 uint64_t line_count
= 0;
3003 show_oops(NULL
, "stats invalid parameters");
3006 if (save_rejected_cookies
) {
3007 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
3009 s
= fgets(line
, sizeof line
, r_cookie_f
);
3010 if (s
== NULL
|| feof(r_cookie_f
) ||
3016 snprintf(line
, sizeof line
,
3017 "<br/>Cookies blocked(*) total: %llu", line_count
);
3019 show_oops(t
, "Can't open blocked cookies file: %s",
3023 body
= g_strdup_printf(
3024 "Cookies blocked(*) this session: %llu"
3026 "<p><small><b>*</b> results vary based on settings</small></p>",
3030 page
= get_html_page("Statistics", body
, "", 0);
3033 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
3040 marco(struct tab
*t
, struct karg
*args
)
3042 char *page
, line
[64 * 1024];
3046 show_oops(NULL
, "marco invalid parameters");
3049 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
3051 page
= get_html_page("Marco Sez...", line
, "", 0);
3053 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
3060 blank(struct tab
*t
, struct karg
*args
)
3063 show_oops(NULL
, "blank invalid parameters");
3065 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
3071 about(struct tab
*t
, struct karg
*args
)
3076 show_oops(NULL
, "about invalid parameters");
3078 body
= g_strdup_printf("<b>Version: %s</b>"
3079 #ifdef XXXTERM_BUILDSTR
3080 "<br><b>Build: %s</b>"
3085 "<li>Marco Peereboom <marco@peereboom.us></li>"
3086 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
3087 "<li>Edd Barrett <vext01@gmail.com> </li>"
3088 "<li>Todd T. Fries <todd@fries.net> </li>"
3089 "<li>Raphael Graf <r@undefined.ch> </li>"
3091 "Copyrights and licenses can be found on the XXXTerm "
3092 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
3094 #ifdef XXXTERM_BUILDSTR
3095 version
, XXXTERM_BUILDSTR
3101 page
= get_html_page("About", body
, "", 0);
3104 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
3111 help(struct tab
*t
, struct karg
*args
)
3113 char *page
, *head
, *body
;
3116 show_oops(NULL
, "help invalid parameters");
3118 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
3119 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
3121 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
3122 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
3123 "cgi-bin/man-cgi?xxxterm</a>";
3125 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
3127 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
3134 startpage(struct tab
*t
, struct karg
*args
)
3136 char *page
, *body
, *b
;
3140 show_oops(NULL
, "startpage invalid parameters");
3142 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
3144 TAILQ_FOREACH(s
, &spl
, entry
) {
3146 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
3150 page
= get_html_page("Startup Exception", body
, "", 0);
3153 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
3160 startpage_add(const char *fmt
, ...)
3170 if (vasprintf(&msg
, fmt
, ap
) == -1)
3171 errx(1, "startpage_add failed");
3174 s
= g_malloc0(sizeof *s
);
3177 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
3181 * update all favorite tabs apart from one. Pass NULL if
3182 * you want to update all.
3185 update_favorite_tabs(struct tab
*apart_from
)
3188 if (!updating_fl_tabs
) {
3189 updating_fl_tabs
= 1; /* stop infinite recursion */
3190 TAILQ_FOREACH(t
, &tabs
, entry
)
3191 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
3192 && (t
!= apart_from
))
3193 xtp_page_fl(t
, NULL
);
3194 updating_fl_tabs
= 0;
3198 /* show a list of favorites (bookmarks) */
3200 xtp_page_fl(struct tab
*t
, struct karg
*args
)
3202 char file
[PATH_MAX
];
3204 char *uri
= NULL
, *title
= NULL
;
3205 size_t len
, lineno
= 0;
3207 char *body
, *tmp
, *page
= NULL
;
3208 const char delim
[3] = {'\\', '\\', '\0'};
3210 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
3213 warn("%s: bad param", __func__
);
3215 /* new session key */
3216 if (!updating_fl_tabs
)
3217 generate_xtp_session_key(&fl_session_key
);
3219 /* open favorites */
3220 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3221 if ((f
= fopen(file
, "r")) == NULL
) {
3222 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3227 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
3228 "<th style='width: 40px'>#</th><th>Link</th>"
3229 "<th style='width: 40px'>Rm</th></tr>\n");
3232 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3234 if (strlen(title
) == 0 || title
[0] == '#') {
3240 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
3241 if (feof(f
) || ferror(f
)) {
3242 show_oops(t
, "favorites file corrupt");
3248 body
= g_strdup_printf("%s<tr>"
3250 "<td><a href='%s'>%s</a></td>"
3251 "<td style='text-align: center'>"
3252 "<a href='%s%d/%s/%d/%d'>X</a></td>"
3254 body
, i
, uri
, title
,
3255 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3267 /* if none, say so */
3270 body
= g_strdup_printf("%s<tr>"
3271 "<td colspan='3' style='text-align: center'>"
3272 "No favorites - To add one use the 'favadd' command."
3273 "</td></tr>", body
);
3278 body
= g_strdup_printf("%s</table>", body
);
3288 page
= get_html_page("Favorites", body
, "", 1);
3289 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3293 update_favorite_tabs(t
);
3302 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3303 size_t cert_count
, char *title
)
3305 gnutls_datum_t cinfo
;
3309 body
= g_strdup("");
3311 for (i
= 0; i
< cert_count
; i
++) {
3312 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3317 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3318 body
, i
, cinfo
.data
);
3319 gnutls_free(cinfo
.data
);
3323 tmp
= get_html_page(title
, body
, "", 0);
3326 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3331 ca_cmd(struct tab
*t
, struct karg
*args
)
3334 int rv
= 1, certs
= 0, certs_read
;
3337 gnutls_x509_crt_t
*c
= NULL
;
3338 char *certs_buf
= NULL
, *s
;
3340 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3341 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3345 if (fstat(fileno(f
), &sb
) == -1) {
3346 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3350 certs_buf
= g_malloc(sb
.st_size
+ 1);
3351 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3352 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3355 certs_buf
[sb
.st_size
] = '\0';
3358 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3360 s
+= strlen("BEGIN CERTIFICATE");
3363 bzero(&dt
, sizeof dt
);
3364 dt
.data
= (unsigned char *)certs_buf
;
3365 dt
.size
= sb
.st_size
;
3366 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3367 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3368 GNUTLS_X509_FMT_PEM
, 0);
3369 if (certs_read
<= 0) {
3370 show_oops(t
, "No cert(s) available");
3373 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3386 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
3390 struct addrinfo hints
, *res
= NULL
, *ai
;
3391 int rv
= -1, s
= -1, on
, error
;
3393 static gchar myerror
[256]; /* this is not thread safe */
3396 *error_str
= myerror
;
3397 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
3398 *error_str
= "invalid URI";
3402 su
= soup_uri_new(uri
);
3404 *error_str
= "invalid soup URI";
3407 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
3408 *error_str
= "invalid HTTPS URI";
3412 snprintf(port
, sizeof port
, "%d", su
->port
);
3413 bzero(&hints
, sizeof(struct addrinfo
));
3414 hints
.ai_flags
= AI_CANONNAME
;
3415 hints
.ai_family
= AF_UNSPEC
;
3416 hints
.ai_socktype
= SOCK_STREAM
;
3418 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
3419 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
3420 gai_strerror(errno
));
3424 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3430 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3432 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3435 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3438 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
3442 snprintf(myerror
, sizeof myerror
,
3443 "could not obtain certificates from: %s",
3449 strlcpy(domain
, su
->host
, domain_sz
);
3456 if (rv
== -1 && s
!= -1)
3463 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3466 gnutls_deinit(gsession
);
3468 gnutls_certificate_free_credentials(xcred
);
3474 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
3475 gnutls_certificate_credentials_t
*xc
)
3477 gnutls_certificate_credentials_t xcred
;
3478 gnutls_session_t gsession
;
3480 static gchar myerror
[1024]; /* this is not thread safe */
3482 if (gs
== NULL
|| xc
== NULL
)
3489 gnutls_certificate_allocate_credentials(&xcred
);
3490 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3491 GNUTLS_X509_FMT_PEM
);
3493 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3494 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3495 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3496 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3497 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3498 snprintf(myerror
, sizeof myerror
,
3499 "gnutls_handshake failed %d fatal %d %s",
3501 gnutls_error_is_fatal(rv
),
3502 gnutls_strerror_name(rv
));
3503 stop_tls(gsession
, xcred
);
3507 gnutls_credentials_type_t cred
;
3508 cred
= gnutls_auth_get_type(gsession
);
3509 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3510 snprintf(myerror
, sizeof myerror
,
3511 "gnutls_auth_get_type failed %d",
3513 stop_tls(gsession
, xcred
);
3521 *error_str
= myerror
;
3526 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3530 const gnutls_datum_t
*cl
;
3531 gnutls_x509_crt_t
*all_certs
;
3534 if (certs
== NULL
|| cert_count
== NULL
)
3536 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3538 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3542 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3543 for (i
= 0; i
< len
; i
++) {
3544 gnutls_x509_crt_init(&all_certs
[i
]);
3545 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3546 GNUTLS_X509_FMT_PEM
< 0)) {
3560 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3564 for (i
= 0; i
< cert_count
; i
++)
3565 gnutls_x509_crt_deinit(certs
[i
]);
3570 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3572 GdkColor c_text
, c_base
;
3574 gdk_color_parse(text
, &c_text
);
3575 gdk_color_parse(base
, &c_base
);
3577 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3578 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3579 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3580 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3582 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3583 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3584 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3585 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3589 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3590 size_t cert_count
, char *domain
)
3593 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3598 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3601 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3602 if ((f
= fopen(file
, "w")) == NULL
) {
3603 show_oops(t
, "Can't create cert file %s %s",
3604 file
, strerror(errno
));
3608 for (i
= 0; i
< cert_count
; i
++) {
3609 cert_buf_sz
= sizeof cert_buf
;
3610 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3611 cert_buf
, &cert_buf_sz
)) {
3612 show_oops(t
, "gnutls_x509_crt_export failed");
3615 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3616 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3621 /* not the best spot but oh well */
3622 gdk_color_parse("lightblue", &color
);
3623 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3624 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3637 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
3639 char domain
[8182], file
[PATH_MAX
];
3640 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3642 unsigned int error
= 0;
3644 size_t cert_buf_sz
, cert_count
;
3645 enum cert_trust rv
= CERT_UNTRUSTED
;
3646 static gchar serr
[80]; /* this isn't thread safe */
3647 gnutls_session_t gsession
;
3648 gnutls_x509_crt_t
*certs
;
3649 gnutls_certificate_credentials_t xcred
;
3651 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
3655 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
3656 sizeof domain
)) == -1)
3659 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
3662 if (start_tls(error_str
, s
, &gsession
, &xcred
))
3664 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
3666 /* verify certs in case cert file doesn't exist */
3667 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
3669 *error_str
= "Invalid certificates";
3674 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3675 *error_str
= "Can't get connection certificates";
3679 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3680 if ((f
= fopen(file
, "r")) == NULL
) {
3686 for (i
= 0; i
< cert_count
; i
++) {
3687 cert_buf_sz
= sizeof cert_buf
;
3688 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3689 cert_buf
, &cert_buf_sz
)) {
3692 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3693 rv
= CERT_BAD
; /* critical */
3696 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3697 rv
= CERT_BAD
; /* critical */
3706 free_connection_certs(certs
, cert_count
);
3708 /* we close the socket first for speed */
3712 /* only complain if we didn't save it locally */
3713 if (error
&& rv
!= CERT_LOCAL
) {
3714 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
3715 if (error
& GNUTLS_CERT_INVALID
)
3716 strlcat(serr
, "invalid, ", sizeof serr
);
3717 if (error
& GNUTLS_CERT_REVOKED
)
3718 strlcat(serr
, "revoked, ", sizeof serr
);
3719 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
3720 strlcat(serr
, "signer not found, ", sizeof serr
);
3721 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
3722 strlcat(serr
, "not signed by CA, ", sizeof serr
);
3723 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
3724 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
3725 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
3726 strlcat(serr
, "not activated, ", sizeof serr
);
3727 if (error
& GNUTLS_CERT_EXPIRED
)
3728 strlcat(serr
, "expired, ", sizeof serr
);
3729 for (i
= strlen(serr
) - 1; i
> 0; i
--)
3730 if (serr
[i
] == ',') {
3737 stop_tls(gsession
, xcred
);
3743 cert_cmd(struct tab
*t
, struct karg
*args
)
3745 const gchar
*uri
, *error_str
= NULL
;
3749 gnutls_session_t gsession
;
3750 gnutls_x509_crt_t
*certs
;
3751 gnutls_certificate_credentials_t xcred
;
3756 if (ssl_ca_file
== NULL
) {
3757 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3761 if ((uri
= get_uri(t
)) == NULL
) {
3762 show_oops(t
, "Invalid URI");
3766 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
3767 sizeof domain
)) == -1) {
3768 show_oops(t
, "%s", error_str
);
3773 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
3777 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3778 show_oops(t
, "get_connection_certs failed");
3782 if (args
->i
& XT_SHOW
)
3783 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3784 else if (args
->i
& XT_SAVE
)
3785 save_certs(t
, certs
, cert_count
, domain
);
3787 free_connection_certs(certs
, cert_count
);
3789 /* we close the socket first for speed */
3792 stop_tls(gsession
, xcred
);
3794 show_oops(t
, "%s", error_str
);
3799 remove_cookie(int index
)
3805 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3807 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3809 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3813 print_cookie("remove cookie", c
);
3814 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3819 soup_cookies_free(cf
);
3825 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3830 body
= g_strdup("");
3833 if (args
->i
& XT_WL_PERSISTENT
) {
3835 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3837 RB_FOREACH(d
, domain_list
, wl
) {
3841 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3847 if (args
->i
& XT_WL_SESSION
) {
3849 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3851 RB_FOREACH(d
, domain_list
, wl
) {
3855 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3860 tmp
= get_html_page(title
, body
, "", 0);
3863 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3865 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3871 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3873 char file
[PATH_MAX
];
3875 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3883 if (t
== NULL
|| args
== NULL
)
3886 if (runtime_settings
[0] == '\0')
3889 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3890 if ((f
= fopen(file
, "r+")) == NULL
)
3894 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3895 if (uri
== NULL
|| dom
== NULL
||
3896 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3897 show_oops(t
, "Can't add domain to %s white list",
3898 js
? "JavaScript" : "cookie");
3902 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3905 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3908 if (!strcmp(line
, lt
))
3914 fprintf(f
, "%s\n", lt
);
3919 d
= wl_find(dom
, &js_wl
);
3921 settings_add("js_wl", dom
);
3922 d
= wl_find(dom
, &js_wl
);
3926 d
= wl_find(dom
, &c_wl
);
3928 settings_add("cookie_wl", dom
);
3929 d
= wl_find(dom
, &c_wl
);
3933 /* find and add to persistent jar */
3934 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3935 for (;cf
; cf
= cf
->next
) {
3937 if (!strcmp(dom
, ci
->domain
) ||
3938 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3939 c
= soup_cookie_copy(ci
);
3940 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3943 soup_cookies_free(cf
);
3961 js_show_wl(struct tab
*t
, struct karg
*args
)
3963 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3964 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3970 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3972 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3973 wl_show(t
, args
, "Cookie White List", &c_wl
);
3979 cookie_cmd(struct tab
*t
, struct karg
*args
)
3981 if (args
->i
& XT_SHOW
)
3982 wl_show(t
, args
, "Cookie White List", &c_wl
);
3983 else if (args
->i
& XT_WL_TOGGLE
) {
3984 args
->i
|= XT_WL_RELOAD
;
3985 toggle_cwl(t
, args
);
3986 } else if (args
->i
& XT_SAVE
) {
3987 args
->i
|= XT_WL_RELOAD
;
3988 wl_save(t
, args
, 0);
3989 } else if (args
->i
& XT_DELETE
)
3990 show_oops(t
, "'cookie delete' currently unimplemented");
3996 js_cmd(struct tab
*t
, struct karg
*args
)
3998 if (args
->i
& XT_SHOW
)
3999 wl_show(t
, args
, "JavaScript White List", &js_wl
);
4000 else if (args
->i
& XT_SAVE
) {
4001 args
->i
|= XT_WL_RELOAD
;
4002 wl_save(t
, args
, 1);
4003 } else if (args
->i
& XT_WL_TOGGLE
) {
4004 args
->i
|= XT_WL_RELOAD
;
4006 } else if (args
->i
& XT_DELETE
)
4007 show_oops(t
, "'js delete' currently unimplemented");
4013 toplevel_cmd(struct tab
*t
, struct karg
*args
)
4015 js_toggle_cb(t
->js_toggle
, t
);
4021 add_favorite(struct tab
*t
, struct karg
*args
)
4023 char file
[PATH_MAX
];
4026 size_t urilen
, linelen
;
4027 const gchar
*uri
, *title
;
4032 /* don't allow adding of xtp pages to favorites */
4033 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
4034 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
4038 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4039 if ((f
= fopen(file
, "r+")) == NULL
) {
4040 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
4044 title
= get_title(t
, FALSE
);
4047 if (title
== NULL
|| uri
== NULL
) {
4048 show_oops(t
, "can't add page to favorites");
4052 urilen
= strlen(uri
);
4055 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
4056 if (feof(f
) || ferror(f
))
4059 if (linelen
== urilen
&& !strcmp(line
, uri
))
4066 fprintf(f
, "\n%s\n%s", title
, uri
);
4072 update_favorite_tabs(NULL
);
4078 can_go_back_for_real(struct tab
*t
)
4081 WebKitWebHistoryItem
*item
;
4087 /* rely on webkit to make sure we can go backward when on an about page */
4089 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4090 return (webkit_web_view_can_go_back(t
->wv
));
4092 /* the back/forwars list is stupid so help determine if we can go back */
4093 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4095 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4096 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4104 can_go_forward_for_real(struct tab
*t
)
4107 WebKitWebHistoryItem
*item
;
4113 /* rely on webkit to make sure we can go forward when on an about page */
4115 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
4116 return (webkit_web_view_can_go_forward(t
->wv
));
4118 /* the back/forwars list is stupid so help selecting a different item */
4119 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4121 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4122 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
4130 go_back_for_real(struct tab
*t
)
4133 WebKitWebHistoryItem
*item
;
4141 webkit_web_view_go_back(t
->wv
);
4144 /* the back/forwars list is stupid so help selecting a different item */
4145 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4147 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4148 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4149 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4156 go_forward_for_real(struct tab
*t
)
4159 WebKitWebHistoryItem
*item
;
4167 webkit_web_view_go_forward(t
->wv
);
4170 /* the back/forwars list is stupid so help selecting a different item */
4171 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4173 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
4174 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
4175 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4182 navaction(struct tab
*t
, struct karg
*args
)
4184 WebKitWebHistoryItem
*item
;
4185 WebKitWebFrame
*frame
;
4187 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
4188 t
->tab_id
, args
->i
);
4190 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4192 if (args
->i
== XT_NAV_BACK
)
4193 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
4195 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
4197 return (XT_CB_PASSTHROUGH
);
4198 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
4200 return (XT_CB_PASSTHROUGH
);
4206 go_back_for_real(t
);
4208 case XT_NAV_FORWARD
:
4210 go_forward_for_real(t
);
4213 frame
= webkit_web_view_get_main_frame(t
->wv
);
4214 webkit_web_frame_reload(frame
);
4217 return (XT_CB_PASSTHROUGH
);
4221 move(struct tab
*t
, struct karg
*args
)
4223 GtkAdjustment
*adjust
;
4224 double pi
, si
, pos
, ps
, upper
, lower
, max
;
4230 case XT_MOVE_BOTTOM
:
4232 case XT_MOVE_PAGEDOWN
:
4233 case XT_MOVE_PAGEUP
:
4234 case XT_MOVE_HALFDOWN
:
4235 case XT_MOVE_HALFUP
:
4236 case XT_MOVE_PERCENT
:
4237 adjust
= t
->adjust_v
;
4240 adjust
= t
->adjust_h
;
4244 pos
= gtk_adjustment_get_value(adjust
);
4245 ps
= gtk_adjustment_get_page_size(adjust
);
4246 upper
= gtk_adjustment_get_upper(adjust
);
4247 lower
= gtk_adjustment_get_lower(adjust
);
4248 si
= gtk_adjustment_get_step_increment(adjust
);
4249 pi
= gtk_adjustment_get_page_increment(adjust
);
4252 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
4253 "max %f si %f pi %f\n",
4254 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
4255 pos
, ps
, upper
, lower
, max
, si
, pi
);
4261 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4266 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4268 case XT_MOVE_BOTTOM
:
4269 case XT_MOVE_FARRIGHT
:
4270 gtk_adjustment_set_value(adjust
, max
);
4273 case XT_MOVE_FARLEFT
:
4274 gtk_adjustment_set_value(adjust
, lower
);
4276 case XT_MOVE_PAGEDOWN
:
4278 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4280 case XT_MOVE_PAGEUP
:
4282 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4284 case XT_MOVE_HALFDOWN
:
4286 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
4288 case XT_MOVE_HALFUP
:
4290 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
4292 case XT_MOVE_PERCENT
:
4293 percent
= atoi(args
->s
) / 100.0;
4294 pos
= max
* percent
;
4295 if (pos
< 0.0 || pos
> max
)
4297 gtk_adjustment_set_value(adjust
, pos
);
4300 return (XT_CB_PASSTHROUGH
);
4303 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
4305 return (XT_CB_HANDLED
);
4309 url_set_visibility(void)
4313 TAILQ_FOREACH(t
, &tabs
, entry
)
4314 if (show_url
== 0) {
4315 gtk_widget_hide(t
->toolbar
);
4318 gtk_widget_show(t
->toolbar
);
4322 notebook_tab_set_visibility(void)
4324 if (show_tabs
== 0) {
4325 gtk_widget_hide(tab_bar
);
4326 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4328 if (tab_style
== XT_TABS_NORMAL
) {
4329 gtk_widget_hide(tab_bar
);
4330 gtk_notebook_set_show_tabs(notebook
, TRUE
);
4331 } else if (tab_style
== XT_TABS_COMPACT
) {
4332 gtk_widget_show(tab_bar
);
4333 gtk_notebook_set_show_tabs(notebook
, FALSE
);
4339 statusbar_set_visibility(void)
4343 TAILQ_FOREACH(t
, &tabs
, entry
)
4344 if (show_statusbar
== 0) {
4345 gtk_widget_hide(t
->statusbar_box
);
4348 gtk_widget_show(t
->statusbar_box
);
4352 url_set(struct tab
*t
, int enable_url_entry
)
4357 show_url
= enable_url_entry
;
4359 if (enable_url_entry
) {
4360 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
4361 GTK_ENTRY_ICON_PRIMARY
, NULL
);
4362 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
4364 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
4365 GTK_ENTRY_ICON_PRIMARY
);
4367 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
4368 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
4369 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
4370 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4376 fullscreen(struct tab
*t
, struct karg
*args
)
4378 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4381 return (XT_CB_PASSTHROUGH
);
4383 if (show_url
== 0) {
4391 url_set_visibility();
4392 notebook_tab_set_visibility();
4394 return (XT_CB_HANDLED
);
4398 statustoggle(struct tab
*t
, struct karg
*args
)
4400 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4402 if (show_statusbar
== 1) {
4404 statusbar_set_visibility();
4405 } else if (show_statusbar
== 0) {
4407 statusbar_set_visibility();
4409 return (XT_CB_HANDLED
);
4413 urlaction(struct tab
*t
, struct karg
*args
)
4415 int rv
= XT_CB_HANDLED
;
4417 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
4420 return (XT_CB_PASSTHROUGH
);
4424 if (show_url
== 0) {
4426 url_set_visibility();
4430 if (show_url
== 1) {
4432 url_set_visibility();
4440 tabaction(struct tab
*t
, struct karg
*args
)
4442 int rv
= XT_CB_HANDLED
;
4443 char *url
= args
->s
;
4447 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4450 return (XT_CB_PASSTHROUGH
);
4454 if (strlen(url
) > 0)
4455 create_new_tab(url
, NULL
, 1, args
->precount
);
4457 create_new_tab(NULL
, NULL
, 1, args
->precount
);
4460 if (args
->precount
< 0)
4463 TAILQ_FOREACH(tt
, &tabs
, entry
)
4464 if (tt
->tab_id
== args
->precount
- 1) {
4469 case XT_TAB_DELQUIT
:
4470 if (gtk_notebook_get_n_pages(notebook
) > 1)
4476 if (strlen(url
) > 0)
4479 rv
= XT_CB_PASSTHROUGH
;
4485 if (show_tabs
== 0) {
4487 notebook_tab_set_visibility();
4491 if (show_tabs
== 1) {
4493 notebook_tab_set_visibility();
4496 case XT_TAB_NEXTSTYLE
:
4497 if (tab_style
== XT_TABS_NORMAL
) {
4498 tab_style
= XT_TABS_COMPACT
;
4499 recolor_compact_tabs();
4502 tab_style
= XT_TABS_NORMAL
;
4503 notebook_tab_set_visibility();
4505 case XT_TAB_UNDO_CLOSE
:
4506 if (undo_count
== 0) {
4507 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4512 u
= TAILQ_FIRST(&undos
);
4513 create_new_tab(u
->uri
, u
, 1, -1);
4515 TAILQ_REMOVE(&undos
, u
, entry
);
4517 /* u->history is freed in create_new_tab() */
4522 rv
= XT_CB_PASSTHROUGH
;
4536 resizetab(struct tab
*t
, struct karg
*args
)
4538 if (t
== NULL
|| args
== NULL
) {
4539 show_oops(NULL
, "resizetab invalid parameters");
4540 return (XT_CB_PASSTHROUGH
);
4543 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4544 t
->tab_id
, args
->i
);
4546 setzoom_webkit(t
, args
->i
);
4548 return (XT_CB_HANDLED
);
4552 movetab(struct tab
*t
, struct karg
*args
)
4556 if (t
== NULL
|| args
== NULL
) {
4557 show_oops(NULL
, "movetab invalid parameters");
4558 return (XT_CB_PASSTHROUGH
);
4561 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4562 t
->tab_id
, args
->i
);
4564 if (args
->i
>= XT_TAB_INVALID
)
4565 return (XT_CB_PASSTHROUGH
);
4567 if (TAILQ_EMPTY(&tabs
))
4568 return (XT_CB_PASSTHROUGH
);
4570 n
= gtk_notebook_get_n_pages(notebook
);
4571 dest
= gtk_notebook_get_current_page(notebook
);
4575 if (args
->precount
< 0)
4576 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4578 dest
= args
->precount
- 1;
4582 if (args
->precount
< 0)
4585 dest
-= args
->precount
% n
;
4598 return (XT_CB_PASSTHROUGH
);
4601 if (dest
< 0 || dest
>= n
)
4602 return (XT_CB_PASSTHROUGH
);
4603 if (t
->tab_id
== dest
) {
4604 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4605 return (XT_CB_HANDLED
);
4608 set_current_tab(dest
);
4610 return (XT_CB_HANDLED
);
4616 command(struct tab
*t
, struct karg
*args
)
4618 char *s
= NULL
, *ss
= NULL
;
4622 if (t
== NULL
|| args
== NULL
) {
4623 show_oops(NULL
, "command invalid parameters");
4624 return (XT_CB_PASSTHROUGH
);
4635 if (cmd_prefix
== 0)
4638 ss
= g_strdup_printf(":%d", cmd_prefix
);
4649 case XT_CMD_OPEN_CURRENT
:
4652 case XT_CMD_TABNEW_CURRENT
:
4653 if (!s
) /* FALL THROUGH? */
4655 if ((uri
= get_uri(t
)) != NULL
) {
4656 ss
= g_strdup_printf("%s%s", s
, uri
);
4661 show_oops(t
, "command: invalid opcode %d", args
->i
);
4662 return (XT_CB_PASSTHROUGH
);
4665 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4667 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4668 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4669 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4671 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4672 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4677 return (XT_CB_HANDLED
);
4681 * Return a new string with a download row (in html)
4682 * appended. Old string is freed.
4685 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4688 WebKitDownloadStatus stat
;
4689 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4691 char cur_sz
[FMT_SCALED_STRSIZE
];
4692 char tot_sz
[FMT_SCALED_STRSIZE
];
4695 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4697 /* All actions wil take this form:
4698 * xxxt://class/seskey
4700 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4701 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4703 stat
= webkit_download_get_status(dl
->download
);
4706 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4707 status_html
= g_strdup_printf("Finished");
4708 cmd_html
= g_strdup_printf(
4709 "<a href='%s%d/%d'>Remove</a>",
4710 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4712 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4713 /* gather size info */
4714 progress
= 100 * webkit_download_get_progress(dl
->download
);
4717 webkit_download_get_current_size(dl
->download
), cur_sz
);
4719 webkit_download_get_total_size(dl
->download
), tot_sz
);
4721 status_html
= g_strdup_printf(
4722 "<div style='width: 100%%' align='center'>"
4723 "<div class='progress-outer'>"
4724 "<div class='progress-inner' style='width: %.2f%%'>"
4725 "</div></div></div>"
4726 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4727 progress
, cur_sz
, tot_sz
, progress
);
4729 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4730 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4734 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4735 status_html
= g_strdup_printf("Cancelled");
4736 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4737 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4739 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4740 status_html
= g_strdup_printf("Error!");
4741 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4742 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4744 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4745 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4746 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4747 status_html
= g_strdup_printf("Starting");
4750 show_oops(t
, "%s: unknown download status", __func__
);
4753 new_html
= g_strdup_printf(
4754 "%s\n<tr><td>%s</td><td>%s</td>"
4755 "<td style='text-align:center'>%s</td></tr>\n",
4756 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4757 status_html
, cmd_html
);
4761 g_free(status_html
);
4772 * update all download tabs apart from one. Pass NULL if
4773 * you want to update all.
4776 update_download_tabs(struct tab
*apart_from
)
4779 if (!updating_dl_tabs
) {
4780 updating_dl_tabs
= 1; /* stop infinite recursion */
4781 TAILQ_FOREACH(t
, &tabs
, entry
)
4782 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4783 && (t
!= apart_from
))
4784 xtp_page_dl(t
, NULL
);
4785 updating_dl_tabs
= 0;
4790 * update all cookie tabs apart from one. Pass NULL if
4791 * you want to update all.
4794 update_cookie_tabs(struct tab
*apart_from
)
4797 if (!updating_cl_tabs
) {
4798 updating_cl_tabs
= 1; /* stop infinite recursion */
4799 TAILQ_FOREACH(t
, &tabs
, entry
)
4800 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4801 && (t
!= apart_from
))
4802 xtp_page_cl(t
, NULL
);
4803 updating_cl_tabs
= 0;
4808 * update all history tabs apart from one. Pass NULL if
4809 * you want to update all.
4812 update_history_tabs(struct tab
*apart_from
)
4816 if (!updating_hl_tabs
) {
4817 updating_hl_tabs
= 1; /* stop infinite recursion */
4818 TAILQ_FOREACH(t
, &tabs
, entry
)
4819 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4820 && (t
!= apart_from
))
4821 xtp_page_hl(t
, NULL
);
4822 updating_hl_tabs
= 0;
4826 /* cookie management XTP page */
4828 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4830 char *body
, *page
, *tmp
;
4831 int i
= 1; /* all ids start 1 */
4832 GSList
*sc
, *pc
, *pc_start
;
4834 char *type
, *table_headers
, *last_domain
;
4836 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4839 show_oops(NULL
, "%s invalid parameters", __func__
);
4843 /* Generate a new session key */
4844 if (!updating_cl_tabs
)
4845 generate_xtp_session_key(&cl_session_key
);
4848 table_headers
= g_strdup_printf("<table><tr>"
4851 "<th style='width:200px'>Value</th>"
4855 "<th>HTTP<br />only</th>"
4856 "<th style='width:40px'>Rm</th></tr>\n");
4858 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4859 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4863 last_domain
= strdup("");
4864 for (; sc
; sc
= sc
->next
) {
4867 if (strcmp(last_domain
, c
->domain
) != 0) {
4870 last_domain
= strdup(c
->domain
);
4874 body
= g_strdup_printf("%s</table>"
4876 body
, c
->domain
, table_headers
);
4880 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4881 c
->domain
, table_headers
);
4886 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4887 if (soup_cookie_equal(pc
->data
, c
)) {
4888 type
= "Session + Persistent";
4893 body
= g_strdup_printf(
4896 "<td style='word-wrap:normal'>%s</td>"
4898 " <textarea rows='4'>%s</textarea>"
4904 "<td style='text-align:center'>"
4905 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4912 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4927 soup_cookies_free(sc
);
4928 soup_cookies_free(pc
);
4930 /* small message if there are none */
4932 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4933 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4936 body
= g_strdup_printf("%s</table>", body
);
4939 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4941 g_free(table_headers
);
4942 g_free(last_domain
);
4944 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4945 update_cookie_tabs(t
);
4953 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4955 char *body
, *page
, *tmp
;
4957 int i
= 1; /* all ids start 1 */
4959 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4962 show_oops(NULL
, "%s invalid parameters", __func__
);
4966 /* Generate a new session key */
4967 if (!updating_hl_tabs
)
4968 generate_xtp_session_key(&hl_session_key
);
4971 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4972 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4974 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4976 body
= g_strdup_printf(
4978 "<td><a href='%s'>%s</a></td>"
4980 "<td style='text-align: center'>"
4981 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4982 body
, h
->uri
, h
->uri
, h
->title
,
4983 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4984 XT_XTP_HL_REMOVE
, i
);
4990 /* small message if there are none */
4993 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4994 "colspan='3'>No History</td></tr>\n", body
);
4999 body
= g_strdup_printf("%s</table>", body
);
5002 page
= get_html_page("History", body
, "", TRUE
);
5006 * update all history manager tabs as the xtp session
5007 * key has now changed. No need to update the current tab.
5008 * Already did that above.
5010 update_history_tabs(t
);
5012 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
5019 * Generate a web page detailing the status of any downloads
5022 xtp_page_dl(struct tab
*t
, struct karg
*args
)
5024 struct download
*dl
;
5025 char *body
, *page
, *tmp
;
5029 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
5032 show_oops(NULL
, "%s invalid parameters", __func__
);
5037 * Generate a new session key for next page instance.
5038 * This only happens for the top level call to xtp_page_dl()
5039 * in which case updating_dl_tabs is 0.
5041 if (!updating_dl_tabs
)
5042 generate_xtp_session_key(&dl_session_key
);
5044 /* header - with refresh so as to update */
5045 if (refresh_interval
>= 1)
5046 ref
= g_strdup_printf(
5047 "<meta http-equiv='refresh' content='%u"
5048 ";url=%s%d/%s/%d' />\n",
5057 body
= g_strdup_printf("<div align='center'>"
5058 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
5059 "</p><table><tr><th style='width: 60%%'>"
5060 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
5061 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
5063 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
5064 body
= xtp_page_dl_row(t
, body
, dl
);
5068 /* message if no downloads in list */
5071 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
5072 " style='text-align: center'>"
5073 "No downloads</td></tr>\n", body
);
5078 body
= g_strdup_printf("%s</table></div>", body
);
5081 page
= get_html_page("Downloads", body
, ref
, 1);
5086 * update all download manager tabs as the xtp session
5087 * key has now changed. No need to update the current tab.
5088 * Already did that above.
5090 update_download_tabs(t
);
5092 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
5099 search(struct tab
*t
, struct karg
*args
)
5103 if (t
== NULL
|| args
== NULL
) {
5104 show_oops(NULL
, "search invalid parameters");
5109 case XT_SEARCH_NEXT
:
5110 d
= t
->search_forward
;
5112 case XT_SEARCH_PREV
:
5113 d
= !t
->search_forward
;
5116 return (XT_CB_PASSTHROUGH
);
5119 if (t
->search_text
== NULL
) {
5120 if (global_search
== NULL
)
5121 return (XT_CB_PASSTHROUGH
);
5123 d
= t
->search_forward
= TRUE
;
5124 t
->search_text
= g_strdup(global_search
);
5125 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
5126 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5130 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
5131 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
5133 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
5135 return (XT_CB_HANDLED
);
5138 struct settings_args
{
5144 print_setting(struct settings
*s
, char *val
, void *cb_args
)
5147 struct settings_args
*sa
= cb_args
;
5152 if (s
->flags
& XT_SF_RUNTIME
)
5158 *sa
->body
= g_strdup_printf(
5160 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
5161 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
5173 set_show(struct tab
*t
, struct karg
*args
)
5175 char *body
, *page
, *tmp
;
5177 struct settings_args sa
;
5179 bzero(&sa
, sizeof sa
);
5183 body
= g_strdup_printf("<div align='center'><table><tr>"
5184 "<th align='left'>Setting</th>"
5185 "<th align='left'>Value</th></tr>\n");
5187 settings_walk(print_setting
, &sa
);
5190 /* small message if there are none */
5193 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
5194 "colspan='2'>No settings</td></tr>\n", body
);
5199 body
= g_strdup_printf("%s</table></div>", body
);
5202 page
= get_html_page("Settings", body
, "", 0);
5206 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
5210 return (XT_CB_PASSTHROUGH
);
5214 set(struct tab
*t
, struct karg
*args
)
5219 if (args
== NULL
|| args
->s
== NULL
)
5220 return (set_show(t
, args
));
5223 p
= g_strstrip(args
->s
);
5226 return (set_show(t
, args
));
5228 /* we got some sort of string */
5229 val
= g_strrstr(p
, "=");
5232 val
= g_strchomp(val
);
5235 for (i
= 0; i
< LENGTH(rs
); i
++) {
5236 if (strcmp(rs
[i
].name
, p
))
5239 if (rs
[i
].activate
) {
5240 if (rs
[i
].activate(val
))
5241 show_oops(t
, "%s invalid value %s",
5244 show_oops(t
, ":set %s = %s", p
, val
);
5247 show_oops(t
, "not a runtime option: %s", p
);
5251 show_oops(t
, "unknown option: %s", p
);
5255 for (i
= 0; i
< LENGTH(rs
); i
++) {
5256 if (strcmp(rs
[i
].name
, p
))
5259 /* XXX this could use some cleanup */
5260 switch (rs
[i
].type
) {
5263 show_oops(t
, "%s = %d",
5264 rs
[i
].name
, *rs
[i
].ival
);
5265 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5266 show_oops(t
, "%s = %s",
5268 rs
[i
].s
->get(&rs
[i
]));
5269 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5270 show_oops(t
, "%s = ...", rs
[i
].name
);
5272 show_oops(t
, "%s = ", rs
[i
].name
);
5276 show_oops(t
, "%s = %f",
5277 rs
[i
].name
, *rs
[i
].fval
);
5278 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5279 show_oops(t
, "%s = %s",
5281 rs
[i
].s
->get(&rs
[i
]));
5282 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5283 show_oops(t
, "%s = ...", rs
[i
].name
);
5285 show_oops(t
, "%s = ", rs
[i
].name
);
5288 if (rs
[i
].sval
&& *rs
[i
].sval
)
5289 show_oops(t
, "%s = %s",
5290 rs
[i
].name
, *rs
[i
].sval
);
5291 else if (rs
[i
].s
&& rs
[i
].s
->get
)
5292 show_oops(t
, "%s = %s",
5294 rs
[i
].s
->get(&rs
[i
]));
5295 else if (rs
[i
].s
&& rs
[i
].s
->get
== NULL
)
5296 show_oops(t
, "%s = ...", rs
[i
].name
);
5298 show_oops(t
, "%s = ", rs
[i
].name
);
5301 show_oops(t
, "unknown type for %s", rs
[i
].name
);
5307 show_oops(t
, "unknown option: %s", p
);
5310 return (XT_CB_PASSTHROUGH
);
5314 session_save(struct tab
*t
, char *filename
)
5320 if (strlen(filename
) == 0)
5323 if (filename
[0] == '.' || filename
[0] == '/')
5327 if (save_tabs(t
, &a
))
5329 strlcpy(named_session
, filename
, sizeof named_session
);
5331 /* add the new session to the list of sessions */
5332 s
= g_malloc(sizeof(struct session
));
5333 s
->name
= g_strdup(filename
);
5334 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
5342 session_open(struct tab
*t
, char *filename
)
5347 if (strlen(filename
) == 0)
5350 if (filename
[0] == '.' || filename
[0] == '/')
5354 a
.i
= XT_SES_CLOSETABS
;
5355 if (open_tabs(t
, &a
))
5358 strlcpy(named_session
, filename
, sizeof named_session
);
5366 session_delete(struct tab
*t
, char *filename
)
5368 char file
[PATH_MAX
];
5372 if (strlen(filename
) == 0)
5375 if (filename
[0] == '.' || filename
[0] == '/')
5378 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
5382 if (!strcmp(filename
, named_session
))
5383 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
5384 sizeof named_session
);
5386 /* remove session from sessions list */
5387 TAILQ_FOREACH(s
, &sessions
, entry
) {
5388 if (!strcmp(s
->name
, filename
))
5393 TAILQ_REMOVE(&sessions
, s
, entry
);
5394 g_free((gpointer
) s
->name
);
5403 session_cmd(struct tab
*t
, struct karg
*args
)
5405 char *filename
= args
->s
;
5410 if (args
->i
& XT_SHOW
)
5411 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
5412 XT_SAVED_TABS_FILE
: named_session
);
5413 else if (args
->i
& XT_SAVE
) {
5414 if (session_save(t
, filename
)) {
5415 show_oops(t
, "Can't save session: %s",
5416 filename
? filename
: "INVALID");
5419 } else if (args
->i
& XT_OPEN
) {
5420 if (session_open(t
, filename
)) {
5421 show_oops(t
, "Can't open session: %s",
5422 filename
? filename
: "INVALID");
5425 } else if (args
->i
& XT_DELETE
) {
5426 if (session_delete(t
, filename
)) {
5427 show_oops(t
, "Can't delete session: %s",
5428 filename
? filename
: "INVALID");
5433 return (XT_CB_PASSTHROUGH
);
5437 * Make a hardcopy of the page
5440 print_page(struct tab
*t
, struct karg
*args
)
5442 WebKitWebFrame
*frame
;
5444 GtkPrintOperation
*op
;
5445 GtkPrintOperationAction action
;
5446 GtkPrintOperationResult print_res
;
5447 GError
*g_err
= NULL
;
5448 int marg_l
, marg_r
, marg_t
, marg_b
;
5450 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
5452 ps
= gtk_page_setup_new();
5453 op
= gtk_print_operation_new();
5454 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
5455 frame
= webkit_web_view_get_main_frame(t
->wv
);
5457 /* the default margins are too small, so we will bump them */
5458 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
5459 XT_PRINT_EXTRA_MARGIN
;
5460 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
5461 XT_PRINT_EXTRA_MARGIN
;
5462 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
5463 XT_PRINT_EXTRA_MARGIN
;
5464 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
5465 XT_PRINT_EXTRA_MARGIN
;
5468 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
5469 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
5470 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
5471 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
5473 gtk_print_operation_set_default_page_setup(op
, ps
);
5475 /* this appears to free 'op' and 'ps' */
5476 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
5478 /* check it worked */
5479 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
5480 show_oops(NULL
, "can't print: %s", g_err
->message
);
5481 g_error_free (g_err
);
5489 go_home(struct tab
*t
, struct karg
*args
)
5496 set_encoding(struct tab
*t
, struct karg
*args
)
5500 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
5501 e
= webkit_web_view_get_custom_encoding(t
->wv
);
5503 e
= webkit_web_view_get_encoding(t
->wv
);
5504 show_oops(t
, "encoding: %s", e
? e
: "N/A");
5506 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
5512 restart(struct tab
*t
, struct karg
*args
)
5516 a
.s
= XT_RESTART_TABS_FILE
;
5518 execvp(start_argv
[0], start_argv
);
5524 #define CTRL GDK_CONTROL_MASK
5525 #define MOD1 GDK_MOD1_MASK
5526 #define SHFT GDK_SHIFT_MASK
5528 /* inherent to GTK not all keys will be caught at all times */
5529 /* XXX sort key bindings */
5530 struct key_binding
{
5535 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
5537 { "cookiejar", MOD1
, 0, GDK_j
},
5538 { "downloadmgr", MOD1
, 0, GDK_d
},
5539 { "history", MOD1
, 0, GDK_h
},
5540 { "print", CTRL
, 0, GDK_p
},
5541 { "search", 0, 0, GDK_slash
},
5542 { "searchb", 0, 0, GDK_question
},
5543 { "statustoggle", CTRL
, 0, GDK_n
},
5544 { "command", 0, 0, GDK_colon
},
5545 { "qa", CTRL
, 0, GDK_q
},
5546 { "restart", MOD1
, 0, GDK_q
},
5547 { "js toggle", CTRL
, 0, GDK_j
},
5548 { "cookie toggle", MOD1
, 0, GDK_c
},
5549 { "togglesrc", CTRL
, 0, GDK_s
},
5550 { "yankuri", 0, 0, GDK_y
},
5551 { "pasteuricur", 0, 0, GDK_p
},
5552 { "pasteurinew", 0, 0, GDK_P
},
5553 { "toplevel toggle", 0, 0, GDK_F4
},
5554 { "help", 0, 0, GDK_F1
},
5555 { "run_script", MOD1
, 0, GDK_r
},
5558 { "searchnext", 0, 0, GDK_n
},
5559 { "searchprevious", 0, 0, GDK_N
},
5562 { "focusaddress", 0, 0, GDK_F6
},
5563 { "focussearch", 0, 0, GDK_F7
},
5566 { "hinting", 0, 0, GDK_f
},
5568 /* custom stylesheet */
5569 { "userstyle", 0, 0, GDK_i
},
5572 { "goback", 0, 0, GDK_BackSpace
},
5573 { "goback", MOD1
, 0, GDK_Left
},
5574 { "goforward", SHFT
, 0, GDK_BackSpace
},
5575 { "goforward", MOD1
, 0, GDK_Right
},
5576 { "reload", 0, 0, GDK_F5
},
5577 { "reload", CTRL
, 0, GDK_r
},
5578 { "reload", CTRL
, 0, GDK_l
},
5579 { "favorites", MOD1
, 1, GDK_f
},
5581 /* vertical movement */
5582 { "scrolldown", 0, 0, GDK_j
},
5583 { "scrolldown", 0, 0, GDK_Down
},
5584 { "scrollup", 0, 0, GDK_Up
},
5585 { "scrollup", 0, 0, GDK_k
},
5586 { "scrollbottom", 0, 0, GDK_G
},
5587 { "scrollbottom", 0, 0, GDK_End
},
5588 { "scrolltop", 0, 0, GDK_Home
},
5589 { "scrollpagedown", 0, 0, GDK_space
},
5590 { "scrollpagedown", CTRL
, 0, GDK_f
},
5591 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5592 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5593 { "scrollpageup", 0, 0, GDK_Page_Up
},
5594 { "scrollpageup", CTRL
, 0, GDK_b
},
5595 { "scrollhalfup", CTRL
, 0, GDK_u
},
5596 /* horizontal movement */
5597 { "scrollright", 0, 0, GDK_l
},
5598 { "scrollright", 0, 0, GDK_Right
},
5599 { "scrollleft", 0, 0, GDK_Left
},
5600 { "scrollleft", 0, 0, GDK_h
},
5601 { "scrollfarright", 0, 0, GDK_dollar
},
5602 { "scrollfarleft", 0, 0, GDK_0
},
5605 { "tabnew", CTRL
, 0, GDK_t
},
5606 { "999tabnew", CTRL
, 0, GDK_T
},
5607 { "tabclose", CTRL
, 1, GDK_w
},
5608 { "tabundoclose", 0, 0, GDK_U
},
5609 { "tabnext 1", CTRL
, 0, GDK_1
},
5610 { "tabnext 2", CTRL
, 0, GDK_2
},
5611 { "tabnext 3", CTRL
, 0, GDK_3
},
5612 { "tabnext 4", CTRL
, 0, GDK_4
},
5613 { "tabnext 5", CTRL
, 0, GDK_5
},
5614 { "tabnext 6", CTRL
, 0, GDK_6
},
5615 { "tabnext 7", CTRL
, 0, GDK_7
},
5616 { "tabnext 8", CTRL
, 0, GDK_8
},
5617 { "tabnext 9", CTRL
, 0, GDK_9
},
5618 { "tabfirst", CTRL
, 0, GDK_less
},
5619 { "tablast", CTRL
, 0, GDK_greater
},
5620 { "tabprevious", CTRL
, 0, GDK_Left
},
5621 { "tabnext", CTRL
, 0, GDK_Right
},
5622 { "focusout", CTRL
, 0, GDK_minus
},
5623 { "focusin", CTRL
, 0, GDK_plus
},
5624 { "focusin", CTRL
, 0, GDK_equal
},
5625 { "focusreset", CTRL
, 0, GDK_0
},
5627 /* command aliases (handy when -S flag is used) */
5628 { "promptopen", 0, 0, GDK_F9
},
5629 { "promptopencurrent", 0, 0, GDK_F10
},
5630 { "prompttabnew", 0, 0, GDK_F11
},
5631 { "prompttabnewcurrent",0, 0, GDK_F12
},
5633 TAILQ_HEAD(keybinding_list
, key_binding
);
5636 walk_kb(struct settings
*s
,
5637 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5639 struct key_binding
*k
;
5642 if (s
== NULL
|| cb
== NULL
) {
5643 show_oops(NULL
, "walk_kb invalid parameters");
5647 TAILQ_FOREACH(k
, &kbl
, entry
) {
5653 if (gdk_keyval_name(k
->key
) == NULL
)
5656 strlcat(str
, k
->cmd
, sizeof str
);
5657 strlcat(str
, ",", sizeof str
);
5659 if (k
->mask
& GDK_SHIFT_MASK
)
5660 strlcat(str
, "S-", sizeof str
);
5661 if (k
->mask
& GDK_CONTROL_MASK
)
5662 strlcat(str
, "C-", sizeof str
);
5663 if (k
->mask
& GDK_MOD1_MASK
)
5664 strlcat(str
, "M1-", sizeof str
);
5665 if (k
->mask
& GDK_MOD2_MASK
)
5666 strlcat(str
, "M2-", sizeof str
);
5667 if (k
->mask
& GDK_MOD3_MASK
)
5668 strlcat(str
, "M3-", sizeof str
);
5669 if (k
->mask
& GDK_MOD4_MASK
)
5670 strlcat(str
, "M4-", sizeof str
);
5671 if (k
->mask
& GDK_MOD5_MASK
)
5672 strlcat(str
, "M5-", sizeof str
);
5674 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5675 cb(s
, str
, cb_args
);
5680 init_keybindings(void)
5683 struct key_binding
*k
;
5685 for (i
= 0; i
< LENGTH(keys
); i
++) {
5686 k
= g_malloc0(sizeof *k
);
5687 k
->cmd
= keys
[i
].cmd
;
5688 k
->mask
= keys
[i
].mask
;
5689 k
->use_in_entry
= keys
[i
].use_in_entry
;
5690 k
->key
= keys
[i
].key
;
5691 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5693 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5694 k
->cmd
? k
->cmd
: "unnamed key");
5699 keybinding_clearall(void)
5701 struct key_binding
*k
, *next
;
5703 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5704 next
= TAILQ_NEXT(k
, entry
);
5708 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5709 k
->cmd
? k
->cmd
: "unnamed key");
5710 TAILQ_REMOVE(&kbl
, k
, entry
);
5716 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5718 struct key_binding
*k
;
5719 guint keyval
, mask
= 0;
5722 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5724 /* Keys which are to be used in entry have been prefixed with an
5725 * exclamation mark. */
5729 /* find modifier keys */
5730 if (strstr(key
, "S-"))
5731 mask
|= GDK_SHIFT_MASK
;
5732 if (strstr(key
, "C-"))
5733 mask
|= GDK_CONTROL_MASK
;
5734 if (strstr(key
, "M1-"))
5735 mask
|= GDK_MOD1_MASK
;
5736 if (strstr(key
, "M2-"))
5737 mask
|= GDK_MOD2_MASK
;
5738 if (strstr(key
, "M3-"))
5739 mask
|= GDK_MOD3_MASK
;
5740 if (strstr(key
, "M4-"))
5741 mask
|= GDK_MOD4_MASK
;
5742 if (strstr(key
, "M5-"))
5743 mask
|= GDK_MOD5_MASK
;
5746 for (i
= strlen(key
) - 1; i
> 0; i
--)
5750 /* validate keyname */
5751 keyval
= gdk_keyval_from_name(key
);
5752 if (keyval
== GDK_VoidSymbol
) {
5753 warnx("invalid keybinding name %s", key
);
5756 /* must run this test too, gtk+ doesn't handle 10 for example */
5757 if (gdk_keyval_name(keyval
) == NULL
) {
5758 warnx("invalid keybinding name %s", key
);
5762 /* Remove eventual dupes. */
5763 TAILQ_FOREACH(k
, &kbl
, entry
)
5764 if (k
->key
== keyval
&& k
->mask
== mask
) {
5765 TAILQ_REMOVE(&kbl
, k
, entry
);
5771 k
= g_malloc0(sizeof *k
);
5772 k
->cmd
= g_strdup(cmd
);
5774 k
->use_in_entry
= use_in_entry
;
5777 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5782 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5783 k
->cmd
, gdk_keyval_name(keyval
));
5785 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5791 add_kb(struct settings
*s
, char *entry
)
5795 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5797 /* clearall is special */
5798 if (!strcmp(entry
, "clearall")) {
5799 keybinding_clearall();
5803 kb
= strstr(entry
, ",");
5809 return (keybinding_add(entry
, key
, key
[0] == '!'));
5815 int (*func
)(struct tab
*, struct karg
*);
5819 { "command", 0, command
, ':', 0 },
5820 { "search", 0, command
, '/', 0 },
5821 { "searchb", 0, command
, '?', 0 },
5822 { "togglesrc", 0, toggle_src
, 0, 0 },
5824 /* yanking and pasting */
5825 { "yankuri", 0, yank_uri
, 0, 0 },
5826 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5827 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5828 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5831 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5832 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5835 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5836 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5839 { "hinting", 0, hint
, 0, 0 },
5841 /* custom stylesheet */
5842 { "userstyle", 0, userstyle
, 0, 0 },
5845 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5846 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5847 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5849 /* vertical movement */
5850 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5851 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5852 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5853 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5854 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5855 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5856 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5857 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5858 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5859 /* horizontal movement */
5860 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5861 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5862 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5863 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5865 { "favorites", 0, xtp_page_fl
, 0, 0 },
5866 { "fav", 0, xtp_page_fl
, 0, 0 },
5867 { "favadd", 0, add_favorite
, 0, 0 },
5869 { "qall", 0, quit
, 0, 0 },
5870 { "quitall", 0, quit
, 0, 0 },
5871 { "w", 0, save_tabs
, 0, 0 },
5872 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5873 { "help", 0, help
, 0, 0 },
5874 { "about", 0, about
, 0, 0 },
5875 { "stats", 0, stats
, 0, 0 },
5876 { "version", 0, about
, 0, 0 },
5879 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5880 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5881 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5882 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5883 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5884 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5885 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5886 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5887 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5888 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5889 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5891 /* cookie command */
5892 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5893 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5894 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5895 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5896 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5897 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5898 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5899 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5900 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5901 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5902 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5904 /* toplevel (domain) command */
5905 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5906 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5909 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5912 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5913 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5914 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5916 { "ca", 0, ca_cmd
, 0, 0 },
5917 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5918 { "dl", 0, xtp_page_dl
, 0, 0 },
5919 { "h", 0, xtp_page_hl
, 0, 0 },
5920 { "history", 0, xtp_page_hl
, 0, 0 },
5921 { "home", 0, go_home
, 0, 0 },
5922 { "restart", 0, restart
, 0, 0 },
5923 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5924 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5925 { "statustoggle", 0, statustoggle
, 0, 0 },
5926 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5928 { "print", 0, print_page
, 0, 0 },
5931 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5932 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5933 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5934 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5935 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5936 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5937 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5938 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5939 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5940 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5941 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5942 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5943 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5944 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5945 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5946 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5947 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5948 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5949 { "buffers", 0, buffers
, 0, 0 },
5950 { "ls", 0, buffers
, 0, 0 },
5951 { "tabs", 0, buffers
, 0, 0 },
5952 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
5954 /* command aliases (handy when -S flag is used) */
5955 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5956 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5957 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5958 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5961 { "set", 0, set
, 0, XT_SETARG
},
5963 { "fullscreen", 0, fullscreen
, 0, 0 },
5964 { "f", 0, fullscreen
, 0, 0 },
5967 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5968 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
5969 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
5970 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5971 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5978 } cmd_status
= {-1, 0};
5981 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5984 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5991 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5998 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6000 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
6006 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
6008 a
.i
= XT_NAV_FORWARD
;
6018 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
6020 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
6022 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
6029 * cancel, remove, etc. downloads
6032 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
6034 struct download find
, *d
= NULL
;
6036 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
6038 /* some commands require a valid download id */
6039 if (cmd
!= XT_XTP_DL_LIST
) {
6040 /* lookup download in question */
6042 d
= RB_FIND(download_list
, &downloads
, &find
);
6045 show_oops(t
, "%s: no such download", __func__
);
6050 /* decide what to do */
6052 case XT_XTP_DL_CANCEL
:
6053 webkit_download_cancel(d
->download
);
6055 case XT_XTP_DL_REMOVE
:
6056 webkit_download_cancel(d
->download
); /* just incase */
6057 g_object_unref(d
->download
);
6058 RB_REMOVE(download_list
, &downloads
, d
);
6060 case XT_XTP_DL_LIST
:
6064 show_oops(t
, "%s: unknown command", __func__
);
6067 xtp_page_dl(t
, NULL
);
6071 * Actions on history, only does one thing for now, but
6072 * we provide the function for future actions
6075 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
6077 struct history
*h
, *next
;
6081 case XT_XTP_HL_REMOVE
:
6082 /* walk backwards, as listed in reverse */
6083 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
6084 next
= RB_PREV(history_list
, &hl
, h
);
6086 RB_REMOVE(history_list
, &hl
, h
);
6087 g_free((gpointer
) h
->title
);
6088 g_free((gpointer
) h
->uri
);
6095 case XT_XTP_HL_LIST
:
6096 /* Nothing - just xtp_page_hl() below */
6099 show_oops(t
, "%s: unknown command", __func__
);
6103 xtp_page_hl(t
, NULL
);
6106 /* remove a favorite */
6108 remove_favorite(struct tab
*t
, int index
)
6110 char file
[PATH_MAX
], *title
, *uri
= NULL
;
6111 char *new_favs
, *tmp
;
6116 /* open favorites */
6117 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6119 if ((f
= fopen(file
, "r")) == NULL
) {
6120 show_oops(t
, "%s: can't open favorites: %s",
6121 __func__
, strerror(errno
));
6125 /* build a string which will become the new favroites file */
6126 new_favs
= g_strdup("");
6129 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
6130 if (feof(f
) || ferror(f
))
6132 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
6139 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
6140 if (feof(f
) || ferror(f
)) {
6141 show_oops(t
, "%s: can't parse favorites %s",
6142 __func__
, strerror(errno
));
6147 /* as long as this isn't the one we are deleting add to file */
6150 new_favs
= g_strdup_printf("%s%s\n%s\n",
6151 new_favs
, title
, uri
);
6163 /* write back new favorites file */
6164 if ((f
= fopen(file
, "w")) == NULL
) {
6165 show_oops(t
, "%s: can't open favorites: %s",
6166 __func__
, strerror(errno
));
6170 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
6171 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
6184 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
6187 case XT_XTP_FL_LIST
:
6188 /* nothing, just the below call to xtp_page_fl() */
6190 case XT_XTP_FL_REMOVE
:
6191 remove_favorite(t
, arg
);
6194 show_oops(t
, "%s: invalid favorites command", __func__
);
6198 xtp_page_fl(t
, NULL
);
6202 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
6205 case XT_XTP_CL_LIST
:
6206 /* nothing, just xtp_page_cl() */
6208 case XT_XTP_CL_REMOVE
:
6212 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
6216 xtp_page_cl(t
, NULL
);
6219 /* link an XTP class to it's session key and handler function */
6220 struct xtp_despatch
{
6223 void (*handle_func
)(struct tab
*, uint8_t, int);
6226 struct xtp_despatch xtp_despatches
[] = {
6227 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
6228 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
6229 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
6230 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
6231 { XT_XTP_INVALID
, NULL
, NULL
}
6235 * is the url xtp protocol? (xxxt://)
6236 * if so, parse and despatch correct bahvior
6239 parse_xtp_url(struct tab
*t
, const char *url
)
6241 char *dup
= NULL
, *p
, *last
= NULL
;
6242 uint8_t n_tokens
= 0;
6243 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
6244 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
6249 * tokens array meaning:
6251 * tokens[1] = session key
6252 * tokens[2] = action
6253 * tokens[3] = optional argument
6256 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
6258 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
6261 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
6263 /* split out the url */
6264 for ((p
= strtok_r(dup
, "/", &last
)); p
;
6265 (p
= strtok_r(NULL
, "/", &last
))) {
6267 tokens
[n_tokens
++] = p
;
6270 /* should be atleast three fields 'class/seskey/command/arg' */
6274 dsp
= xtp_despatches
;
6275 req_class
= atoi(tokens
[0]);
6276 while (dsp
->xtp_class
) {
6277 if (dsp
->xtp_class
== req_class
) {
6284 /* did we find one atall? */
6285 if (dsp_match
== NULL
) {
6286 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
6290 /* check session key and call despatch function */
6291 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
6292 ret
= TRUE
; /* all is well, this was a valid xtp request */
6293 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
6306 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6308 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
6310 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
6313 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
6318 show_oops(t
, "activate_uri_entry_cb no uri");
6322 uri
+= strspn(uri
, "\t ");
6324 /* if xxxt:// treat specially */
6325 if (parse_xtp_url(t
, uri
))
6328 /* otherwise continue to load page normally */
6329 load_uri(t
, (gchar
*)uri
);
6334 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
6336 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
6337 char *newuri
= NULL
;
6340 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
6343 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
6347 if (search_string
== NULL
) {
6348 show_oops(t
, "no search_string");
6352 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6354 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
6355 newuri
= g_strdup_printf(search_string
, enc_search
);
6359 webkit_web_view_load_uri(t
->wv
, newuri
);
6367 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
6369 struct domain
*d
= NULL
;
6372 if (uri
== NULL
|| t
== NULL
)
6375 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
6380 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
6381 es
? "enable" : "disable", uri
);
6383 g_object_set(G_OBJECT(t
->settings
),
6384 "enable-html5-local-storage", es
, (char *)NULL
);
6385 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6389 check_and_set_js(const gchar
*uri
, struct tab
*t
)
6391 struct domain
*d
= NULL
;
6394 if (uri
== NULL
|| t
== NULL
)
6397 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6402 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
6403 es
? "enable" : "disable", uri
);
6405 g_object_set(G_OBJECT(t
->settings
),
6406 "enable-scripts", es
, (char *)NULL
);
6407 g_object_set(G_OBJECT(t
->settings
),
6408 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
6409 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6411 button_set_stockid(t
->js_toggle
,
6412 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
6416 color_address_bar(gpointer p
)
6419 struct tab
*tt
, *t
= p
;
6420 gchar
*col_str
= XT_COLOR_WHITE
;
6421 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
6423 gdk_threads_enter();
6424 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
6426 /* make sure t still exists */
6429 TAILQ_FOREACH(tt
, &tabs
, entry
)
6435 if ((uri
= get_uri(t
)) == NULL
)
6439 gdk_threads_leave();
6441 col_str
= XT_COLOR_YELLOW
;
6442 switch (load_compare_cert(u
, &error_str
)) {
6444 col_str
= XT_COLOR_BLUE
;
6447 col_str
= XT_COLOR_GREEN
;
6449 case CERT_UNTRUSTED
:
6450 col_str
= XT_COLOR_YELLOW
;
6453 col_str
= XT_COLOR_RED
;
6457 gdk_threads_enter();
6459 /* make sure t isn't deleted */
6460 TAILQ_FOREACH(tt
, &tabs
, entry
)
6466 /* test to see if the user navigated away and canceled the thread */
6467 if (t
->thread
!= g_thread_self())
6470 gdk_color_parse(col_str
, &color
);
6471 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6473 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6474 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6476 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6478 if (error_str
&& error_str
[0] != '\0')
6479 show_oops(t
, "%s", error_str
);
6483 /* t is invalid at this point */
6485 g_free((gpointer
)u
);
6486 gdk_threads_leave();
6490 show_ca_status(struct tab
*t
, const char *uri
)
6493 gchar
*col_str
= XT_COLOR_WHITE
;
6495 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
6496 ssl_strict_certs
, ssl_ca_file
, uri
);
6503 if (ssl_ca_file
== NULL
) {
6504 if (g_str_has_prefix(uri
, "http://"))
6506 if (g_str_has_prefix(uri
, "https://")) {
6507 col_str
= XT_COLOR_RED
;
6512 if (g_str_has_prefix(uri
, "http://") ||
6513 !g_str_has_prefix(uri
, "https://"))
6517 * It is not necessary to see if the thread is already running.
6518 * If the thread is in progress setting it to something else aborts it
6522 /* thread the coloring of the address bar */
6523 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
6529 gdk_color_parse(col_str
, &color
);
6530 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6532 if (!strcmp(col_str
, XT_COLOR_WHITE
))
6533 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
6535 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
6540 free_favicon(struct tab
*t
)
6542 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
6543 __func__
, t
->icon_download
, t
->icon_request
);
6545 if (t
->icon_request
)
6546 g_object_unref(t
->icon_request
);
6547 if (t
->icon_dest_uri
)
6548 g_free(t
->icon_dest_uri
);
6550 t
->icon_request
= NULL
;
6551 t
->icon_dest_uri
= NULL
;
6555 xt_icon_from_name(struct tab
*t
, gchar
*name
)
6557 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6558 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6560 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6561 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6563 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6564 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6568 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
6570 GdkPixbuf
*pb_scaled
;
6572 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
6573 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
6574 GDK_INTERP_BILINEAR
);
6578 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
6579 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6581 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
6582 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
6584 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
6585 GTK_ENTRY_ICON_PRIMARY
, NULL
);
6587 if (pb_scaled
!= pb
)
6588 g_object_unref(pb_scaled
);
6592 xt_icon_from_file(struct tab
*t
, char *file
)
6596 if (g_str_has_prefix(file
, "file://"))
6597 file
+= strlen("file://");
6599 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6601 xt_icon_from_pixbuf(t
, pb
);
6604 xt_icon_from_name(t
, "text-html");
6608 is_valid_icon(char *file
)
6611 const char *mime_type
;
6615 gf
= g_file_new_for_path(file
);
6616 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6618 mime_type
= g_file_info_get_content_type(fi
);
6619 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
6620 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
6621 g_strcmp0(mime_type
, "image/png") == 0 ||
6622 g_strcmp0(mime_type
, "image/gif") == 0 ||
6623 g_strcmp0(mime_type
, "application/octet-stream") == 0;
6631 set_favicon_from_file(struct tab
*t
, char *file
)
6635 if (t
== NULL
|| file
== NULL
)
6638 if (g_str_has_prefix(file
, "file://"))
6639 file
+= strlen("file://");
6640 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
6642 if (!stat(file
, &sb
)) {
6643 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
6644 /* corrupt icon so trash it */
6645 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6648 /* no need to set icon to default here */
6652 xt_icon_from_file(t
, file
);
6656 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6659 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6660 struct tab
*tt
= NULL
, *t
= NULL
;
6663 * find the webview instead of passing in the tab as it could have been
6664 * deleted from underneath us.
6666 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6675 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6676 __func__
, t
->tab_id
, status
);
6679 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6681 t
->icon_download
= NULL
;
6684 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6687 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6690 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6692 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6693 __func__
, t
->tab_id
);
6694 t
->icon_download
= NULL
;
6697 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6700 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6701 __func__
, t
->icon_dest_uri
);
6702 set_favicon_from_file(t
, t
->icon_dest_uri
);
6703 /* these will be freed post callback */
6704 t
->icon_request
= NULL
;
6705 t
->icon_download
= NULL
;
6713 abort_favicon_download(struct tab
*t
)
6715 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6717 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6718 if (t
->icon_download
) {
6719 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6720 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6721 webkit_download_cancel(t
->icon_download
);
6722 t
->icon_download
= NULL
;
6727 xt_icon_from_name(t
, "text-html");
6731 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6733 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6735 if (uri
== NULL
|| t
== NULL
)
6738 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6739 /* take icon from WebKitIconDatabase */
6742 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6744 xt_icon_from_pixbuf(t
, pb
);
6747 xt_icon_from_name(t
, "text-html");
6748 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6749 /* download icon to cache dir */
6750 gchar
*name_hash
, file
[PATH_MAX
];
6753 if (t
->icon_request
) {
6754 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6758 /* check to see if we got the icon in cache */
6759 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6760 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6763 if (!stat(file
, &sb
)) {
6764 if (sb
.st_size
> 0) {
6765 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6767 set_favicon_from_file(t
, file
);
6771 /* corrupt icon so trash it */
6772 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6777 /* create download for icon */
6778 t
->icon_request
= webkit_network_request_new(uri
);
6779 if (t
->icon_request
== NULL
) {
6780 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6785 t
->icon_download
= webkit_download_new(t
->icon_request
);
6786 if (t
->icon_download
== NULL
)
6789 /* we have to free icon_dest_uri later */
6790 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6791 webkit_download_set_destination_uri(t
->icon_download
,
6794 if (webkit_download_get_status(t
->icon_download
) ==
6795 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6796 g_object_unref(t
->icon_request
);
6797 g_free(t
->icon_dest_uri
);
6798 t
->icon_request
= NULL
;
6799 t
->icon_dest_uri
= NULL
;
6803 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6804 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6806 webkit_download_start(t
->icon_download
);
6811 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6813 const gchar
*uri
= NULL
, *title
= NULL
;
6814 struct history
*h
, find
;
6818 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6819 webkit_web_view_get_load_status(wview
),
6820 get_uri(t
) ? get_uri(t
) : "NOTHING");
6823 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6827 switch (webkit_web_view_get_load_status(wview
)) {
6828 case WEBKIT_LOAD_PROVISIONAL
:
6830 abort_favicon_download(t
);
6831 #if GTK_CHECK_VERSION(2, 20, 0)
6832 gtk_widget_show(t
->spinner
);
6833 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6835 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6837 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6839 /* assume we are a new address */
6840 gdk_color_parse("white", &color
);
6841 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
6842 statusbar_modify_attr(t
, "white", XT_COLOR_BLACK
);
6844 /* take focus if we are visible */
6848 /* kill color thread */
6853 case WEBKIT_LOAD_COMMITTED
:
6858 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6864 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6866 /* check if js white listing is enabled */
6867 if (enable_cookie_whitelist
)
6868 check_and_set_cookie(uri
, t
);
6869 if (enable_js_whitelist
)
6870 check_and_set_js(uri
, t
);
6876 /* we know enough to autosave the session */
6877 if (session_autosave
) {
6882 show_ca_status(t
, uri
);
6885 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6889 case WEBKIT_LOAD_FINISHED
:
6895 if (!strncmp(uri
, "http://", strlen("http://")) ||
6896 !strncmp(uri
, "https://", strlen("https://")) ||
6897 !strncmp(uri
, "file://", strlen("file://"))) {
6899 h
= RB_FIND(history_list
, &hl
, &find
);
6901 title
= get_title(t
, FALSE
);
6902 h
= g_malloc(sizeof *h
);
6903 h
->uri
= g_strdup(uri
);
6904 h
->title
= g_strdup(title
);
6905 RB_INSERT(history_list
, &hl
, h
);
6906 completion_add_uri(h
->uri
);
6907 update_history_tabs(NULL
);
6911 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6912 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6913 case WEBKIT_LOAD_FAILED
:
6916 #if GTK_CHECK_VERSION(2, 20, 0)
6917 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6918 gtk_widget_hide(t
->spinner
);
6921 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6926 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6928 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6929 can_go_back_for_real(t
));
6931 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6932 can_go_forward_for_real(t
));
6936 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6938 const gchar
*title
= NULL
, *win_title
= NULL
;
6940 title
= get_title(t
, FALSE
);
6941 win_title
= get_title(t
, TRUE
);
6942 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6943 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6944 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6945 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6949 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6951 run_script(t
, JS_HINTING
);
6955 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6957 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6958 progress
== 100 ? 0 : (double)progress
/ 100);
6959 if (show_url
== 0) {
6960 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6961 progress
== 100 ? 0 : (double)progress
/ 100);
6964 update_statusbar_position(NULL
, NULL
);
6968 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6969 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6970 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6973 WebKitWebNavigationReason reason
;
6974 struct domain
*d
= NULL
;
6977 show_oops(NULL
, "webview_npd_cb invalid parameters");
6981 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6983 webkit_network_request_get_uri(request
));
6985 uri
= (char *)webkit_network_request_get_uri(request
);
6987 /* if this is an xtp url, we don't load anything else */
6988 if (parse_xtp_url(t
, uri
))
6991 if (t
->ctrl_click
) {
6993 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6994 webkit_web_policy_decision_ignore(pd
);
6995 return (TRUE
); /* we made the decission */
6999 * This is a little hairy but it comes down to this:
7000 * when we run in whitelist mode we have to assist the browser in
7001 * opening the URL that it would have opened in a new tab.
7003 reason
= webkit_web_navigation_action_get_reason(na
);
7004 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
7005 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7006 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
7007 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7009 webkit_web_policy_decision_use(pd
);
7010 return (TRUE
); /* we made the decision */
7017 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
7020 struct domain
*d
= NULL
;
7022 WebKitWebView
*webview
= NULL
;
7024 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
7025 webkit_web_view_get_uri(wv
));
7028 /* open in current tab */
7030 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7031 uri
= webkit_web_view_get_uri(wv
);
7032 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7035 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7037 } else if (enable_scripts
== 1) {
7038 tt
= create_new_tab(NULL
, NULL
, 1, -1);
7046 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
7049 struct domain
*d
= NULL
;
7051 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
7053 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
7054 uri
= webkit_web_view_get_uri(wv
);
7055 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
7059 } else if (enable_scripts
== 1)
7066 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
7068 /* we can not eat the event without throwing gtk off so defer it */
7070 /* catch middle click */
7071 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
7076 /* catch ctrl click */
7077 if (e
->type
== GDK_BUTTON_RELEASE
&&
7078 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
7083 return (XT_CB_PASSTHROUGH
);
7087 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
7089 struct mime_type
*m
;
7091 m
= find_mime_type(mime_type
);
7099 show_oops(t
, "can't fork mime handler");
7109 execlp(m
->mt_action
, m
->mt_action
,
7110 webkit_network_request_get_uri(request
), (void *)NULL
);
7119 get_mime_type(const char *file
)
7122 char *mime_type
= NULL
;
7126 if (g_str_has_prefix(file
, "file://"))
7127 file
+= strlen("file://");
7129 gf
= g_file_new_for_path(file
);
7130 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
7132 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
7133 mime_type
= g_strdup(m
);
7141 run_download_mimehandler(char *mime_type
, char *file
)
7143 struct mime_type
*m
;
7145 m
= find_mime_type(mime_type
);
7151 show_oops(NULL
, "can't fork download mime handler");
7161 if (g_str_has_prefix(file
, "file://"))
7162 file
+= strlen("file://");
7163 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
7172 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
7175 WebKitDownloadStatus status
;
7176 const char *file
= NULL
;
7179 if (download
== NULL
)
7181 status
= webkit_download_get_status(download
);
7182 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
7185 file
= webkit_download_get_destination_uri(download
);
7188 mime
= get_mime_type(file
);
7192 run_download_mimehandler((char *)mime
, (char *)file
);
7197 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
7198 WebKitNetworkRequest
*request
, char *mime_type
,
7199 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
7202 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
7206 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
7207 t
->tab_id
, mime_type
);
7209 if (run_mimehandler(t
, mime_type
, request
) == 0) {
7210 webkit_web_policy_decision_ignore(decision
);
7215 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
7216 webkit_web_policy_decision_download(decision
);
7224 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
7228 const gchar
*suggested_name
;
7229 gchar
*filename
= NULL
;
7231 struct download
*download_entry
;
7234 if (wk_download
== NULL
|| t
== NULL
) {
7235 show_oops(NULL
, "%s invalid parameters", __func__
);
7239 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
7240 if (suggested_name
== NULL
)
7241 return (FALSE
); /* abort download */
7252 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
7254 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
7255 filename
: suggested_name
);
7257 } while (!stat(uri
+ strlen("file://"), &sb
));
7259 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
7260 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
7262 webkit_download_set_destination_uri(wk_download
, uri
);
7264 if (webkit_download_get_status(wk_download
) ==
7265 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
7266 show_oops(t
, "%s: download failed to start", __func__
);
7268 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
7270 /* connect "download first" mime handler */
7271 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
7272 G_CALLBACK(download_status_changed_cb
), NULL
);
7274 download_entry
= g_malloc(sizeof(struct download
));
7275 download_entry
->download
= wk_download
;
7276 download_entry
->tab
= t
;
7277 download_entry
->id
= next_download_id
++;
7278 RB_INSERT(download_list
, &downloads
, download_entry
);
7279 /* get from history */
7280 g_object_ref(wk_download
);
7281 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
7282 show_oops(t
, "Download of '%s' started...",
7283 basename((char *)webkit_download_get_destination_uri(wk_download
)));
7292 /* sync other download manager tabs */
7293 update_download_tabs(NULL
);
7296 * NOTE: never redirect/render the current tab before this
7297 * function returns. This will cause the download to never start.
7299 return (ret
); /* start download */
7303 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
7305 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
7308 show_oops(NULL
, "webview_hover_cb");
7313 set_status(t
, uri
, XT_STATUS_LINK
);
7316 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
7321 mark(struct tab
*t
, struct karg
*arg
)
7327 if ((index
= marktoindex(mark
)) == -1)
7330 if (arg
->i
== XT_MARK_SET
)
7331 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
7332 else if (arg
->i
== XT_MARK_GOTO
) {
7333 if (t
->mark
[index
] == XT_INVALID_MARK
) {
7334 show_oops(t
, "mark '%c' does not exist", mark
);
7337 /* XXX t->mark[index] can be bigger than the maximum if ajax or
7338 something changes the document size */
7339 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
7346 marks_clear(struct tab
*t
)
7350 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
7351 t
->mark
[i
] = XT_INVALID_MARK
;
7357 char file
[PATH_MAX
];
7358 char *line
= NULL
, *p
;
7363 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7364 if ((f
= fopen(file
, "r+")) == NULL
) {
7365 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7369 for (i
= 1; ; i
++) {
7370 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
7372 if (strlen(line
) == 0 || line
[0] == '#') {
7378 p
= strtok(line
, " \t");
7380 if (p
== NULL
|| strlen(p
) != 1 ||
7381 (index
= marktoindex(*p
)) == -1) {
7382 warnx("corrupt quickmarks file, line %d", i
);
7386 p
= strtok(NULL
, " \t");
7387 if (qmarks
[index
] != NULL
)
7388 g_free(qmarks
[index
]);
7389 qmarks
[index
] = g_strdup(p
);
7400 char file
[PATH_MAX
];
7404 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
7405 if ((f
= fopen(file
, "r+")) == NULL
) {
7406 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
7410 for (i
= 0; i
< XT_NOMARKS
; i
++)
7411 if (qmarks
[i
] != NULL
)
7412 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
7420 qmark(struct tab
*t
, struct karg
*arg
)
7425 mark
= arg
->s
[strlen(arg
->s
)-1];
7426 index
= marktoindex(mark
);
7432 if (qmarks
[index
] != NULL
)
7433 g_free(qmarks
[index
]);
7435 qmarks_load(); /* sync if multiple instances */
7436 qmarks
[index
] = g_strdup(get_uri(t
));
7440 if (qmarks
[index
] != NULL
)
7441 load_uri(t
, qmarks
[index
]);
7443 show_oops(t
, "quickmark \"%c\" does not exist",
7449 if (qmarks
[index
] != NULL
)
7450 create_new_tab(qmarks
[index
], NULL
, 1, -1);
7452 show_oops(t
, "quickmark \"%c\" does not exist",
7463 go_up(struct tab
*t
, struct karg
*args
)
7469 levels
= atoi(args
->s
);
7473 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
7474 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
7476 tmp
+= strlen(XT_PROTO_DELIM
);
7478 /* if an uri starts with a slash, leave it alone (for file:///) */
7485 p
= strrchr(tmp
, '/');
7499 gototab(struct tab
*t
, struct karg
*args
)
7502 struct karg arg
= {0, NULL
, -1};
7504 tab
= atoi(args
->s
);
7506 arg
.i
= XT_TAB_NEXT
;
7515 zoom_amount(struct tab
*t
, struct karg
*arg
)
7517 struct karg narg
= {0, NULL
, -1};
7519 narg
.i
= atoi(arg
->s
);
7520 resizetab(t
, &narg
);
7526 flip_colon(struct tab
*t
, struct karg
*arg
)
7528 struct karg narg
= {0, NULL
, -1};
7531 if (t
== NULL
|| arg
== NULL
)
7534 p
= strstr(arg
->s
, ":");
7546 /* buffer commands receive the regex that triggered them in arg.s */
7547 char bcmd
[XT_BUFCMD_SZ
];
7551 #define XT_PRE_NO (0)
7552 #define XT_PRE_YES (1)
7553 #define XT_PRE_MAYBE (2)
7555 int (*func
)(struct tab
*, struct karg
*);
7559 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
7560 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
7561 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
7562 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
7563 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
7564 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
7565 { "^['][a-zA-Z0-9]$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
7566 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
7567 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
7568 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
7569 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
7570 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
7571 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
7572 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
7573 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
7574 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
7575 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
7576 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
7580 buffercmd_init(void)
7584 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7585 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
7586 REG_EXTENDED
| REG_NOSUB
))
7587 startpage_add("invalid buffercmd regex %s",
7588 buffercmds
[i
].regex
);
7592 buffercmd_abort(struct tab
*t
)
7596 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
7597 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7600 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
7601 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7605 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
7607 struct karg arg
= {0, NULL
, -1};
7610 arg
.s
= g_strdup(bcmd
);
7612 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
7613 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
7623 buffercmd_addkey(struct tab
*t
, guint keyval
)
7626 char s
[XT_BUFCMD_SZ
];
7628 if (keyval
== GDK_Escape
) {
7630 return (XT_CB_HANDLED
);
7633 /* key with modifier or non-ascii character */
7634 if (!isascii(keyval
))
7635 return (XT_CB_PASSTHROUGH
);
7637 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
7638 "to buffer \"%s\"\n", keyval
, bcmd
);
7640 for (i
= 0; i
< LENGTH(bcmd
); i
++)
7641 if (bcmd
[i
] == '\0') {
7646 /* buffer full, ignore input */
7647 if (i
>= LENGTH(bcmd
) -1) {
7648 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
7650 return (XT_CB_HANDLED
);
7653 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
7655 /* find exact match */
7656 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
7657 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
7658 (size_t) 0, NULL
, 0) == 0) {
7659 buffercmd_execute(t
, &buffercmds
[i
]);
7663 /* find non exact matches to see if we need to abort ot not */
7664 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
7665 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
7668 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
7669 if (isdigit(bcmd
[0])) {
7670 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7674 if (sscanf(bcmd
, "%s", s
) == 0)
7677 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
7678 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
7681 if (sscanf(bcmd
, "%s", s
) == 0)
7684 if (c
== -1 && buffercmds
[i
].precount
)
7686 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
7689 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
7690 i
, match
, buffercmds
[i
].cmd
, c
, s
);
7693 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
7698 return (XT_CB_HANDLED
);
7702 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
7704 struct key_binding
*k
;
7706 /* handle keybindings if buffercmd is empty.
7707 if not empty, allow commands like C-n */
7708 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
7709 TAILQ_FOREACH(k
, &kbl
, entry
)
7710 if (e
->keyval
== k
->key
7711 && (entry
? k
->use_in_entry
: 1)) {
7713 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7714 return (cmd_execute(t
, k
->cmd
));
7715 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7716 return (cmd_execute(t
, k
->cmd
));
7720 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7721 return buffercmd_addkey(t
, e
->keyval
);
7723 return (XT_CB_PASSTHROUGH
);
7727 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7729 char s
[2], buf
[128];
7730 const char *errstr
= NULL
;
7732 /* don't use w directly; use t->whatever instead */
7735 show_oops(NULL
, "wv_keypress_after_cb");
7736 return (XT_CB_PASSTHROUGH
);
7739 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7740 e
->keyval
, e
->state
, t
);
7744 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7746 return (XT_CB_HANDLED
);
7750 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7752 /* we have a string */
7754 /* we have a number */
7755 snprintf(buf
, sizeof buf
,
7756 "vimprobable_fire(%s)", t
->hint_num
);
7763 /* XXX unfuck this */
7764 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7765 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7766 /* last input was numerical */
7768 l
= strlen(t
->hint_num
);
7775 t
->hint_num
[l
] = '\0';
7779 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7780 /* last input was alphanumerical */
7782 l
= strlen(t
->hint_buf
);
7789 t
->hint_buf
[l
] = '\0';
7799 /* numerical input */
7800 if (CLEAN(e
->state
) == 0 &&
7801 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7802 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7803 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7804 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7805 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7809 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7810 "invalid link number\n");
7813 snprintf(buf
, sizeof buf
,
7814 "vimprobable_update_hints(%s)",
7816 t
->hint_mode
= XT_HINT_NUMERICAL
;
7820 /* empty the counter buffer */
7821 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7822 return (XT_CB_HANDLED
);
7825 /* alphanumerical input */
7826 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7827 e
->keyval
<= GDK_z
) ||
7828 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7829 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7830 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7831 e
->keyval
<= GDK_9
) ||
7832 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7833 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7834 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7835 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7836 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7837 " %s\n", t
->hint_buf
);
7839 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7842 snprintf(buf
, sizeof buf
,
7843 "vimprobable_show_hints('%s')", t
->hint_buf
);
7844 t
->hint_mode
= XT_HINT_ALPHANUM
;
7847 /* empty the counter buffer */
7848 bzero(t
->hint_num
, sizeof t
->hint_num
);
7849 return (XT_CB_HANDLED
);
7852 return (XT_CB_HANDLED
);
7855 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7856 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7857 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7860 return (handle_keypress(t
, e
, 0));
7864 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7868 /* Hide buffers, if they are visible, with escape. */
7869 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7870 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7871 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7873 return (XT_CB_HANDLED
);
7876 return (XT_CB_PASSTHROUGH
);
7880 search_continue(struct tab
*t
)
7882 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7883 gboolean rv
= FALSE
;
7887 if (strlen(c
) == 1) {
7888 webkit_web_view_unmark_text_matches(t
->wv
);
7893 t
->search_forward
= TRUE
;
7894 else if (c
[0] == '?')
7895 t
->search_forward
= FALSE
;
7905 search_cb(struct tab
*t
)
7907 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
7910 if (search_continue(t
) == FALSE
)
7914 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
7916 /* not found, mark red */
7917 gdk_color_parse(XT_COLOR_RED
, &color
);
7918 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7919 /* unmark and remove selection */
7920 webkit_web_view_unmark_text_matches(t
->wv
);
7921 /* my kingdom for a way to unselect text in webview */
7923 /* found, highlight all */
7924 webkit_web_view_unmark_text_matches(t
->wv
);
7925 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7926 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7927 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7928 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7936 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7938 const gchar
*c
= gtk_entry_get_text(w
);
7941 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7942 return (XT_CB_PASSTHROUGH
);
7945 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7946 e
->keyval
, e
->state
, t
);
7948 if (search_continue(t
) == FALSE
)
7951 /* if search length is > 4 then no longer play timeout games */
7952 if (strlen(c
) > 4) {
7954 g_source_remove(t
->search_id
);
7961 /* reestablish a new timer if the user types fast */
7963 g_source_remove(t
->search_id
);
7964 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
7967 return (XT_CB_PASSTHROUGH
);
7971 match_uri(const gchar
*uri
, const gchar
*key
) {
7974 gboolean match
= FALSE
;
7978 if (!strncmp(key
, uri
, len
))
7981 voffset
= strstr(uri
, "/") + 2;
7982 if (!strncmp(key
, voffset
, len
))
7984 else if (g_str_has_prefix(voffset
, "www.")) {
7985 voffset
= voffset
+ strlen("www.");
7986 if (!strncmp(key
, voffset
, len
))
7995 match_session(const gchar
*name
, const gchar
*key
) {
7998 sub
= strcasestr(name
, key
);
8004 cmd_getlist(int id
, char *key
)
8011 if (cmds
[id
].type
& XT_URLARG
) {
8012 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
8013 if (match_uri(h
->uri
, key
)) {
8014 cmd_status
.list
[c
] = (char *)h
->uri
;
8020 } else if (cmds
[id
].type
& XT_SESSARG
) {
8021 TAILQ_FOREACH(s
, &sessions
, entry
)
8022 if (match_session(s
->name
, key
)) {
8023 cmd_status
.list
[c
] = (char *)s
->name
;
8029 } else if (cmds
[id
].type
& XT_SETARG
) {
8030 for (i
= 0; i
< LENGTH(rs
); i
++)
8031 if(!strncmp(key
, rs
[i
].name
, strlen(key
)))
8032 cmd_status
.list
[c
++] = rs
[i
].name
;
8038 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
8040 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
8041 if (cmds
[i
].level
< dep
)
8043 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
8044 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
8045 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
8053 cmd_getnext(int dir
)
8055 cmd_status
.index
+= dir
;
8057 if (cmd_status
.index
< 0)
8058 cmd_status
.index
= cmd_status
.len
- 1;
8059 else if (cmd_status
.index
>= cmd_status
.len
)
8060 cmd_status
.index
= 0;
8062 return cmd_status
.list
[cmd_status
.index
];
8066 cmd_tokenize(char *s
, char *tokens
[])
8069 char *tok
, *last
= NULL
;
8070 size_t len
= strlen(s
);
8073 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
8074 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
8075 tok
= strtok_r(NULL
, " ", &last
), i
++)
8085 cmd_complete(struct tab
*t
, char *str
, int dir
)
8087 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
8088 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
8090 char *tok
, *match
, *s
= g_strdup(str
);
8092 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
8095 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
8098 for (i
= 0; isdigit(s
[i
]); i
++)
8101 for (; isspace(s
[i
]); i
++)
8106 levels
= cmd_tokenize(s
, tokens
);
8108 for (i
= 0; i
< levels
- 1; i
++) {
8111 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8112 if (cmds
[j
].level
< dep
)
8114 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
8118 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
8125 if (matchcount
== 1) {
8126 strlcat(res
, tok
, sizeof res
);
8127 strlcat(res
, " ", sizeof res
);
8137 if (cmd_status
.index
== -1)
8138 cmd_getlist(parent
, tokens
[i
]);
8140 if (cmd_status
.len
> 0) {
8141 match
= cmd_getnext(dir
);
8142 strlcat(res
, match
, sizeof res
);
8143 gtk_entry_set_text(w
, res
);
8144 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8151 cmd_execute(struct tab
*t
, char *str
)
8153 struct cmd
*cmd
= NULL
;
8154 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
8156 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
8157 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
8158 struct karg arg
= {0, NULL
, -1};
8163 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
8169 while (isspace(s
[0]))
8172 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
8173 prefix
= atoi(prefixstr
);
8177 for (tok
= strtok_r(s
, " ", &last
); tok
;
8178 tok
= strtok_r(NULL
, " ", &last
)) {
8180 for (j
= c
; j
< LENGTH(cmds
); j
++) {
8181 if (cmds
[j
].level
< dep
)
8183 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
8185 if (cmds
[j
].level
== dep
&&
8186 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
8190 if (len
== strlen(cmds
[j
].cmd
)) {
8196 if (matchcount
== 1) {
8201 show_oops(t
, "Invalid command: %s", str
);
8207 show_oops(t
, "Empty command");
8213 arg
.precount
= prefix
;
8214 else if (cmd_prefix
> 0)
8215 arg
.precount
= cmd_prefix
;
8217 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
8218 show_oops(t
, "No prefix allowed: %s", str
);
8222 arg
.s
= last
? g_strdup(last
) : g_strdup("");
8223 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
8224 if (arg
.s
== NULL
) {
8225 show_oops(t
, "Invalid command");
8228 arg
.precount
= atoi(arg
.s
);
8229 if (arg
.precount
<= 0) {
8230 if (arg
.s
[0] == '0')
8231 show_oops(t
, "Zero count");
8233 show_oops(t
, "Trailing characters");
8238 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
8239 __func__
, arg
.precount
, arg
.s
);
8255 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8258 show_oops(NULL
, "entry_key_cb invalid parameters");
8259 return (XT_CB_PASSTHROUGH
);
8262 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
8263 e
->keyval
, e
->state
, t
);
8267 if (e
->keyval
== GDK_Escape
) {
8268 /* don't use focus_webview(t) because we want to type :cmds */
8269 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8272 return (handle_keypress(t
, e
, 1));
8275 struct command_entry
*
8276 history_prev(struct command_list
*l
, struct command_entry
*at
)
8279 at
= TAILQ_LAST(l
, command_list
);
8281 at
= TAILQ_PREV(at
, command_list
, entry
);
8283 at
= TAILQ_LAST(l
, command_list
);
8289 struct command_entry
*
8290 history_next(struct command_list
*l
, struct command_entry
*at
)
8293 at
= TAILQ_FIRST(l
);
8295 at
= TAILQ_NEXT(at
, entry
);
8297 at
= TAILQ_FIRST(l
);
8304 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
8306 int rv
= XT_CB_HANDLED
;
8307 const gchar
*c
= gtk_entry_get_text(w
);
8310 show_oops(NULL
, "cmd_keypress_cb parameters");
8311 return (XT_CB_PASSTHROUGH
);
8314 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
8315 e
->keyval
, e
->state
, t
);
8319 e
->keyval
= GDK_Escape
;
8320 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8321 e
->keyval
= GDK_Escape
;
8323 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
8324 e
->keyval
!= GDK_ISO_Left_Tab
)
8325 cmd_status
.index
= -1;
8327 switch (e
->keyval
) {
8330 cmd_complete(t
, (char *)&c
[1], 1);
8332 case GDK_ISO_Left_Tab
:
8334 cmd_complete(t
, (char *)&c
[1], -1);
8339 if ((search_at
= history_next(&shl
, search_at
))) {
8340 search_at
->line
[0] = c
[0];
8341 gtk_entry_set_text(w
, search_at
->line
);
8342 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8345 if ((history_at
= history_prev(&chl
, history_at
))) {
8346 history_at
->line
[0] = c
[0];
8347 gtk_entry_set_text(w
, history_at
->line
);
8348 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8355 if ((search_at
= history_next(&shl
, search_at
))) {
8356 search_at
->line
[0] = c
[0];
8357 gtk_entry_set_text(w
, search_at
->line
);
8358 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8361 if ((history_at
= history_next(&chl
, history_at
))) {
8362 history_at
->line
[0] = c
[0];
8363 gtk_entry_set_text(w
, history_at
->line
);
8364 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
8370 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
8378 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
8379 webkit_web_view_unmark_text_matches(t
->wv
);
8383 rv
= XT_CB_PASSTHROUGH
;
8389 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8391 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
8395 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
8397 /* popup menu enabled */
8402 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
8405 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
8406 return (XT_CB_PASSTHROUGH
);
8409 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
8410 t
->tab_id
, t
->popup
);
8412 /* if popup is enabled don't lose focus */
8415 return (XT_CB_PASSTHROUGH
);
8421 if (show_url
== 0 || t
->focus_wv
)
8424 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8426 return (XT_CB_PASSTHROUGH
);
8430 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
8433 const gchar
*c
= gtk_entry_get_text(entry
);
8436 show_oops(NULL
, "cmd_activate_cb invalid parameters");
8440 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
8447 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
8453 if (c
[0] == '/' || c
[0] == '?') {
8454 /* see if there is a timer pending */
8456 g_source_remove(t
->search_id
);
8461 if (t
->search_text
) {
8462 g_free(t
->search_text
);
8463 t
->search_text
= NULL
;
8466 t
->search_text
= g_strdup(s
);
8468 g_free(global_search
);
8469 global_search
= g_strdup(s
);
8470 t
->search_forward
= c
[0] == '/';
8472 history_add(&shl
, search_file
, s
, &search_history_count
);
8476 history_add(&chl
, command_file
, s
, &cmd_history_count
);
8483 backward_cb(GtkWidget
*w
, struct tab
*t
)
8488 show_oops(NULL
, "backward_cb invalid parameters");
8492 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
8499 forward_cb(GtkWidget
*w
, struct tab
*t
)
8504 show_oops(NULL
, "forward_cb invalid parameters");
8508 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
8510 a
.i
= XT_NAV_FORWARD
;
8515 home_cb(GtkWidget
*w
, struct tab
*t
)
8518 show_oops(NULL
, "home_cb invalid parameters");
8522 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
8528 stop_cb(GtkWidget
*w
, struct tab
*t
)
8530 WebKitWebFrame
*frame
;
8533 show_oops(NULL
, "stop_cb invalid parameters");
8537 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
8539 frame
= webkit_web_view_get_main_frame(t
->wv
);
8540 if (frame
== NULL
) {
8541 show_oops(t
, "stop_cb: no frame");
8545 webkit_web_frame_stop_loading(frame
);
8546 abort_favicon_download(t
);
8550 setup_webkit(struct tab
*t
)
8552 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
8553 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
8554 FALSE
, (char *)NULL
);
8556 warnx("webkit does not have \"enable-dns-prefetching\" property");
8557 g_object_set(G_OBJECT(t
->settings
),
8558 "user-agent", t
->user_agent
, (char *)NULL
);
8559 g_object_set(G_OBJECT(t
->settings
),
8560 "enable-scripts", enable_scripts
, (char *)NULL
);
8561 g_object_set(G_OBJECT(t
->settings
),
8562 "enable-plugins", enable_plugins
, (char *)NULL
);
8563 g_object_set(G_OBJECT(t
->settings
),
8564 "javascript-can-open-windows-automatically", enable_scripts
,
8566 g_object_set(G_OBJECT(t
->settings
),
8567 "enable-html5-database", FALSE
, (char *)NULL
);
8568 g_object_set(G_OBJECT(t
->settings
),
8569 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
8570 g_object_set(G_OBJECT(t
->settings
),
8571 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
8572 g_object_set(G_OBJECT(t
->settings
),
8573 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
8574 g_object_set(G_OBJECT(t
->wv
),
8575 "full-content-zoom", TRUE
, (char *)NULL
);
8577 webkit_web_view_set_settings(t
->wv
, t
->settings
);
8581 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
8583 struct tab
*ti
, *t
= NULL
;
8584 gdouble view_size
, value
, max
;
8587 TAILQ_FOREACH(ti
, &tabs
, entry
)
8588 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
8596 if (adjustment
== NULL
)
8597 adjustment
= gtk_scrolled_window_get_vadjustment(
8598 GTK_SCROLLED_WINDOW(t
->browser_win
));
8600 view_size
= gtk_adjustment_get_page_size(adjustment
);
8601 value
= gtk_adjustment_get_value(adjustment
);
8602 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
8605 position
= g_strdup("All");
8606 else if (value
== max
)
8607 position
= g_strdup("Bot");
8608 else if (value
== 0)
8609 position
= g_strdup("Top");
8611 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
8613 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
8620 create_browser(struct tab
*t
)
8624 GtkAdjustment
*adjustment
;
8627 show_oops(NULL
, "create_browser invalid parameters");
8631 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
8632 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
8633 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
8634 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
8636 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
8637 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
8638 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
8640 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
8641 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
8644 t
->settings
= webkit_web_settings_new();
8646 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
8648 if (user_agent
== NULL
) {
8649 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
8651 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
8654 t
->user_agent
= g_strdup(user_agent
);
8656 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
8659 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
8660 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
8661 G_CALLBACK(update_statusbar_position
), NULL
);
8673 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
8674 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
8675 gtk_widget_set_name(w
, "xxxterm");
8676 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
8677 g_signal_connect(G_OBJECT(w
), "delete_event",
8678 G_CALLBACK (gtk_main_quit
), NULL
);
8684 create_kiosk_toolbar(struct tab
*t
)
8686 GtkWidget
*toolbar
= NULL
, *b
;
8688 b
= gtk_hbox_new(FALSE
, 0);
8690 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8692 /* backward button */
8693 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8694 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8695 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8696 G_CALLBACK(backward_cb
), t
);
8697 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
8699 /* forward button */
8700 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
8701 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8702 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8703 G_CALLBACK(forward_cb
), t
);
8704 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
8707 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
8708 gtk_widget_set_sensitive(t
->gohome
, true);
8709 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
8710 G_CALLBACK(home_cb
), t
);
8711 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
8713 /* create widgets but don't use them */
8714 t
->uri_entry
= gtk_entry_new();
8715 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8716 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8717 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8723 create_toolbar(struct tab
*t
)
8725 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
8727 b
= gtk_hbox_new(FALSE
, 0);
8729 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
8731 /* backward button */
8732 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
8733 gtk_widget_set_sensitive(t
->backward
, FALSE
);
8734 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
8735 G_CALLBACK(backward_cb
), t
);
8736 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
8738 /* forward button */
8739 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
8740 gtk_widget_set_sensitive(t
->forward
, FALSE
);
8741 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
8742 G_CALLBACK(forward_cb
), t
);
8743 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
8747 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
8748 gtk_widget_set_sensitive(t
->stop
, FALSE
);
8749 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
8750 G_CALLBACK(stop_cb
), t
);
8751 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
8755 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
8756 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
8757 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
8758 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
8759 G_CALLBACK(js_toggle_cb
), t
);
8760 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
8762 t
->uri_entry
= gtk_entry_new();
8763 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
8764 G_CALLBACK(activate_uri_entry_cb
), t
);
8765 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
8766 G_CALLBACK(entry_key_cb
), t
);
8768 eb1
= gtk_hbox_new(FALSE
, 0);
8769 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
8770 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
8771 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
8774 if (search_string
) {
8776 t
->search_entry
= gtk_entry_new();
8777 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
8778 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
8779 G_CALLBACK(activate_search_entry_cb
), t
);
8780 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
8781 G_CALLBACK(entry_key_cb
), t
);
8782 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
8783 eb2
= gtk_hbox_new(FALSE
, 0);
8784 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
8785 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
8787 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
8794 create_buffers(struct tab
*t
)
8796 GtkCellRenderer
*renderer
;
8799 view
= gtk_tree_view_new();
8801 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
8803 renderer
= gtk_cell_renderer_text_new();
8804 gtk_tree_view_insert_column_with_attributes
8805 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
8807 renderer
= gtk_cell_renderer_text_new();
8808 gtk_tree_view_insert_column_with_attributes
8809 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
8812 gtk_tree_view_set_model
8813 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
8819 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
8820 GtkTreeViewColumn
*col
, struct tab
*t
)
8825 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8827 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
8830 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
8831 set_current_tab(id
- 1);
8837 /* after tab reordering/creation/removal */
8844 TAILQ_FOREACH(t
, &tabs
, entry
) {
8845 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
8846 if (t
->tab_id
> maxid
)
8849 gtk_widget_show(t
->tab_elems
.sep
);
8852 TAILQ_FOREACH(t
, &tabs
, entry
) {
8853 if (t
->tab_id
== maxid
) {
8854 gtk_widget_hide(t
->tab_elems
.sep
);
8860 /* after active tab change */
8862 recolor_compact_tabs(void)
8868 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8869 TAILQ_FOREACH(t
, &tabs
, entry
)
8870 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
8873 curid
= gtk_notebook_get_current_page(notebook
);
8874 TAILQ_FOREACH(t
, &tabs
, entry
)
8875 if (t
->tab_id
== curid
) {
8876 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8877 gtk_widget_modify_fg(t
->tab_elems
.label
,
8878 GTK_STATE_NORMAL
, &color
);
8884 set_current_tab(int page_num
)
8886 buffercmd_abort(get_current_tab());
8887 gtk_notebook_set_current_page(notebook
, page_num
);
8888 recolor_compact_tabs();
8892 undo_close_tab_save(struct tab
*t
)
8896 struct undo
*u1
, *u2
;
8898 WebKitWebHistoryItem
*item
;
8900 if ((uri
= get_uri(t
)) == NULL
)
8903 u1
= g_malloc0(sizeof(struct undo
));
8904 u1
->uri
= g_strdup(uri
);
8906 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8908 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8909 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8912 /* forward history */
8913 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8917 u1
->history
= g_list_prepend(u1
->history
,
8918 webkit_web_history_item_copy(item
));
8919 items
= g_list_next(items
);
8924 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8925 u1
->history
= g_list_prepend(u1
->history
,
8926 webkit_web_history_item_copy(item
));
8930 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8934 u1
->history
= g_list_prepend(u1
->history
,
8935 webkit_web_history_item_copy(item
));
8936 items
= g_list_next(items
);
8939 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8941 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8942 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8943 TAILQ_REMOVE(&undos
, u2
, entry
);
8945 g_list_free(u2
->history
);
8954 delete_tab(struct tab
*t
)
8958 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8964 * no need to join thread here because it won't access t on completion
8968 TAILQ_REMOVE(&tabs
, t
, entry
);
8970 /* Halt all webkit activity. */
8971 abort_favicon_download(t
);
8972 webkit_web_view_stop_loading(t
->wv
);
8974 /* Save the tab, so we can undo the close. */
8975 undo_close_tab_save(t
);
8977 if (browser_mode
== XT_BM_KIOSK
) {
8978 gtk_widget_destroy(t
->uri_entry
);
8979 gtk_widget_destroy(t
->stop
);
8980 gtk_widget_destroy(t
->js_toggle
);
8983 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8984 gtk_widget_destroy(t
->vbox
);
8988 g_source_remove(t
->search_id
);
8990 g_free(t
->user_agent
);
8991 g_free(t
->stylesheet
);
8995 if (TAILQ_EMPTY(&tabs
)) {
8996 if (browser_mode
== XT_BM_KIOSK
)
8997 create_new_tab(home
, NULL
, 1, -1);
8999 create_new_tab(NULL
, NULL
, 1, -1);
9002 /* recreate session */
9003 if (session_autosave
) {
9009 recolor_compact_tabs();
9013 update_statusbar_zoom(struct tab
*t
)
9016 char s
[16] = { '\0' };
9018 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9019 if ((zoom
<= 0.99 || zoom
>= 1.01))
9020 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
9021 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
9025 setzoom_webkit(struct tab
*t
, int adjust
)
9027 #define XT_ZOOMPERCENT 0.04
9032 show_oops(NULL
, "setzoom_webkit invalid parameters");
9036 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
9037 if (adjust
== XT_ZOOM_IN
)
9038 zoom
+= XT_ZOOMPERCENT
;
9039 else if (adjust
== XT_ZOOM_OUT
)
9040 zoom
-= XT_ZOOMPERCENT
;
9041 else if (adjust
> 0)
9042 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
9044 show_oops(t
, "setzoom_webkit invalid zoom value");
9048 if (zoom
< XT_ZOOMPERCENT
)
9049 zoom
= XT_ZOOMPERCENT
;
9050 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
9051 update_statusbar_zoom(t
);
9055 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
9057 struct tab
*t
= (struct tab
*) data
;
9059 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
9061 switch (event
->button
) {
9063 set_current_tab(t
->tab_id
);
9074 append_tab(struct tab
*t
)
9079 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9080 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
9084 create_sbe(int width
)
9088 sbe
= gtk_entry_new();
9089 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
9090 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
9091 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
9092 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
9093 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
9094 gtk_widget_set_size_request(sbe
, width
, -1);
9100 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
9105 WebKitWebHistoryItem
*item
;
9109 int sbe_p
= 0, sbe_b
= 0,
9112 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
9114 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
9115 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
9119 t
= g_malloc0(sizeof *t
);
9121 if (title
== NULL
) {
9122 title
= "(untitled)";
9126 t
->vbox
= gtk_vbox_new(FALSE
, 0);
9128 /* label + button for tab */
9129 b
= gtk_hbox_new(FALSE
, 0);
9132 #if GTK_CHECK_VERSION(2, 20, 0)
9133 t
->spinner
= gtk_spinner_new();
9135 t
->label
= gtk_label_new(title
);
9136 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
9137 gtk_widget_set_size_request(t
->label
, 100, 0);
9138 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
9139 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
9140 gtk_widget_set_size_request(b
, 130, 0);
9142 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
9143 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
9144 #if GTK_CHECK_VERSION(2, 20, 0)
9145 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
9149 if (browser_mode
== XT_BM_KIOSK
) {
9150 t
->toolbar
= create_kiosk_toolbar(t
);
9151 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
9154 t
->toolbar
= create_toolbar(t
);
9156 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
9164 t
->browser_win
= create_browser(t
);
9165 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
9167 /* oops message for user feedback */
9168 t
->oops
= gtk_entry_new();
9169 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
9170 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
9171 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
9172 gdk_color_parse(XT_COLOR_RED
, &color
);
9173 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
9174 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
9175 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
9178 t
->cmd
= gtk_entry_new();
9179 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
9180 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
9181 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
9182 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
9185 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
9187 t
->sbe
.statusbar
= gtk_entry_new();
9188 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
9189 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
9190 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
9191 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
9193 /* create these widgets only if specified in statusbar_elems */
9195 t
->sbe
.position
= create_sbe(40);
9196 t
->sbe
.zoom
= create_sbe(40);
9197 t
->sbe
.buffercmd
= create_sbe(60);
9199 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
9201 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
9204 /* gtk widgets cannot be added to a box twice. sbe_* variables
9205 make sure of this */
9206 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
9210 GtkWidget
*sep
= gtk_vseparator_new();
9212 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
9213 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
9214 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
9215 FALSE
, FALSE
, FALSE
);
9220 warnx("flag \"%c\" specified more than "
9221 "once in statusbar_elems\n", *p
);
9225 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9226 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
9230 warnx("flag \"%c\" specified more than "
9231 "once in statusbar_elems\n", *p
);
9235 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9236 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
9240 warnx("flag \"%c\" specified more than "
9241 "once in statusbar_elems\n", *p
);
9245 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
9246 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
9249 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
9254 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
9257 t
->buffers
= create_buffers(t
);
9258 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
9260 /* xtp meaning is normal by default */
9261 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
9263 /* set empty favicon */
9264 xt_icon_from_name(t
, "text-html");
9266 /* and show it all */
9267 gtk_widget_show_all(b
);
9268 gtk_widget_show_all(t
->vbox
);
9270 /* compact tab bar */
9271 t
->tab_elems
.label
= gtk_label_new(title
);
9272 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
9273 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
9274 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
9275 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
9277 t
->tab_elems
.eventbox
= gtk_event_box_new();
9278 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
9279 t
->tab_elems
.sep
= gtk_vseparator_new();
9281 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
9282 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
9283 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
9284 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
9285 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
9286 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
9288 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
9290 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
9292 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
9295 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
9297 gtk_widget_show_all(t
->tab_elems
.eventbox
);
9299 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
9302 id
= position
>= 0 ? position
:
9303 gtk_notebook_get_current_page(notebook
) + 1;
9304 if (id
> gtk_notebook_get_n_pages(notebook
))
9307 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
9308 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
9309 gtk_box_reorder_child(GTK_BOX(tab_bar
),
9310 t
->tab_elems
.eventbox
, id
);
9315 #if GTK_CHECK_VERSION(2, 20, 0)
9316 /* turn spinner off if we are a new tab without uri */
9318 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
9319 gtk_widget_hide(t
->spinner
);
9322 /* make notebook tabs reorderable */
9323 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
9325 /* compact tabs clickable */
9326 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
9327 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
9329 g_object_connect(G_OBJECT(t
->cmd
),
9330 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
9331 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
9332 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
9333 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
9334 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
9337 /* reuse wv_button_cb to hide oops */
9338 g_object_connect(G_OBJECT(t
->oops
),
9339 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9342 g_signal_connect(t
->buffers
,
9343 "row-activated", G_CALLBACK(row_activated_cb
), t
);
9344 g_object_connect(G_OBJECT(t
->buffers
),
9345 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
9347 g_object_connect(G_OBJECT(t
->wv
),
9348 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
9349 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9350 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
9351 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
9352 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
9353 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9354 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
9355 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
9356 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
9357 "signal::event", G_CALLBACK(webview_event_cb
), t
,
9358 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
9359 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
9360 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
9361 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
9362 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
9363 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
9365 g_signal_connect(t
->wv
,
9366 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
9367 g_signal_connect(t
->wv
,
9368 "notify::title", G_CALLBACK(notify_title_cb
), t
);
9370 /* hijack the unused keys as if we were the browser */
9371 g_object_connect(G_OBJECT(t
->toolbar
),
9372 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
9375 g_signal_connect(G_OBJECT(bb
), "button_press_event",
9376 G_CALLBACK(tab_close_cb
), t
);
9379 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
9380 /* restore the tab's history */
9381 if (u
&& u
->history
) {
9385 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
9386 items
= g_list_next(items
);
9389 item
= g_list_nth_data(u
->history
, u
->back
);
9391 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
9394 g_list_free(u
->history
);
9396 webkit_web_back_forward_list_clear(t
->bfl
);
9402 url_set_visibility();
9403 statusbar_set_visibility();
9406 set_current_tab(t
->tab_id
);
9407 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
9411 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
9415 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
9422 recolor_compact_tabs();
9423 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
9428 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9434 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
9436 if (gtk_notebook_get_current_page(notebook
) == -1)
9439 TAILQ_FOREACH(t
, &tabs
, entry
) {
9440 if (t
->tab_id
== pn
) {
9441 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
9444 uri
= get_title(t
, TRUE
);
9445 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
9451 /* can't use focus_webview here */
9452 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
9459 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
9462 struct tab
*t
= NULL
, *tt
;
9466 TAILQ_FOREACH(tt
, &tabs
, entry
)
9467 if (tt
->tab_id
== pn
) {
9473 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
9475 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
9480 menuitem_response(struct tab
*t
)
9482 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
9486 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
9488 GtkWidget
*menu
, *menu_items
;
9489 GdkEventButton
*bevent
;
9493 if (event
->type
== GDK_BUTTON_PRESS
) {
9494 bevent
= (GdkEventButton
*) event
;
9495 menu
= gtk_menu_new();
9497 TAILQ_FOREACH(ti
, &tabs
, entry
) {
9498 if ((uri
= get_uri(ti
)) == NULL
)
9499 /* XXX make sure there is something to print */
9500 /* XXX add gui pages in here to look purdy */
9502 menu_items
= gtk_menu_item_new_with_label(uri
);
9503 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
9504 gtk_widget_show(menu_items
);
9506 g_signal_connect_swapped((menu_items
),
9507 "activate", G_CALLBACK(menuitem_response
),
9511 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
9512 bevent
->button
, bevent
->time
);
9514 /* unref object so it'll free itself when popped down */
9515 #if !GTK_CHECK_VERSION(3, 0, 0)
9516 /* XXX does not need unref with gtk+3? */
9517 g_object_ref_sink(menu
);
9518 g_object_unref(menu
);
9521 return (TRUE
/* eat event */);
9524 return (FALSE
/* propagate */);
9528 icon_size_map(int icon_size
)
9530 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
9531 icon_size
> GTK_ICON_SIZE_DIALOG
)
9532 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
9538 create_button(char *name
, char *stockid
, int size
)
9540 GtkWidget
*button
, *image
;
9544 rcstring
= g_strdup_printf(
9545 "style \"%s-style\"\n"
9547 " GtkWidget::focus-padding = 0\n"
9548 " GtkWidget::focus-line-width = 0\n"
9552 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
9553 gtk_rc_parse_string(rcstring
);
9555 button
= gtk_button_new();
9556 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
9557 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
9559 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
9560 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9561 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
9562 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
9563 gtk_widget_set_name(button
, name
);
9564 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
9570 button_set_stockid(GtkWidget
*button
, char *stockid
)
9574 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
9575 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
9576 gtk_button_set_image(GTK_BUTTON(button
), image
);
9580 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
9583 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
9586 if (xterm_workaround
== 0)
9590 * xterm doesn't play nice with clipboards because it clears the
9591 * primary when clicked. We rely on primary being set to properly
9592 * handle middle mouse button clicks (paste). So when someone clears
9593 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
9594 * other application behavior (as in DON'T clear primary).
9597 p
= gtk_clipboard_wait_for_text(primary
);
9599 if (gdk_property_get(gdk_get_default_root_window(),
9601 gdk_atom_intern("STRING", FALSE
),
9603 1024 * 1024 /* picked out of my butt */,
9609 /* yes sir, we need to NUL the string */
9611 gtk_clipboard_set_text(primary
, p
, -1);
9625 char file
[PATH_MAX
];
9628 vbox
= gtk_vbox_new(FALSE
, 0);
9629 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
9630 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
9631 #if !GTK_CHECK_VERSION(3, 0, 0)
9632 /* XXX seems to be needed with gtk+2 */
9633 gtk_notebook_set_tab_hborder(notebook
, 0);
9634 gtk_notebook_set_tab_vborder(notebook
, 0);
9636 gtk_notebook_set_scrollable(notebook
, TRUE
);
9637 gtk_notebook_set_show_border(notebook
, FALSE
);
9638 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
9640 abtn
= gtk_button_new();
9641 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
9642 gtk_widget_set_size_request(arrow
, -1, -1);
9643 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
9644 gtk_widget_set_size_request(abtn
, -1, 20);
9646 #if GTK_CHECK_VERSION(2, 20, 0)
9647 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
9649 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
9651 /* compact tab bar */
9652 tab_bar
= gtk_hbox_new(TRUE
, 0);
9654 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
9655 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
9656 gtk_widget_set_size_request(vbox
, -1, -1);
9658 g_object_connect(G_OBJECT(notebook
),
9659 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
9661 g_object_connect(G_OBJECT(notebook
),
9662 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
9663 NULL
, (char *)NULL
);
9664 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
9665 G_CALLBACK(arrow_cb
), NULL
);
9667 main_window
= create_window();
9668 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
9671 for (i
= 0; i
< LENGTH(icons
); i
++) {
9672 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
9673 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
9674 l
= g_list_append(l
, pb
);
9676 gtk_window_set_default_icon_list(l
);
9678 /* clipboard work around */
9679 if (xterm_workaround
)
9681 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
9682 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
9684 gtk_widget_show_all(abtn
);
9685 gtk_widget_show_all(main_window
);
9686 notebook_tab_set_visibility();
9690 set_hook(void **hook
, char *name
)
9693 errx(1, "set_hook");
9695 if (*hook
== NULL
) {
9696 *hook
= dlsym(RTLD_NEXT
, name
);
9698 errx(1, "can't hook %s", name
);
9702 /* override libsoup soup_cookie_equal because it doesn't look at domain */
9704 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
9706 g_return_val_if_fail(cookie1
, FALSE
);
9707 g_return_val_if_fail(cookie2
, FALSE
);
9709 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
9710 !strcmp (cookie1
->value
, cookie2
->value
) &&
9711 !strcmp (cookie1
->path
, cookie2
->path
) &&
9712 !strcmp (cookie1
->domain
, cookie2
->domain
));
9716 transfer_cookies(void)
9719 SoupCookie
*sc
, *pc
;
9721 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9723 for (;cf
; cf
= cf
->next
) {
9725 sc
= soup_cookie_copy(pc
);
9726 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
9729 soup_cookies_free(cf
);
9733 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
9738 print_cookie("soup_cookie_jar_delete_cookie", c
);
9740 if (cookies_enabled
== 0)
9743 if (jar
== NULL
|| c
== NULL
)
9746 /* find and remove from persistent jar */
9747 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
9749 for (;cf
; cf
= cf
->next
) {
9751 if (soup_cookie_equal(ci
, c
)) {
9752 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
9757 soup_cookies_free(cf
);
9759 /* delete from session jar */
9760 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
9764 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
9766 struct domain
*d
= NULL
;
9770 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
9771 jar
, p_cookiejar
, s_cookiejar
);
9773 if (cookies_enabled
== 0)
9776 /* see if we are up and running */
9777 if (p_cookiejar
== NULL
) {
9778 _soup_cookie_jar_add_cookie(jar
, cookie
);
9781 /* disallow p_cookiejar adds, shouldn't happen */
9782 if (jar
== p_cookiejar
)
9786 if (jar
== NULL
|| cookie
== NULL
)
9789 if (enable_cookie_whitelist
&&
9790 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
9792 DNPRINTF(XT_D_COOKIE
,
9793 "soup_cookie_jar_add_cookie: reject %s\n",
9795 if (save_rejected_cookies
) {
9796 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
9797 show_oops(NULL
, "can't open reject cookie file");
9800 fseek(r_cookie_f
, 0, SEEK_END
);
9801 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
9802 cookie
->http_only
? "#HttpOnly_" : "",
9804 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
9806 cookie
->secure
? "TRUE" : "FALSE",
9808 (gulong
)soup_date_to_time_t(cookie
->expires
) :
9815 if (!allow_volatile_cookies
)
9819 if (cookie
->expires
== NULL
&& session_timeout
) {
9820 soup_cookie_set_expires(cookie
,
9821 soup_date_new_from_now(session_timeout
));
9822 print_cookie("modified add cookie", cookie
);
9825 /* see if we are white listed for persistence */
9826 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
9827 /* add to persistent jar */
9828 c
= soup_cookie_copy(cookie
);
9829 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
9830 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
9833 /* add to session jar */
9834 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
9835 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
9841 char file
[PATH_MAX
];
9843 set_hook((void *)&_soup_cookie_jar_add_cookie
,
9844 "soup_cookie_jar_add_cookie");
9845 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
9846 "soup_cookie_jar_delete_cookie");
9848 if (cookies_enabled
== 0)
9852 * the following code is intricate due to overriding several libsoup
9854 * do not alter order of these operations.
9857 /* rejected cookies */
9858 if (save_rejected_cookies
)
9859 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
9862 /* persistent cookies */
9863 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
9864 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
9866 /* session cookies */
9867 s_cookiejar
= soup_cookie_jar_new();
9868 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
9869 cookie_policy
, (void *)NULL
);
9872 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
9876 setup_proxy(char *uri
)
9879 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
9880 soup_uri_free(proxy_uri
);
9884 if (http_proxy
!= uri
) {
9891 http_proxy
= g_strdup(uri
);
9892 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9893 proxy_uri
= soup_uri_new(http_proxy
);
9894 if (!(proxy_uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(proxy_uri
)))
9895 g_object_set(session
, "proxy-uri", proxy_uri
,
9901 set_http_proxy(char *proxy
)
9908 /* see if we need to clear it instead */
9909 if (strlen(proxy
) == 0) {
9914 uri
= soup_uri_new(proxy
);
9915 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
))
9926 send_cmd_to_socket(char *cmd
)
9929 struct sockaddr_un sa
;
9931 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9932 warnx("%s: socket", __func__
);
9936 sa
.sun_family
= AF_UNIX
;
9937 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9938 work_dir
, XT_SOCKET_FILE
);
9941 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9942 warnx("%s: connect", __func__
);
9946 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9947 warnx("%s: send", __func__
);
9958 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9961 char str
[XT_MAX_URL_LENGTH
];
9962 socklen_t t
= sizeof(struct sockaddr_un
);
9963 struct sockaddr_un sa
;
9968 gint fd
= g_io_channel_unix_get_fd(source
);
9970 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9975 if (getpeereid(s
, &uid
, &gid
) == -1) {
9979 if (uid
!= getuid() || gid
!= getgid()) {
9980 warnx("unauthorized user");
9986 warnx("not a valid user");
9990 n
= recv(s
, str
, sizeof(str
), 0);
9994 tt
= TAILQ_LAST(&tabs
, tab_list
);
9995 cmd_execute(tt
, str
);
10002 int s
, len
, rv
= 1;
10003 struct sockaddr_un sa
;
10005 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10006 warn("is_running: socket");
10010 sa
.sun_family
= AF_UNIX
;
10011 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10012 work_dir
, XT_SOCKET_FILE
);
10013 len
= SUN_LEN(&sa
);
10015 /* connect to see if there is a listener */
10016 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
10017 rv
= 0; /* not running */
10019 rv
= 1; /* already running */
10030 struct sockaddr_un sa
;
10032 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
10033 warn("build_socket: socket");
10037 sa
.sun_family
= AF_UNIX
;
10038 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
10039 work_dir
, XT_SOCKET_FILE
);
10040 len
= SUN_LEN(&sa
);
10042 /* connect to see if there is a listener */
10043 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10044 /* no listener so we will */
10045 unlink(sa
.sun_path
);
10047 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
10048 warn("build_socket: bind");
10052 if (listen(s
, 1) == -1) {
10053 warn("build_socket: listen");
10066 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10067 GtkTreeIter
*iter
, struct tab
*t
)
10071 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10072 load_uri(t
, value
);
10079 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
10080 GtkTreeIter
*iter
, struct tab
*t
)
10084 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
10085 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
10086 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
10093 completion_add_uri(const gchar
*uri
)
10097 /* add uri to list_store */
10098 gtk_list_store_append(completion_model
, &iter
);
10099 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
10103 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
10104 GtkTreeIter
*iter
, gpointer user_data
)
10107 gboolean match
= FALSE
;
10109 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
10115 match
= match_uri(value
, key
);
10122 completion_add(struct tab
*t
)
10124 /* enable completion for tab */
10125 t
->completion
= gtk_entry_completion_new();
10126 gtk_entry_completion_set_text_column(t
->completion
, 0);
10127 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
10128 gtk_entry_completion_set_model(t
->completion
,
10129 GTK_TREE_MODEL(completion_model
));
10130 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
10132 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
10133 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
10134 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
10135 G_CALLBACK(completion_select_cb
), t
);
10136 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
10137 G_CALLBACK(completion_hover_cb
), t
);
10145 if (stat(dir
, &sb
)) {
10146 if (mkdir(dir
, S_IRWXU
) == -1)
10147 err(1, "mkdir %s", dir
);
10148 if (stat(dir
, &sb
))
10149 err(1, "stat %s", dir
);
10151 if (S_ISDIR(sb
.st_mode
) == 0)
10152 errx(1, "%s not a dir", dir
);
10153 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
10154 warnx("fixing invalid permissions on %s", dir
);
10155 if (chmod(dir
, S_IRWXU
) == -1)
10156 err(1, "chmod %s", dir
);
10164 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
10170 main(int argc
, char *argv
[])
10173 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
10174 char conf
[PATH_MAX
] = { '\0' };
10175 char file
[PATH_MAX
];
10176 char *env_proxy
= NULL
;
10180 struct sigaction sact
;
10181 GIOChannel
*channel
;
10187 g_thread_init(NULL
);
10188 #if !defined(__linux__)
10189 gdk_threads_init();
10191 gdk_threads_enter();
10192 gtk_init(&argc
, &argv
);
10194 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
10198 RB_INIT(&downloads
);
10200 TAILQ_INIT(&sessions
);
10203 TAILQ_INIT(&aliases
);
10204 TAILQ_INIT(&undos
);
10210 /* fiddle with ulimits */
10211 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10214 /* just use them all */
10215 rlp
.rlim_cur
= rlp
.rlim_max
;
10216 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10218 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
10220 else if (rlp
.rlim_cur
<= 256)
10221 startpage_add("%s requires at least 256 file "
10222 "descriptors, currently it has up to %d available",
10223 __progname
, rlp
.rlim_cur
);
10226 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
10235 errx(0 , "Version: %s", version
);
10238 strlcpy(conf
, optarg
, sizeof(conf
));
10241 strlcpy(named_session
, optarg
, sizeof(named_session
));
10260 init_keybindings();
10262 gnutls_global_init();
10264 /* generate session keys for xtp pages */
10265 generate_xtp_session_key(&dl_session_key
);
10266 generate_xtp_session_key(&hl_session_key
);
10267 generate_xtp_session_key(&cl_session_key
);
10268 generate_xtp_session_key(&fl_session_key
);
10271 bzero(&sact
, sizeof(sact
));
10272 sigemptyset(&sact
.sa_mask
);
10273 sact
.sa_handler
= sigchild
;
10274 sact
.sa_flags
= SA_NOCLDSTOP
;
10275 sigaction(SIGCHLD
, &sact
, NULL
);
10277 /* set download dir */
10278 pwd
= getpwuid(getuid());
10280 errx(1, "invalid user %d", getuid());
10281 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
10283 /* compile buffer command regexes */
10286 /* set default string settings */
10287 home
= g_strdup("https://www.cyphertite.com");
10288 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
10289 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
10290 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
10291 cmd_font_name
= g_strdup("monospace normal 9");
10292 oops_font_name
= g_strdup("monospace normal 9");
10293 statusbar_font_name
= g_strdup("monospace normal 9");
10294 tabbar_font_name
= g_strdup("monospace normal 9");
10295 statusbar_elems
= g_strdup("BP");
10296 encoding
= g_strdup("ISO-8859-1");
10298 /* read config file */
10299 if (strlen(conf
) == 0)
10300 snprintf(conf
, sizeof conf
, "%s/.%s",
10301 pwd
->pw_dir
, XT_CONF_FILE
);
10302 config_parse(conf
, 0);
10305 cmd_font
= pango_font_description_from_string(cmd_font_name
);
10306 oops_font
= pango_font_description_from_string(oops_font_name
);
10307 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
10308 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
10310 /* working directory */
10311 if (strlen(work_dir
) == 0)
10312 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
10313 pwd
->pw_dir
, XT_DIR
);
10316 /* icon cache dir */
10317 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
10318 xxx_dir(cache_dir
);
10321 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
10322 xxx_dir(certs_dir
);
10325 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
10326 work_dir
, XT_SESSIONS_DIR
);
10327 xxx_dir(sessions_dir
);
10329 /* runtime settings that can override config file */
10330 if (runtime_settings
[0] != '\0')
10331 config_parse(runtime_settings
, 1);
10334 if (!strcmp(download_dir
, pwd
->pw_dir
))
10335 strlcat(download_dir
, "/downloads", sizeof download_dir
);
10336 xxx_dir(download_dir
);
10338 /* favorites file */
10339 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
10340 if (stat(file
, &sb
)) {
10341 warnx("favorites file doesn't exist, creating it");
10342 if ((f
= fopen(file
, "w")) == NULL
)
10343 err(1, "favorites");
10347 /* quickmarks file */
10348 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
10349 if (stat(file
, &sb
)) {
10350 warnx("quickmarks file doesn't exist, creating it");
10351 if ((f
= fopen(file
, "w")) == NULL
)
10352 err(1, "quickmarks");
10356 /* search history */
10357 if (history_autosave
) {
10358 snprintf(search_file
, sizeof search_file
, "%s/%s",
10359 work_dir
, XT_SEARCH_FILE
);
10360 if (stat(search_file
, &sb
)) {
10361 warnx("search history file doesn't exist, creating it");
10362 if ((f
= fopen(search_file
, "w")) == NULL
)
10363 err(1, "search_history");
10366 history_read(&shl
, search_file
, &search_history_count
);
10369 /* command history */
10370 if (history_autosave
) {
10371 snprintf(command_file
, sizeof command_file
, "%s/%s",
10372 work_dir
, XT_COMMAND_FILE
);
10373 if (stat(command_file
, &sb
)) {
10374 warnx("command history file doesn't exist, creating it");
10375 if ((f
= fopen(command_file
, "w")) == NULL
)
10376 err(1, "command_history");
10379 history_read(&chl
, command_file
, &cmd_history_count
);
10383 session
= webkit_get_default_session();
10388 if (stat(ssl_ca_file
, &sb
)) {
10389 warnx("no CA file: %s", ssl_ca_file
);
10390 g_free(ssl_ca_file
);
10391 ssl_ca_file
= NULL
;
10393 g_object_set(session
,
10394 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
10395 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
10399 /* guess_search regex */
10400 if (url_regex
== NULL
)
10401 url_regex
= g_strdup(XT_URL_REGEX
);
10403 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
10404 startpage_add("invalid url regex %s", url_regex
);
10407 env_proxy
= getenv("http_proxy");
10409 setup_proxy(env_proxy
);
10411 setup_proxy(http_proxy
);
10414 send_cmd_to_socket(argv
[0]);
10418 /* set some connection parameters */
10419 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
10420 g_object_set(session
, "max-conns-per-host", max_host_connections
,
10423 /* see if there is already an xxxterm running */
10424 if (single_instance
&& is_running()) {
10426 warnx("already running");
10431 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
10432 send_cmd_to_socket(cmd
);
10442 /* uri completion */
10443 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
10446 buffers_store
= gtk_list_store_new
10447 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
10453 notebook_tab_set_visibility();
10455 if (save_global_history
)
10456 restore_global_history();
10458 /* restore session list */
10459 restore_sessions_list();
10461 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
10462 restore_saved_tabs();
10464 a
.s
= named_session
;
10465 a
.i
= XT_SES_DONOTHING
;
10466 open_tabs(NULL
, &a
);
10469 /* see if we have an exception */
10470 if (!TAILQ_EMPTY(&spl
)) {
10471 create_new_tab("about:startpage", NULL
, focus
, -1);
10476 create_new_tab(argv
[0], NULL
, focus
, -1);
10483 if (TAILQ_EMPTY(&tabs
))
10484 create_new_tab(home
, NULL
, 1, -1);
10487 if ((s
= build_socket()) != -1) {
10488 channel
= g_io_channel_unix_new(s
);
10489 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
10494 gnutls_global_deinit();
10499 gdk_threads_leave();