3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * inverse color browsing
26 * multi letter commands
27 * pre and post counts for commands
28 * autocompletion on various inputs
29 * create privacy browsing
30 * - encrypted local data
46 #include <sys/types.h>
48 #if defined(__linux__)
49 #include "linux/util.h"
50 #include "linux/tree.h"
51 #elif defined(__FreeBSD__)
53 #include "freebsd/util.h"
59 #include <sys/queue.h>
61 #include <sys/socket.h>
65 #include <gdk/gdkkeysyms.h>
66 #include <webkit/webkit.h>
67 #include <libsoup/soup.h>
68 #include <gnutls/gnutls.h>
69 #include <JavaScriptCore/JavaScript.h>
70 #include <gnutls/x509.h>
72 #include "javascript.h"
75 javascript.h borrowed from vimprobable2 under the following license:
77 Copyright (c) 2009 Leon Winter
78 Copyright (c) 2009 Hannes Schueller
79 Copyright (c) 2009 Matto Fransen
81 Permission is hereby granted, free of charge, to any person obtaining a copy
82 of this software and associated documentation files (the "Software"), to deal
83 in the Software without restriction, including without limitation the rights
84 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
85 copies of the Software, and to permit persons to whom the Software is
86 furnished to do so, subject to the following conditions:
88 The above copyright notice and this permission notice shall be included in
89 all copies or substantial portions of the Software.
91 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
92 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
93 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
94 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
95 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
96 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
100 static char *version
= "$xxxterm$";
102 /* hooked functions */
103 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
104 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
109 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
110 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
111 #define XT_D_MOVE 0x0001
112 #define XT_D_KEY 0x0002
113 #define XT_D_TAB 0x0004
114 #define XT_D_URL 0x0008
115 #define XT_D_CMD 0x0010
116 #define XT_D_NAV 0x0020
117 #define XT_D_DOWNLOAD 0x0040
118 #define XT_D_CONFIG 0x0080
119 #define XT_D_JS 0x0100
120 #define XT_D_FAVORITE 0x0200
121 #define XT_D_PRINTING 0x0400
122 #define XT_D_COOKIE 0x0800
123 #define XT_D_KEYBINDING 0x1000
124 u_int32_t swm_debug
= 0
140 #define DPRINTF(x...)
141 #define DNPRINTF(n,x...)
144 #define LENGTH(x) (sizeof x / sizeof x[0])
145 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
146 ~(GDK_BUTTON1_MASK) & \
147 ~(GDK_BUTTON2_MASK) & \
148 ~(GDK_BUTTON3_MASK) & \
149 ~(GDK_BUTTON4_MASK) & \
161 TAILQ_ENTRY(tab
) entry
;
163 GtkWidget
*tab_content
;
166 GtkWidget
*uri_entry
;
167 GtkWidget
*search_entry
;
169 GtkWidget
*browser_win
;
170 GtkWidget
*statusbar
;
176 GtkWidget
*js_toggle
;
180 WebKitWebHistoryItem
*item
;
181 WebKitWebBackForwardList
*bfl
;
184 WebKitNetworkRequest
*icon_request
;
185 WebKitDownload
*icon_download
;
186 GdkPixbuf
*icon_pixbuf
;
187 gchar
*icon_dest_uri
;
189 /* adjustments for browser */
192 GtkAdjustment
*adjust_h
;
193 GtkAdjustment
*adjust_v
;
199 int xtp_meaning
; /* identifies dls/favorites */
204 #define XT_HINT_NONE (0)
205 #define XT_HINT_NUMERICAL (1)
206 #define XT_HINT_ALPHANUM (2)
215 WebKitWebSettings
*settings
;
219 TAILQ_HEAD(tab_list
, tab
);
222 RB_ENTRY(history
) entry
;
226 RB_HEAD(history_list
, history
);
229 RB_ENTRY(download
) entry
;
231 WebKitDownload
*download
;
234 RB_HEAD(download_list
, download
);
237 RB_ENTRY(domain
) entry
;
239 int handy
; /* app use */
241 RB_HEAD(domain_list
, domain
);
244 TAILQ_ENTRY(undo
) entry
;
247 int back
; /* Keeps track of how many back
248 * history items there are. */
250 TAILQ_HEAD(undo_tailq
, undo
);
252 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
253 int next_download_id
= 1;
261 #define XT_NAME ("XXXTerm")
262 #define XT_DIR (".xxxterm")
263 #define XT_CACHE_DIR ("cache")
264 #define XT_CERT_DIR ("certs/")
265 #define XT_SESSIONS_DIR ("sessions/")
266 #define XT_CONF_FILE ("xxxterm.conf")
267 #define XT_FAVS_FILE ("favorites")
268 #define XT_SAVED_TABS_FILE ("main_session")
269 #define XT_RESTART_TABS_FILE ("restart_tabs")
270 #define XT_SOCKET_FILE ("socket")
271 #define XT_HISTORY_FILE ("history")
272 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
273 #define XT_CB_HANDLED (TRUE)
274 #define XT_CB_PASSTHROUGH (FALSE)
275 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
276 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
277 #define XT_DLMAN_REFRESH "10"
278 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
279 "td {overflow: hidden;" \
280 " padding: 2px 2px 2px 2px;" \
281 " border: 1px solid black}\n" \
282 "tr:hover {background: #ffff99 ;}\n" \
283 "th {background-color: #cccccc;" \
284 " border: 1px solid black}" \
285 "table {border-spacing: 0; " \
287 " border: 1px black solid;}\n" \
289 " border: 1px solid black;" \
295 " background: green;}" \
297 " font-size: small;" \
298 " text-align: center;}" \
300 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
301 #define XT_MAX_UNDO_CLOSE_TAB (32)
302 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
303 #define XT_PRINT_EXTRA_MARGIN 10
306 #define SZ_KB ((uint64_t) 1024)
307 #define SZ_MB (SZ_KB * SZ_KB)
308 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
309 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
312 * xxxterm "protocol" (xtp)
313 * We use this for managing stuff like downloads and favorites. They
314 * make magical HTML pages in memory which have xxxt:// links in order
315 * to communicate with xxxterm's internals. These links take the format:
316 * xxxt://class/session_key/action/arg
318 * Don't begin xtp class/actions as 0. atoi returns that on error.
320 * Typically we have not put addition of items in this framework, as
321 * adding items is either done via an ex-command or via a keybinding instead.
324 #define XT_XTP_STR "xxxt://"
326 /* XTP classes (xxxt://<class>) */
327 #define XT_XTP_DL 1 /* downloads */
328 #define XT_XTP_HL 2 /* history */
329 #define XT_XTP_CL 3 /* cookies */
330 #define XT_XTP_FL 4 /* favorites */
332 /* XTP download actions */
333 #define XT_XTP_DL_LIST 1
334 #define XT_XTP_DL_CANCEL 2
335 #define XT_XTP_DL_REMOVE 3
337 /* XTP history actions */
338 #define XT_XTP_HL_LIST 1
339 #define XT_XTP_HL_REMOVE 2
341 /* XTP cookie actions */
342 #define XT_XTP_CL_LIST 1
343 #define XT_XTP_CL_REMOVE 2
345 /* XTP cookie actions */
346 #define XT_XTP_FL_LIST 1
347 #define XT_XTP_FL_REMOVE 2
349 /* xtp tab meanings - identifies which tabs have xtp pages in */
350 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
351 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
352 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
353 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
354 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
357 #define XT_MOVE_INVALID (0)
358 #define XT_MOVE_DOWN (1)
359 #define XT_MOVE_UP (2)
360 #define XT_MOVE_BOTTOM (3)
361 #define XT_MOVE_TOP (4)
362 #define XT_MOVE_PAGEDOWN (5)
363 #define XT_MOVE_PAGEUP (6)
364 #define XT_MOVE_HALFDOWN (7)
365 #define XT_MOVE_HALFUP (8)
366 #define XT_MOVE_LEFT (9)
367 #define XT_MOVE_FARLEFT (10)
368 #define XT_MOVE_RIGHT (11)
369 #define XT_MOVE_FARRIGHT (12)
371 #define XT_TAB_LAST (-4)
372 #define XT_TAB_FIRST (-3)
373 #define XT_TAB_PREV (-2)
374 #define XT_TAB_NEXT (-1)
375 #define XT_TAB_INVALID (0)
376 #define XT_TAB_NEW (1)
377 #define XT_TAB_DELETE (2)
378 #define XT_TAB_DELQUIT (3)
379 #define XT_TAB_OPEN (4)
380 #define XT_TAB_UNDO_CLOSE (5)
381 #define XT_TAB_SHOW (6)
382 #define XT_TAB_HIDE (7)
384 #define XT_NAV_INVALID (0)
385 #define XT_NAV_BACK (1)
386 #define XT_NAV_FORWARD (2)
387 #define XT_NAV_RELOAD (3)
388 #define XT_NAV_RELOAD_CACHE (4)
390 #define XT_FOCUS_INVALID (0)
391 #define XT_FOCUS_URI (1)
392 #define XT_FOCUS_SEARCH (2)
394 #define XT_SEARCH_INVALID (0)
395 #define XT_SEARCH_NEXT (1)
396 #define XT_SEARCH_PREV (2)
398 #define XT_PASTE_CURRENT_TAB (0)
399 #define XT_PASTE_NEW_TAB (1)
401 #define XT_FONT_SET (0)
403 #define XT_URL_SHOW (1)
404 #define XT_URL_HIDE (2)
406 #define XT_STATUSBAR_SHOW (1)
407 #define XT_STATUSBAR_HIDE (2)
409 #define XT_WL_TOGGLE (1<<0)
410 #define XT_WL_ENABLE (1<<1)
411 #define XT_WL_DISABLE (1<<2)
412 #define XT_WL_FQDN (1<<3) /* default */
413 #define XT_WL_TOPLEVEL (1<<4)
415 #define XT_CMD_OPEN (0)
416 #define XT_CMD_OPEN_CURRENT (1)
417 #define XT_CMD_TABNEW (2)
418 #define XT_CMD_TABNEW_CURRENT (3)
420 #define XT_STATUS_NOTHING (0)
421 #define XT_STATUS_LINK (1)
422 #define XT_STATUS_URI (2)
423 #define XT_STATUS_LOADING (3)
425 #define XT_SES_DONOTHING (0)
426 #define XT_SES_CLOSETABS (1)
428 #define XT_COOKIE_NORMAL (0)
429 #define XT_COOKIE_WHITELIST (1)
436 TAILQ_ENTRY(mime_type
) entry
;
438 TAILQ_HEAD(mime_type_list
, mime_type
);
444 TAILQ_ENTRY(alias
) entry
;
446 TAILQ_HEAD(alias_list
, alias
);
448 /* settings that require restart */
449 int tabless
= 0; /* allow only 1 tab */
450 int enable_socket
= 0;
451 int single_instance
= 0; /* only allow one xxxterm to run */
452 int fancy_bar
= 1; /* fancy toolbar */
453 int browser_mode
= XT_COOKIE_NORMAL
;
455 /* runtime settings */
456 int show_tabs
= 1; /* show tabs on notebook */
457 int show_url
= 1; /* show url toolbar on notebook */
458 int show_statusbar
= 0; /* vimperator style status bar */
459 int ctrl_click_focus
= 0; /* ctrl click gets focus */
460 int cookies_enabled
= 1; /* enable cookies */
461 int read_only_cookies
= 0; /* enable to not write cookies */
462 int enable_scripts
= 1;
463 int enable_plugins
= 0;
464 int default_font_size
= 12;
465 int window_height
= 768;
466 int window_width
= 1024;
467 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
468 unsigned refresh_interval
= 10; /* download refresh interval */
469 int enable_cookie_whitelist
= 0;
470 int enable_js_whitelist
= 0;
471 time_t session_timeout
= 3600; /* cookie session timeout */
472 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
473 char *ssl_ca_file
= NULL
;
474 char *resource_dir
= NULL
;
475 gboolean ssl_strict_certs
= FALSE
;
476 int append_next
= 1; /* append tab after current tab */
478 char *search_string
= NULL
;
479 char *http_proxy
= NULL
;
480 char download_dir
[PATH_MAX
];
481 char runtime_settings
[PATH_MAX
]; /* override of settings */
482 int allow_volatile_cookies
= 0;
483 int save_global_history
= 0; /* save global history to disk */
484 char *user_agent
= NULL
;
485 int save_rejected_cookies
= 0;
486 time_t session_autosave
= 0;
487 int guess_search
= 0;
491 int set_download_dir(struct settings
*, char *);
492 int set_work_dir(struct settings
*, char *);
493 int set_runtime_dir(struct settings
*, char *);
494 int set_browser_mode(struct settings
*, char *);
495 int set_cookie_policy(struct settings
*, char *);
496 int add_alias(struct settings
*, char *);
497 int add_mime_type(struct settings
*, char *);
498 int add_cookie_wl(struct settings
*, char *);
499 int add_js_wl(struct settings
*, char *);
500 int add_kb(struct settings
*, char *);
501 void button_set_stockid(GtkWidget
*, char *);
502 GtkWidget
* create_button(char *, char *, int);
504 char *get_browser_mode(struct settings
*);
505 char *get_cookie_policy(struct settings
*);
507 char *get_download_dir(struct settings
*);
508 char *get_work_dir(struct settings
*);
509 char *get_runtime_dir(struct settings
*);
511 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
512 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
513 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
514 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
515 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
518 int (*set
)(struct settings
*, char *);
519 char *(*get
)(struct settings
*);
520 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
523 struct special s_browser_mode
= {
529 struct special s_cookie
= {
535 struct special s_alias
= {
541 struct special s_mime
= {
547 struct special s_js
= {
553 struct special s_kb
= {
559 struct special s_cookie_wl
= {
565 struct special s_download_dir
= {
571 struct special s_work_dir
= {
580 #define XT_S_INVALID (0)
584 #define XT_SF_RESTART (1<<0)
585 #define XT_SF_RUNTIME (1<<1)
590 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
591 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
592 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
593 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
594 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
595 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
596 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
597 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
598 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
599 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
600 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
601 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
602 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
603 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
604 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
605 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
606 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
607 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
608 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
609 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
610 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
611 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
612 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
613 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
614 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
615 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
616 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
617 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
618 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
619 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
620 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
621 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
622 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
623 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
624 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
625 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
627 /* runtime settings */
628 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
629 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
630 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
631 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
632 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
635 int about(struct tab
*, struct karg
*);
636 int blank(struct tab
*, struct karg
*);
637 int cookie_show_wl(struct tab
*, struct karg
*);
638 int js_show_wl(struct tab
*, struct karg
*);
639 int help(struct tab
*, struct karg
*);
640 int set(struct tab
*, struct karg
*);
641 int stats(struct tab
*, struct karg
*);
642 int xtp_page_cl(struct tab
*, struct karg
*);
643 int xtp_page_dl(struct tab
*, struct karg
*);
644 int xtp_page_fl(struct tab
*, struct karg
*);
645 int xtp_page_hl(struct tab
*, struct karg
*);
647 #define XT_URI_ABOUT ("about:")
648 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
649 #define XT_URI_ABOUT_ABOUT ("about")
650 #define XT_URI_ABOUT_BLANK ("blank")
651 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
652 #define XT_URI_ABOUT_COOKIEWL ("cookiewl") /* NOT YET */
653 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
654 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
655 #define XT_URI_ABOUT_FAVORITES ("favorites")
656 #define XT_URI_ABOUT_HELP ("help")
657 #define XT_URI_ABOUT_HISTORY ("history")
658 #define XT_URI_ABOUT_JSWL ("jswl") /* NOT YET */
659 #define XT_URI_ABOUT_SET ("set")
660 #define XT_URI_ABOUT_STATS ("stats")
664 int (*func
)(struct tab
*, struct karg
*);
666 { XT_URI_ABOUT_ABOUT
, about
},
667 { XT_URI_ABOUT_BLANK
, blank
},
668 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
669 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
670 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
671 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
672 { XT_URI_ABOUT_HELP
, help
},
673 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
674 { XT_URI_ABOUT_JSWL
, js_show_wl
},
675 { XT_URI_ABOUT_SET
, set
},
676 { XT_URI_ABOUT_STATS
, stats
},
680 extern char *__progname
;
683 GtkWidget
*main_window
;
684 GtkNotebook
*notebook
;
685 GtkWidget
*arrow
, *abtn
;
686 struct tab_list tabs
;
687 struct history_list hl
;
688 struct download_list downloads
;
689 struct domain_list c_wl
;
690 struct domain_list js_wl
;
691 struct undo_tailq undos
;
692 struct keybinding_list kbl
;
694 int updating_dl_tabs
= 0;
695 int updating_hl_tabs
= 0;
696 int updating_cl_tabs
= 0;
697 int updating_fl_tabs
= 0;
699 uint64_t blocked_cookies
= 0;
700 char named_session
[PATH_MAX
];
701 void update_favicon(struct tab
*);
702 int icon_size_map(int);
707 int saved_errno
, status
;
712 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
716 if (errno
!= ECHILD
) {
718 clog_warn("sigchild: waitpid:");
724 if (WIFEXITED(status
)) {
725 if (WEXITSTATUS(status
) != 0) {
727 clog_warnx("sigchild: child exit status: %d",
728 WEXITSTATUS(status));
733 clog_warnx("sigchild: child is terminated abnormally");
742 _load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
746 /* we set this to indicate we want to manually do navaction */
748 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
749 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
752 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, title
);
753 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
759 load_webkit_string(struct tab
*t
, const char *str
)
761 /* we set this to indicate we want to manually do navaction */
762 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
763 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
767 set_status(struct tab
*t
, gchar
*s
, int status
)
775 case XT_STATUS_LOADING
:
776 type
= g_strdup_printf("Loading: %s", s
);
780 type
= g_strdup_printf("Link: %s", s
);
782 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
786 type
= g_strdup_printf("%s", s
);
788 t
->status
= g_strdup(type
);
792 t
->status
= g_strdup(s
);
794 case XT_STATUS_NOTHING
:
799 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
805 hide_oops(struct tab
*t
)
807 gtk_widget_hide(t
->oops
);
811 hide_cmd(struct tab
*t
)
813 gtk_widget_hide(t
->cmd
);
817 show_cmd(struct tab
*t
)
819 gtk_widget_hide(t
->oops
);
820 gtk_widget_show(t
->cmd
);
824 show_oops(struct tab
*t
, const char *fmt
, ...)
833 if (vasprintf(&msg
, fmt
, ap
) == -1)
834 errx(1, "show_oops failed");
837 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
838 gtk_widget_hide(t
->cmd
);
839 gtk_widget_show(t
->oops
);
842 /* XXX collapse with show_oops */
844 show_oops_s(const char *fmt
, ...)
848 struct tab
*ti
, *t
= NULL
;
853 TAILQ_FOREACH(ti
, &tabs
, entry
)
854 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
862 if (vasprintf(&msg
, fmt
, ap
) == -1)
863 errx(1, "show_oops_s failed");
866 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
867 gtk_widget_hide(t
->cmd
);
868 gtk_widget_show(t
->oops
);
872 get_as_string(struct settings
*s
)
883 warnx("get_as_string skip %s\n", s
->name
);
884 } else if (s
->type
== XT_S_INT
)
885 r
= g_strdup_printf("%d", *s
->ival
);
886 else if (s
->type
== XT_S_STR
)
887 r
= g_strdup(*s
->sval
);
889 r
= g_strdup_printf("INVALID TYPE");
895 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
900 for (i
= 0; i
< LENGTH(rs
); i
++) {
901 if (rs
[i
].s
&& rs
[i
].s
->walk
)
902 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
904 s
= get_as_string(&rs
[i
]);
905 cb(&rs
[i
], s
, cb_args
);
912 set_browser_mode(struct settings
*s
, char *val
)
914 if (!strcmp(val
, "whitelist")) {
915 browser_mode
= XT_COOKIE_WHITELIST
;
916 allow_volatile_cookies
= 0;
917 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
919 enable_cookie_whitelist
= 1;
920 read_only_cookies
= 0;
921 save_rejected_cookies
= 0;
922 session_timeout
= 3600;
924 enable_js_whitelist
= 1;
925 } else if (!strcmp(val
, "normal")) {
926 browser_mode
= XT_COOKIE_NORMAL
;
927 allow_volatile_cookies
= 0;
928 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
930 enable_cookie_whitelist
= 0;
931 read_only_cookies
= 0;
932 save_rejected_cookies
= 0;
933 session_timeout
= 3600;
935 enable_js_whitelist
= 0;
943 get_browser_mode(struct settings
*s
)
947 if (browser_mode
== XT_COOKIE_WHITELIST
)
948 r
= g_strdup("whitelist");
949 else if (browser_mode
== XT_COOKIE_NORMAL
)
950 r
= g_strdup("normal");
958 set_cookie_policy(struct settings
*s
, char *val
)
960 if (!strcmp(val
, "no3rdparty"))
961 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
962 else if (!strcmp(val
, "accept"))
963 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
964 else if (!strcmp(val
, "reject"))
965 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
973 get_cookie_policy(struct settings
*s
)
977 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
978 r
= g_strdup("no3rdparty");
979 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
980 r
= g_strdup("accept");
981 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
982 r
= g_strdup("reject");
990 get_download_dir(struct settings
*s
)
992 if (download_dir
[0] == '\0')
994 return (g_strdup(download_dir
));
998 set_download_dir(struct settings
*s
, char *val
)
1001 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1002 pwd
->pw_dir
, &val
[1]);
1004 strlcpy(download_dir
, val
, sizeof download_dir
);
1011 * We use these to prevent people putting xxxt:// URLs on
1012 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1014 #define XT_XTP_SES_KEY_SZ 8
1015 #define XT_XTP_SES_KEY_HEX_FMT \
1016 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1017 char *dl_session_key
; /* downloads */
1018 char *hl_session_key
; /* history list */
1019 char *cl_session_key
; /* cookie list */
1020 char *fl_session_key
; /* favorites list */
1022 char work_dir
[PATH_MAX
];
1023 char certs_dir
[PATH_MAX
];
1024 char cache_dir
[PATH_MAX
];
1025 char sessions_dir
[PATH_MAX
];
1026 char cookie_file
[PATH_MAX
];
1027 SoupURI
*proxy_uri
= NULL
;
1028 SoupSession
*session
;
1029 SoupCookieJar
*s_cookiejar
;
1030 SoupCookieJar
*p_cookiejar
;
1031 char rc_fname
[PATH_MAX
];
1033 struct mime_type_list mtl
;
1034 struct alias_list aliases
;
1037 void create_new_tab(char *, struct undo
*, int);
1038 void delete_tab(struct tab
*);
1039 void adjustfont_webkit(struct tab
*, int);
1040 int run_script(struct tab
*, char *);
1041 int download_rb_cmp(struct download
*, struct download
*);
1044 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1046 return (strcmp(h1
->uri
, h2
->uri
));
1048 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1051 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1053 return (strcmp(d1
->d
, d2
->d
));
1055 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1058 get_work_dir(struct settings
*s
)
1060 if (work_dir
[0] == '\0')
1062 return (g_strdup(work_dir
));
1066 set_work_dir(struct settings
*s
, char *val
)
1069 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1070 pwd
->pw_dir
, &val
[1]);
1072 strlcpy(work_dir
, val
, sizeof work_dir
);
1078 * generate a session key to secure xtp commands.
1079 * pass in a ptr to the key in question and it will
1080 * be modified in place.
1083 generate_xtp_session_key(char **key
)
1085 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1091 /* make a new one */
1092 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1093 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1094 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1095 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1097 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1101 * validate a xtp session key.
1105 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1107 if (strcmp(trusted
, untrusted
) != 0) {
1108 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1117 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1119 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1121 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1123 struct valid_url_types
{
1134 valid_url_type(char *url
)
1138 for (i
= 0; i
< LENGTH(vut
); i
++)
1139 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1146 print_cookie(char *msg
, SoupCookie
*c
)
1152 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1153 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1154 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1155 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1156 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1157 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1158 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1159 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1160 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1161 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1165 walk_alias(struct settings
*s
,
1166 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1171 if (s
== NULL
|| cb
== NULL
) {
1172 show_oops_s("walk_alias invalid parameters");
1176 TAILQ_FOREACH(a
, &aliases
, entry
) {
1177 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1178 cb(s
, str
, cb_args
);
1184 match_alias(char *url_in
)
1188 char *url_out
= NULL
, *search
, *enc_arg
;
1190 search
= g_strdup(url_in
);
1192 if (strsep(&arg
, " \t") == NULL
) {
1193 show_oops_s("match_alias: NULL URL");
1197 TAILQ_FOREACH(a
, &aliases
, entry
) {
1198 if (!strcmp(search
, a
->a_name
))
1203 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1206 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1207 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1210 url_out
= g_strdup(a
->a_uri
);
1218 guess_url_type(char *url_in
)
1221 char *url_out
= NULL
, *enc_search
= NULL
;
1223 url_out
= match_alias(url_in
);
1224 if (url_out
!= NULL
)
1229 * If there is no dot nor slash in the string and it isn't a
1230 * path to a local file and doesn't resolves to an IP, assume
1231 * that the user wants to search for the string.
1234 if (strchr(url_in
, '.') == NULL
&&
1235 strchr(url_in
, '/') == NULL
&&
1236 stat(url_in
, &sb
) != 0 &&
1237 gethostbyname(url_in
) == NULL
) {
1239 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1240 url_out
= g_strdup_printf(search_string
, enc_search
);
1246 /* XXX not sure about this heuristic */
1247 if (stat(url_in
, &sb
) == 0)
1248 url_out
= g_strdup_printf("file://%s", url_in
);
1250 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1252 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1258 load_uri(struct tab
*t
, gchar
*uri
)
1261 gchar
*newuri
= NULL
;
1267 /* Strip leading spaces. */
1268 while(*uri
&& isspace(*uri
))
1271 if (strlen(uri
) == 0) {
1276 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1277 for (i
= 0; i
< LENGTH(about_list
); i
++)
1278 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1279 bzero(&args
, sizeof args
);
1280 about_list
[i
].func(t
, &args
);
1283 show_oops(t
, "invalid about page");
1287 if (valid_url_type(uri
)) {
1288 newuri
= guess_url_type(uri
);
1292 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1293 webkit_web_view_load_uri(t
->wv
, uri
);
1300 get_uri(WebKitWebView
*wv
)
1302 WebKitWebFrame
*frame
;
1305 frame
= webkit_web_view_get_main_frame(wv
);
1306 uri
= webkit_web_frame_get_uri(frame
);
1308 if (uri
&& strlen(uri
) > 0)
1315 add_alias(struct settings
*s
, char *line
)
1318 struct alias
*a
= NULL
;
1320 if (s
== NULL
|| line
== NULL
) {
1321 show_oops_s("add_alias invalid parameters");
1326 a
= g_malloc(sizeof(*a
));
1328 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1329 show_oops_s("add_alias: incomplete alias definition");
1332 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1333 show_oops_s("add_alias: invalid alias definition");
1337 a
->a_name
= g_strdup(alias
);
1338 a
->a_uri
= g_strdup(l
);
1340 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1342 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1352 add_mime_type(struct settings
*s
, char *line
)
1356 struct mime_type
*m
= NULL
;
1358 /* XXX this could be smarter */
1361 show_oops_s("add_mime_type invalid parameters");
1366 m
= g_malloc(sizeof(*m
));
1368 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1369 show_oops_s("add_mime_type: invalid mime_type");
1372 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1373 mime_type
[strlen(mime_type
) - 1] = '\0';
1378 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1379 show_oops_s("add_mime_type: invalid mime_type");
1383 m
->mt_type
= g_strdup(mime_type
);
1384 m
->mt_action
= g_strdup(l
);
1386 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1387 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1389 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1399 find_mime_type(char *mime_type
)
1401 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1403 TAILQ_FOREACH(m
, &mtl
, entry
) {
1404 if (m
->mt_default
&&
1405 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1408 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1421 walk_mime_type(struct settings
*s
,
1422 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1424 struct mime_type
*m
;
1427 if (s
== NULL
|| cb
== NULL
)
1428 show_oops_s("walk_mime_type invalid parameters");
1430 TAILQ_FOREACH(m
, &mtl
, entry
) {
1431 str
= g_strdup_printf("%s%s --> %s",
1433 m
->mt_default
? "*" : "",
1435 cb(s
, str
, cb_args
);
1441 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1446 if (str
== NULL
|| wl
== NULL
)
1448 if (strlen(str
) < 2)
1451 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1453 /* treat *.moo.com the same as .moo.com */
1454 if (str
[0] == '*' && str
[1] == '.')
1456 else if (str
[0] == '.')
1461 d
= g_malloc(sizeof *d
);
1463 d
->d
= g_strdup_printf(".%s", str
);
1465 d
->d
= g_strdup(str
);
1468 if (RB_INSERT(domain_list
, wl
, d
))
1471 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1482 add_cookie_wl(struct settings
*s
, char *entry
)
1484 wl_add(entry
, &c_wl
, 1);
1489 walk_cookie_wl(struct settings
*s
,
1490 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1494 if (s
== NULL
|| cb
== NULL
) {
1495 show_oops_s("walk_cookie_wl invalid parameters");
1499 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1500 cb(s
, d
->d
, cb_args
);
1504 walk_js_wl(struct settings
*s
,
1505 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1509 if (s
== NULL
|| cb
== NULL
) {
1510 show_oops_s("walk_js_wl invalid parameters");
1514 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1515 cb(s
, d
->d
, cb_args
);
1519 add_js_wl(struct settings
*s
, char *entry
)
1521 wl_add(entry
, &js_wl
, 1 /* persistent */);
1526 wl_find(const gchar
*search
, struct domain_list
*wl
)
1529 struct domain
*d
= NULL
, dfind
;
1532 if (search
== NULL
|| wl
== NULL
)
1534 if (strlen(search
) < 2)
1537 if (search
[0] != '.')
1538 s
= g_strdup_printf(".%s", search
);
1540 s
= g_strdup(search
);
1542 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1545 d
= RB_FIND(domain_list
, wl
, &dfind
);
1559 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1565 if (s
== NULL
|| wl
== NULL
)
1568 if (!strncmp(s
, "http://", strlen("http://")))
1569 s
= &s
[strlen("http://")];
1570 else if (!strncmp(s
, "https://", strlen("https://")))
1571 s
= &s
[strlen("https://")];
1576 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1577 /* chop string at first slash */
1578 if (s
[i
] == '/' || s
[i
] == '\0') {
1581 r
= wl_find(ss
, wl
);
1590 get_toplevel_domain(char *domain
)
1597 if (strlen(domain
) < 2)
1600 s
= &domain
[strlen(domain
) - 1];
1601 while (s
!= domain
) {
1617 settings_add(char *var
, char *val
)
1623 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1624 if (strcmp(var
, rs
[i
].name
))
1628 if (rs
[i
].s
->set(&rs
[i
], val
))
1629 errx(1, "invalid value for %s", var
);
1633 switch (rs
[i
].type
) {
1642 errx(1, "invalid sval for %s",
1651 errx(1, "invalid type for %s", var
);
1660 config_parse(char *filename
, int runtime
)
1663 char *line
, *cp
, *var
, *val
;
1664 size_t len
, lineno
= 0;
1666 char file
[PATH_MAX
];
1669 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1671 if (filename
== NULL
)
1674 if (runtime
&& runtime_settings
[0] != '\0') {
1675 snprintf(file
, sizeof file
, "%s/%s",
1676 work_dir
, runtime_settings
);
1677 if (stat(file
, &sb
)) {
1678 warnx("runtime file doesn't exist, creating it");
1679 if ((f
= fopen(file
, "w")) == NULL
)
1681 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1685 strlcpy(file
, filename
, sizeof file
);
1687 if ((config
= fopen(file
, "r")) == NULL
) {
1688 warn("config_parse: cannot open %s", filename
);
1693 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1694 if (feof(config
) || ferror(config
))
1698 cp
+= (long)strspn(cp
, WS
);
1699 if (cp
[0] == '\0') {
1705 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1706 errx(1, "invalid config file entry: %s", line
);
1708 cp
+= (long)strspn(cp
, WS
);
1710 if ((val
= strsep(&cp
, "\0")) == NULL
)
1713 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1714 handled
= settings_add(var
, val
);
1716 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1725 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1731 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1735 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1738 JSStringGetUTF8CString(jsref
, s
, l
);
1739 JSStringRelease(jsref
);
1745 disable_hints(struct tab
*t
)
1747 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1748 bzero(t
->hint_num
, sizeof t
->hint_num
);
1749 run_script(t
, "vimprobable_clear()");
1751 t
->hint_mode
= XT_HINT_NONE
;
1755 enable_hints(struct tab
*t
)
1757 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1758 run_script(t
, "vimprobable_show_hints()");
1760 t
->hint_mode
= XT_HINT_NONE
;
1763 #define XT_JS_OPEN ("open;")
1764 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1765 #define XT_JS_FIRE ("fire;")
1766 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1767 #define XT_JS_FOUND ("found;")
1768 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1771 run_script(struct tab
*t
, char *s
)
1773 JSGlobalContextRef ctx
;
1774 WebKitWebFrame
*frame
;
1776 JSValueRef val
, exception
;
1779 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1780 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1782 frame
= webkit_web_view_get_main_frame(t
->wv
);
1783 ctx
= webkit_web_frame_get_global_context(frame
);
1785 str
= JSStringCreateWithUTF8CString(s
);
1786 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1787 NULL
, 0, &exception
);
1788 JSStringRelease(str
);
1790 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1792 es
= js_ref_to_string(ctx
, exception
);
1793 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1797 es
= js_ref_to_string(ctx
, val
);
1798 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1800 /* handle return value right here */
1801 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1803 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1806 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1807 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1808 &es
[XT_JS_FIRE_LEN
]);
1813 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1814 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1825 hint(struct tab
*t
, struct karg
*args
)
1828 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1830 if (t
->hints_on
== 0)
1839 * Doesn't work fully, due to the following bug:
1840 * https://bugs.webkit.org/show_bug.cgi?id=51747
1843 restore_global_history(void)
1845 char file
[PATH_MAX
];
1851 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1853 if ((f
= fopen(file
, "r")) == NULL
) {
1854 warnx("%s: fopen", __func__
);
1859 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1860 if (feof(f
) || ferror(f
))
1863 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1864 if (feof(f
) || ferror(f
)) {
1866 warnx("%s: broken history file\n", __func__
);
1870 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1871 webkit_web_history_item_new_with_data(uri
, title
);
1872 h
= g_malloc(sizeof(struct history
));
1873 h
->uri
= g_strdup(uri
);
1874 h
->title
= g_strdup(title
);
1875 RB_INSERT(history_list
, &hl
, h
);
1877 warnx("%s: failed to restore history\n", __func__
);
1893 save_global_history_to_disk(struct tab
*t
)
1895 char file
[PATH_MAX
];
1899 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1901 if ((f
= fopen(file
, "w")) == NULL
) {
1902 show_oops(t
, "%s: global history file: %s",
1903 __func__
, strerror(errno
));
1907 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1908 if (h
->uri
&& h
->title
)
1909 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1918 quit(struct tab
*t
, struct karg
*args
)
1920 if (save_global_history
)
1921 save_global_history_to_disk(t
);
1929 open_tabs(struct tab
*t
, struct karg
*a
)
1931 char file
[PATH_MAX
];
1935 struct tab
*ti
, *tt
;
1940 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1941 if ((f
= fopen(file
, "r")) == NULL
)
1944 ti
= TAILQ_LAST(&tabs
, tab_list
);
1947 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1948 if (feof(f
) || ferror(f
))
1951 /* retrieve session name */
1952 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
1953 strlcpy(named_session
,
1954 &uri
[strlen(XT_SAVE_SESSION_ID
)],
1955 sizeof named_session
);
1959 if (uri
&& strlen(uri
))
1960 create_new_tab(uri
, NULL
, 1);
1966 /* close open tabs */
1967 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
1969 tt
= TAILQ_FIRST(&tabs
);
1988 restore_saved_tabs(void)
1990 char file
[PATH_MAX
];
1991 int unlink_file
= 0;
1996 snprintf(file
, sizeof file
, "%s/%s",
1997 sessions_dir
, XT_RESTART_TABS_FILE
);
1998 if (stat(file
, &sb
) == -1)
1999 a
.s
= XT_SAVED_TABS_FILE
;
2002 a
.s
= XT_RESTART_TABS_FILE
;
2005 a
.i
= XT_SES_DONOTHING
;
2006 rv
= open_tabs(NULL
, &a
);
2015 save_tabs(struct tab
*t
, struct karg
*a
)
2017 char file
[PATH_MAX
];
2022 const gchar
**arr
= NULL
;
2027 snprintf(file
, sizeof file
, "%s/%s",
2028 sessions_dir
, named_session
);
2030 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2032 if ((f
= fopen(file
, "w")) == NULL
) {
2033 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2037 /* save session name */
2038 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2040 /* save tabs, in the order they are arranged in the notebook */
2041 TAILQ_FOREACH(ti
, &tabs
, entry
)
2044 arr
= g_malloc0(len
* sizeof(gchar
*));
2046 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2047 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2048 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2051 for (i
= 0; i
< len
; i
++)
2053 fprintf(f
, "%s\n", arr
[i
]);
2062 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2074 yank_uri(struct tab
*t
, struct karg
*args
)
2077 GtkClipboard
*clipboard
;
2079 if ((uri
= get_uri(t
->wv
)) == NULL
)
2082 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2083 gtk_clipboard_set_text(clipboard
, uri
, -1);
2094 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
2096 struct paste_args
*pap
;
2098 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
2101 pap
= (struct paste_args
*)data
;
2104 case XT_PASTE_CURRENT_TAB
:
2105 load_uri(pap
->t
, (gchar
*)text
);
2107 case XT_PASTE_NEW_TAB
:
2108 create_new_tab((gchar
*)text
, NULL
, 1);
2116 paste_uri(struct tab
*t
, struct karg
*args
)
2118 GtkClipboard
*clipboard
;
2119 struct paste_args
*pap
;
2121 pap
= g_malloc(sizeof(struct paste_args
));
2126 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2127 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
2133 find_domain(const gchar
*s
, int add_dot
)
2136 char *r
= NULL
, *ss
= NULL
;
2141 if (!strncmp(s
, "http://", strlen("http://")))
2142 s
= &s
[strlen("http://")];
2143 else if (!strncmp(s
, "https://", strlen("https://")))
2144 s
= &s
[strlen("https://")];
2150 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2151 /* chop string at first slash */
2152 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2155 r
= g_strdup_printf(".%s", ss
);
2166 toggle_cwl(struct tab
*t
, struct karg
*args
)
2170 char *dom
= NULL
, *dom_toggle
= NULL
;
2176 uri
= get_uri(t
->wv
);
2177 dom
= find_domain(uri
, 1);
2178 d
= wl_find(dom
, &c_wl
);
2185 if (args
->i
& XT_WL_TOGGLE
)
2187 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2189 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2192 if (args
->i
& XT_WL_TOPLEVEL
)
2193 dom_toggle
= get_toplevel_domain(dom
);
2198 /* enable cookies for domain */
2199 wl_add(dom_toggle
, &c_wl
, 0);
2201 /* disable cookies for domain */
2202 RB_REMOVE(domain_list
, &c_wl
, d
);
2204 webkit_web_view_reload(t
->wv
);
2211 toggle_js(struct tab
*t
, struct karg
*args
)
2216 char *dom
= NULL
, *dom_toggle
= NULL
;
2221 g_object_get(G_OBJECT(t
->settings
),
2222 "enable-scripts", &es
, (char *)NULL
);
2223 if (args
->i
& XT_WL_TOGGLE
)
2225 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2227 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2232 uri
= get_uri(t
->wv
);
2233 dom
= find_domain(uri
, 1);
2235 if (uri
== NULL
|| dom
== NULL
) {
2236 show_oops(t
, "Can't toggle domain in JavaScript white list");
2240 if (args
->i
& XT_WL_TOPLEVEL
)
2241 dom_toggle
= get_toplevel_domain(dom
);
2246 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2247 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2249 d
= wl_find(dom_toggle
, &js_wl
);
2251 RB_REMOVE(domain_list
, &js_wl
, d
);
2252 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2254 g_object_set(G_OBJECT(t
->settings
),
2255 "enable-scripts", es
, (char *)NULL
);
2256 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2257 webkit_web_view_reload(t
->wv
);
2265 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2269 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2274 toggle_src(struct tab
*t
, struct karg
*args
)
2281 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2282 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2283 webkit_web_view_reload(t
->wv
);
2289 focus_webview(struct tab
*t
)
2294 /* only grab focus if we are visible */
2295 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2296 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2300 focus(struct tab
*t
, struct karg
*args
)
2302 if (t
== NULL
|| args
== NULL
)
2308 if (args
->i
== XT_FOCUS_URI
)
2309 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2310 else if (args
->i
== XT_FOCUS_SEARCH
)
2311 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2317 stats(struct tab
*t
, struct karg
*args
)
2319 char *stats
, *s
, line
[64 * 1024];
2320 uint64_t line_count
= 0;
2324 show_oops_s("stats invalid parameters");
2327 if (save_rejected_cookies
) {
2328 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2330 s
= fgets(line
, sizeof line
, r_cookie_f
);
2331 if (s
== NULL
|| feof(r_cookie_f
) ||
2337 snprintf(line
, sizeof line
,
2338 "<br>Cookies blocked(*) total: %llu", line_count
);
2340 show_oops(t
, "Can't open blocked cookies file: %s",
2344 stats
= g_strdup_printf(XT_DOCTYPE
2347 "<title>Statistics</title>"
2349 "<h1>Statistics</h1>"
2351 "Cookies blocked(*) this session: %llu"
2353 "<p><small><b>*</b> results vary based on settings"
2359 _load_webkit_string(t
, stats
, XT_URI_ABOUT_STATS
);
2366 blank(struct tab
*t
, struct karg
*args
)
2369 show_oops_s("about invalid parameters");
2371 _load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2376 about(struct tab
*t
, struct karg
*args
)
2381 show_oops_s("about invalid parameters");
2383 about
= g_strdup_printf(XT_DOCTYPE
2386 "<title>About</title>"
2390 "<b>Version: %s</b><p>"
2393 "<li>Marco Peereboom <marco@peereboom.us></li>"
2394 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2395 "<li>Edd Barrett <vext01@gmail.com> </li>"
2396 "<li>Todd T. Fries <todd@fries.net> </li>"
2398 "Copyrights and licenses can be found on the XXXterm "
2399 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2405 _load_webkit_string(t
, about
, XT_URI_ABOUT_ABOUT
);
2412 help(struct tab
*t
, struct karg
*args
)
2417 show_oops_s("help invalid parameters");
2422 "<title>XXXterm</title>"
2423 "<meta http-equiv=\"REFRESH\" content=\"0;"
2424 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2427 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2428 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2429 "cgi-bin/man-cgi?xxxterm</a>"
2434 _load_webkit_string(t
, help
, XT_URI_ABOUT_HELP
);
2440 * update all favorite tabs apart from one. Pass NULL if
2441 * you want to update all.
2444 update_favorite_tabs(struct tab
*apart_from
)
2447 if (!updating_fl_tabs
) {
2448 updating_fl_tabs
= 1; /* stop infinite recursion */
2449 TAILQ_FOREACH(t
, &tabs
, entry
)
2450 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2451 && (t
!= apart_from
))
2452 xtp_page_fl(t
, NULL
);
2453 updating_fl_tabs
= 0;
2457 /* show a list of favorites (bookmarks) */
2459 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2461 char file
[PATH_MAX
];
2463 char *uri
= NULL
, *title
= NULL
;
2464 size_t len
, lineno
= 0;
2466 char *header
, *body
, *tmp
, *html
= NULL
;
2468 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2471 warn("%s: bad param", __func__
);
2473 /* mark tab as favorite list */
2474 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2476 /* new session key */
2477 if (!updating_fl_tabs
)
2478 generate_xtp_session_key(&fl_session_key
);
2480 /* open favorites */
2481 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2482 if ((f
= fopen(file
, "r")) == NULL
) {
2483 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2488 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2489 "<title>Favorites</title>\n"
2492 "<h1>Favorites</h1>\n",
2496 body
= g_strdup_printf("<div align='center'><table><tr>"
2497 "<th style='width: 4%%'>#</th><th>Link</th>"
2498 "<th style='width: 15%%'>Remove</th></tr>\n");
2501 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2502 if (feof(f
) || ferror(f
))
2510 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2511 if (feof(f
) || ferror(f
)) {
2512 show_oops(t
, "favorites file corrupt");
2518 body
= g_strdup_printf("%s<tr>"
2520 "<td><a href='%s'>%s</a></td>"
2521 "<td style='text-align: center'>"
2522 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2524 body
, i
, uri
, title
,
2525 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2537 /* if none, say so */
2540 body
= g_strdup_printf("%s<tr>"
2541 "<td colspan='3' style='text-align: center'>"
2542 "No favorites - To add one use the 'favadd' command."
2543 "</td></tr>", body
);
2554 html
= g_strdup_printf("%s%s</table></div></html>",
2556 _load_webkit_string(t
, html
, XT_URI_ABOUT_FAVORITES
);
2559 update_favorite_tabs(t
);
2572 getparams(char *cmd
, char *cmp
)
2577 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2578 rv
= cmd
+ strlen(cmp
);
2581 if (strlen(rv
) == 0)
2590 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2591 size_t cert_count
, char *title
)
2593 gnutls_datum_t cinfo
;
2594 char *tmp
, *header
, *body
, *footer
;
2597 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2598 footer
= g_strdup("</body></html>");
2599 body
= g_strdup("");
2601 for (i
= 0; i
< cert_count
; i
++) {
2602 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2607 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2608 body
, i
, cinfo
.data
);
2609 gnutls_free(cinfo
.data
);
2613 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2617 _load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2622 ca_cmd(struct tab
*t
, struct karg
*args
)
2625 int rv
= 1, certs
= 0, certs_read
;
2628 gnutls_x509_crt_t
*c
= NULL
;
2629 char *certs_buf
= NULL
, *s
;
2631 /* yeah yeah stat race */
2632 if (stat(ssl_ca_file
, &sb
)) {
2633 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2637 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2638 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2642 certs_buf
= g_malloc(sb
.st_size
+ 1);
2643 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2644 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2647 certs_buf
[sb
.st_size
] = '\0';
2650 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2652 s
+= strlen("BEGIN CERTIFICATE");
2655 bzero(&dt
, sizeof dt
);
2656 dt
.data
= certs_buf
;
2657 dt
.size
= sb
.st_size
;
2658 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2659 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2660 GNUTLS_X509_FMT_PEM
, 0);
2661 if (certs_read
<= 0) {
2662 show_oops(t
, "No cert(s) available");
2665 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2678 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2681 struct addrinfo hints
, *res
= NULL
, *ai
;
2685 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2688 su
= soup_uri_new(uri
);
2691 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2694 snprintf(port
, sizeof port
, "%d", su
->port
);
2695 bzero(&hints
, sizeof(struct addrinfo
));
2696 hints
.ai_flags
= AI_CANONNAME
;
2697 hints
.ai_family
= AF_UNSPEC
;
2698 hints
.ai_socktype
= SOCK_STREAM
;
2700 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2703 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2704 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2707 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2710 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2714 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2719 strlcpy(domain
, su
->host
, domain_sz
);
2730 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2733 gnutls_deinit(gsession
);
2735 gnutls_certificate_free_credentials(xcred
);
2741 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2742 gnutls_certificate_credentials_t
*xc
)
2744 gnutls_certificate_credentials_t xcred
;
2745 gnutls_session_t gsession
;
2748 if (gs
== NULL
|| xc
== NULL
)
2754 gnutls_certificate_allocate_credentials(&xcred
);
2755 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2756 GNUTLS_X509_FMT_PEM
);
2757 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2758 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2759 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2760 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2761 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2762 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2764 gnutls_error_is_fatal(rv
),
2765 gnutls_strerror_name(rv
));
2766 stop_tls(gsession
, xcred
);
2770 gnutls_credentials_type_t cred
;
2771 cred
= gnutls_auth_get_type(gsession
);
2772 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2773 stop_tls(gsession
, xcred
);
2785 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2789 const gnutls_datum_t
*cl
;
2790 gnutls_x509_crt_t
*all_certs
;
2793 if (certs
== NULL
|| cert_count
== NULL
)
2795 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2797 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2801 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2802 for (i
= 0; i
< len
; i
++) {
2803 gnutls_x509_crt_init(&all_certs
[i
]);
2804 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2805 GNUTLS_X509_FMT_PEM
< 0)) {
2819 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2823 for (i
= 0; i
< cert_count
; i
++)
2824 gnutls_x509_crt_deinit(certs
[i
]);
2829 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2830 size_t cert_count
, char *domain
)
2833 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2838 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2841 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2842 if ((f
= fopen(file
, "w")) == NULL
) {
2843 show_oops(t
, "Can't create cert file %s %s",
2844 file
, strerror(errno
));
2848 for (i
= 0; i
< cert_count
; i
++) {
2849 cert_buf_sz
= sizeof cert_buf
;
2850 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2851 cert_buf
, &cert_buf_sz
)) {
2852 show_oops(t
, "gnutls_x509_crt_export failed");
2855 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2856 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2861 /* not the best spot but oh well */
2862 gdk_color_parse("lightblue", &color
);
2863 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2864 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2865 gdk_color_parse("black", &color
);
2866 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2872 load_compare_cert(struct tab
*t
, struct karg
*args
)
2875 char domain
[8182], file
[PATH_MAX
];
2876 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2877 int s
= -1, rv
= 1, i
;
2881 gnutls_session_t gsession
;
2882 gnutls_x509_crt_t
*certs
;
2883 gnutls_certificate_credentials_t xcred
;
2888 if ((uri
= get_uri(t
->wv
)) == NULL
)
2891 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2895 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2896 show_oops(t
, "Start TLS failed");
2901 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2902 show_oops(t
, "Can't get connection certificates");
2906 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2907 if ((f
= fopen(file
, "r")) == NULL
)
2910 for (i
= 0; i
< cert_count
; i
++) {
2911 cert_buf_sz
= sizeof cert_buf
;
2912 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2913 cert_buf
, &cert_buf_sz
)) {
2916 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2917 rv
= -1; /* critical */
2920 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2921 rv
= -1; /* critical */
2930 free_connection_certs(certs
, cert_count
);
2932 /* we close the socket first for speed */
2935 stop_tls(gsession
, xcred
);
2941 cert_cmd(struct tab
*t
, struct karg
*args
)
2944 char *action
, domain
[8182];
2947 gnutls_session_t gsession
;
2948 gnutls_x509_crt_t
*certs
;
2949 gnutls_certificate_credentials_t xcred
;
2954 if ((action
= getparams(args
->s
, "cert")))
2959 if ((uri
= get_uri(t
->wv
)) == NULL
) {
2960 show_oops(t
, "Invalid URI");
2964 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2965 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2970 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2971 show_oops(t
, "Start TLS failed");
2976 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2977 show_oops(t
, "get_connection_certs failed");
2981 if (!strcmp(action
, "show"))
2982 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2983 else if (!strcmp(action
, "save"))
2984 save_certs(t
, certs
, cert_count
, domain
);
2986 show_oops(t
, "Invalid command: %s", action
);
2988 free_connection_certs(certs
, cert_count
);
2990 /* we close the socket first for speed */
2993 stop_tls(gsession
, xcred
);
2999 remove_cookie(int index
)
3005 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3007 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3009 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3013 print_cookie("remove cookie", c
);
3014 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3019 soup_cookies_free(cf
);
3025 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
3028 char *tmp
, *header
, *body
, *footer
;
3029 int p_js
= 0, s_js
= 0;
3031 /* we set this to indicate we want to manually do navaction */
3032 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3034 if (g_str_has_prefix(args
, "show a") ||
3035 !strcmp(args
, "show")) {
3039 } else if (g_str_has_prefix(args
, "show p")) {
3040 /* show persistent */
3042 } else if (g_str_has_prefix(args
, "show s")) {
3048 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
3050 footer
= g_strdup("</body></html>");
3051 body
= g_strdup("");
3056 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3058 RB_FOREACH(d
, domain_list
, wl
) {
3062 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3070 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3072 RB_FOREACH(d
, domain_list
, wl
) {
3076 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3081 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3086 _load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3088 _load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3094 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3096 char file
[PATH_MAX
];
3098 char *line
= NULL
, *lt
= NULL
;
3101 char *dom
= NULL
, *dom_save
= NULL
;
3108 if (t
== NULL
|| args
== NULL
)
3111 if (runtime_settings
[0] == '\0')
3114 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3115 if ((f
= fopen(file
, "r+")) == NULL
)
3118 uri
= get_uri(t
->wv
);
3119 dom
= find_domain(uri
, 1);
3120 if (uri
== NULL
|| dom
== NULL
) {
3121 show_oops(t
, "Can't add domain to %s white list",
3122 js
? "JavaScript" : "cookie");
3126 if (g_str_has_prefix(args
->s
, "save d")) {
3128 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3129 show_oops(t
, "invalid domain: %s", dom
);
3132 flags
= XT_WL_TOPLEVEL
;
3133 } else if (g_str_has_prefix(args
->s
, "save f") ||
3134 !strcmp(args
->s
, "save")) {
3139 show_oops(t
, "invalid command: %s", args
->s
);
3143 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3146 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3149 if (!strcmp(line
, lt
))
3155 fprintf(f
, "%s\n", lt
);
3160 d
= wl_find(dom_save
, &js_wl
);
3162 settings_add("js_wl", dom_save
);
3163 d
= wl_find(dom_save
, &js_wl
);
3167 d
= wl_find(dom_save
, &c_wl
);
3169 settings_add("cookie_wl", dom_save
);
3170 d
= wl_find(dom_save
, &c_wl
);
3174 /* find and add to persistent jar */
3175 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3176 for (;cf
; cf
= cf
->next
) {
3178 if (!strcmp(dom_save
, ci
->domain
) ||
3179 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3180 c
= soup_cookie_copy(ci
);
3181 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3184 soup_cookies_free(cf
);
3202 js_show_wl(struct tab
*t
, struct karg
*args
)
3204 wl_show(t
, "show all", "JavaScript White List", &js_wl
);
3210 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3212 wl_show(t
, "show all", "Cookie White List", &c_wl
);
3218 cookie_cmd(struct tab
*t
, struct karg
*args
)
3223 if ((cmd
= getparams(args
->s
, "cookie")))
3229 if (g_str_has_prefix(cmd
, "show")) {
3230 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
3231 } else if (g_str_has_prefix(cmd
, "save")) {
3234 } else if (g_str_has_prefix(cmd
, "toggle")) {
3236 if (g_str_has_prefix(cmd
, "toggle d"))
3237 a
.i
|= XT_WL_TOPLEVEL
;
3241 } else if (g_str_has_prefix(cmd
, "delete")) {
3242 show_oops(t
, "'cookie delete' currently unimplemented");
3244 show_oops(t
, "unknown cookie command: %s", cmd
);
3250 js_cmd(struct tab
*t
, struct karg
*args
)
3255 if ((cmd
= getparams(args
->s
, "js")))
3260 if (g_str_has_prefix(cmd
, "show")) {
3261 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
3262 } else if (g_str_has_prefix(cmd
, "save")) {
3265 } else if (g_str_has_prefix(cmd
, "toggle")) {
3267 if (g_str_has_prefix(cmd
, "toggle d"))
3268 a
.i
|= XT_WL_TOPLEVEL
;
3272 } else if (g_str_has_prefix(cmd
, "delete")) {
3273 show_oops(t
, "'js delete' currently unimplemented");
3275 show_oops(t
, "unknown js command: %s", cmd
);
3281 add_favorite(struct tab
*t
, struct karg
*args
)
3283 char file
[PATH_MAX
];
3286 size_t urilen
, linelen
;
3287 const gchar
*uri
, *title
;
3292 /* don't allow adding of xtp pages to favorites */
3293 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3294 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3298 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3299 if ((f
= fopen(file
, "r+")) == NULL
) {
3300 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3304 title
= webkit_web_view_get_title(t
->wv
);
3305 uri
= get_uri(t
->wv
);
3310 if (title
== NULL
|| uri
== NULL
) {
3311 show_oops(t
, "can't add page to favorites");
3315 urilen
= strlen(uri
);
3318 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3319 if (feof(f
) || ferror(f
))
3322 if (linelen
== urilen
&& !strcmp(line
, uri
))
3329 fprintf(f
, "\n%s\n%s", title
, uri
);
3335 update_favorite_tabs(NULL
);
3341 navaction(struct tab
*t
, struct karg
*args
)
3343 WebKitWebHistoryItem
*item
;
3345 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3346 t
->tab_id
, args
->i
);
3349 if (args
->i
== XT_NAV_BACK
)
3350 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3352 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3354 return (XT_CB_PASSTHROUGH
);
3355 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3357 return (XT_CB_PASSTHROUGH
);
3362 webkit_web_view_go_back(t
->wv
);
3364 case XT_NAV_FORWARD
:
3365 webkit_web_view_go_forward(t
->wv
);
3368 webkit_web_view_reload(t
->wv
);
3370 case XT_NAV_RELOAD_CACHE
:
3371 webkit_web_view_reload_bypass_cache(t
->wv
);
3374 return (XT_CB_PASSTHROUGH
);
3378 move(struct tab
*t
, struct karg
*args
)
3380 GtkAdjustment
*adjust
;
3381 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3386 case XT_MOVE_BOTTOM
:
3388 case XT_MOVE_PAGEDOWN
:
3389 case XT_MOVE_PAGEUP
:
3390 case XT_MOVE_HALFDOWN
:
3391 case XT_MOVE_HALFUP
:
3392 adjust
= t
->adjust_v
;
3395 adjust
= t
->adjust_h
;
3399 pos
= gtk_adjustment_get_value(adjust
);
3400 ps
= gtk_adjustment_get_page_size(adjust
);
3401 upper
= gtk_adjustment_get_upper(adjust
);
3402 lower
= gtk_adjustment_get_lower(adjust
);
3403 si
= gtk_adjustment_get_step_increment(adjust
);
3404 pi
= gtk_adjustment_get_page_increment(adjust
);
3407 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3408 "max %f si %f pi %f\n",
3409 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3410 pos
, ps
, upper
, lower
, max
, si
, pi
);
3416 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3421 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3423 case XT_MOVE_BOTTOM
:
3424 case XT_MOVE_FARRIGHT
:
3425 gtk_adjustment_set_value(adjust
, max
);
3428 case XT_MOVE_FARLEFT
:
3429 gtk_adjustment_set_value(adjust
, lower
);
3431 case XT_MOVE_PAGEDOWN
:
3433 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3435 case XT_MOVE_PAGEUP
:
3437 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3439 case XT_MOVE_HALFDOWN
:
3441 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3443 case XT_MOVE_HALFUP
:
3445 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3448 return (XT_CB_PASSTHROUGH
);
3451 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3453 return (XT_CB_HANDLED
);
3457 url_set_visibility(void)
3461 TAILQ_FOREACH(t
, &tabs
, entry
) {
3462 if (show_url
== 0) {
3463 gtk_widget_hide(t
->toolbar
);
3466 gtk_widget_show(t
->toolbar
);
3471 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3474 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3476 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3480 statusbar_set_visibility(void)
3484 TAILQ_FOREACH(t
, &tabs
, entry
) {
3485 if (show_statusbar
== 0) {
3486 gtk_widget_hide(t
->statusbar
);
3489 gtk_widget_show(t
->statusbar
);
3494 url_set(struct tab
*t
, int enable_url_entry
)
3499 show_url
= enable_url_entry
;
3501 if (enable_url_entry
) {
3502 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3503 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3504 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3506 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3507 GTK_ENTRY_ICON_PRIMARY
);
3509 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3510 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3511 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3512 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3518 fullscreen(struct tab
*t
, struct karg
*args
)
3520 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3523 return (XT_CB_PASSTHROUGH
);
3525 if (show_url
== 0) {
3533 url_set_visibility();
3534 notebook_tab_set_visibility(notebook
);
3536 return (XT_CB_HANDLED
);
3540 statusaction(struct tab
*t
, struct karg
*args
)
3542 int rv
= XT_CB_HANDLED
;
3544 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3547 return (XT_CB_PASSTHROUGH
);
3550 case XT_STATUSBAR_SHOW
:
3551 if (show_statusbar
== 0) {
3553 statusbar_set_visibility();
3556 case XT_STATUSBAR_HIDE
:
3557 if (show_statusbar
== 1) {
3559 statusbar_set_visibility();
3567 urlaction(struct tab
*t
, struct karg
*args
)
3569 int rv
= XT_CB_HANDLED
;
3571 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3574 return (XT_CB_PASSTHROUGH
);
3578 if (show_url
== 0) {
3580 url_set_visibility();
3584 if (show_url
== 1) {
3586 url_set_visibility();
3594 tabaction(struct tab
*t
, struct karg
*args
)
3596 int rv
= XT_CB_HANDLED
;
3600 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3603 return (XT_CB_PASSTHROUGH
);
3607 if ((url
= getparams(args
->s
, "tabnew")))
3608 create_new_tab(url
, NULL
, 1);
3610 create_new_tab(NULL
, NULL
, 1);
3615 case XT_TAB_DELQUIT
:
3616 if (gtk_notebook_get_n_pages(notebook
) > 1)
3622 if ((url
= getparams(args
->s
, "open")) ||
3623 ((url
= getparams(args
->s
, "op"))) ||
3624 ((url
= getparams(args
->s
, "o"))))
3627 rv
= XT_CB_PASSTHROUGH
;
3633 if (show_tabs
== 0) {
3635 notebook_tab_set_visibility(notebook
);
3639 if (show_tabs
== 1) {
3641 notebook_tab_set_visibility(notebook
);
3644 case XT_TAB_UNDO_CLOSE
:
3645 if (undo_count
== 0) {
3646 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3650 u
= TAILQ_FIRST(&undos
);
3651 create_new_tab(u
->uri
, u
, 1);
3653 TAILQ_REMOVE(&undos
, u
, entry
);
3655 /* u->history is freed in create_new_tab() */
3660 rv
= XT_CB_PASSTHROUGH
;
3674 resizetab(struct tab
*t
, struct karg
*args
)
3676 if (t
== NULL
|| args
== NULL
) {
3677 show_oops_s("resizetab invalid parameters");
3678 return (XT_CB_PASSTHROUGH
);
3681 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3682 t
->tab_id
, args
->i
);
3684 adjustfont_webkit(t
, args
->i
);
3686 return (XT_CB_HANDLED
);
3690 movetab(struct tab
*t
, struct karg
*args
)
3695 if (t
== NULL
|| args
== NULL
) {
3696 show_oops_s("movetab invalid parameters");
3697 return (XT_CB_PASSTHROUGH
);
3700 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3701 t
->tab_id
, args
->i
);
3703 if (args
->i
== XT_TAB_INVALID
)
3704 return (XT_CB_PASSTHROUGH
);
3706 if (args
->i
< XT_TAB_INVALID
) {
3707 /* next or previous tab */
3708 if (TAILQ_EMPTY(&tabs
))
3709 return (XT_CB_PASSTHROUGH
);
3713 /* if at the last page, loop around to the first */
3714 if (gtk_notebook_get_current_page(notebook
) ==
3715 gtk_notebook_get_n_pages(notebook
) - 1)
3716 gtk_notebook_set_current_page(notebook
, 0);
3718 gtk_notebook_next_page(notebook
);
3721 /* if at the first page, loop around to the last */
3722 if (gtk_notebook_current_page(notebook
) == 0)
3723 gtk_notebook_set_current_page(notebook
,
3724 gtk_notebook_get_n_pages(notebook
) - 1);
3726 gtk_notebook_prev_page(notebook
);
3729 gtk_notebook_set_current_page(notebook
, 0);
3732 gtk_notebook_set_current_page(notebook
, -1);
3735 return (XT_CB_PASSTHROUGH
);
3738 return (XT_CB_HANDLED
);
3743 if (t
->tab_id
== x
) {
3744 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3745 return (XT_CB_HANDLED
);
3748 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3749 if (tt
->tab_id
== x
) {
3750 gtk_notebook_set_current_page(notebook
, x
);
3751 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3757 return (XT_CB_HANDLED
);
3761 command(struct tab
*t
, struct karg
*args
)
3763 char *s
= NULL
, *ss
= NULL
;
3767 if (t
== NULL
|| args
== NULL
) {
3768 show_oops_s("command invalid parameters");
3769 return (XT_CB_PASSTHROUGH
);
3788 case XT_CMD_OPEN_CURRENT
:
3791 case XT_CMD_TABNEW_CURRENT
:
3792 if (!s
) /* FALL THROUGH? */
3794 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3795 ss
= g_strdup_printf("%s%s", s
, uri
);
3800 show_oops(t
, "command: invalid opcode %d", args
->i
);
3801 return (XT_CB_PASSTHROUGH
);
3804 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3806 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3807 gdk_color_parse("white", &color
);
3808 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3810 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3811 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3816 return (XT_CB_HANDLED
);
3820 * Return a new string with a download row (in html)
3821 * appended. Old string is freed.
3824 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3827 WebKitDownloadStatus stat
;
3828 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3830 char cur_sz
[FMT_SCALED_STRSIZE
];
3831 char tot_sz
[FMT_SCALED_STRSIZE
];
3834 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3836 /* All actions wil take this form:
3837 * xxxt://class/seskey
3839 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3840 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3842 stat
= webkit_download_get_status(dl
->download
);
3845 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3846 status_html
= g_strdup_printf("Finished");
3847 cmd_html
= g_strdup_printf(
3848 "<a href='%s%d/%d'>Remove</a>",
3849 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3851 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3852 /* gather size info */
3853 progress
= 100 * webkit_download_get_progress(dl
->download
);
3856 webkit_download_get_current_size(dl
->download
), cur_sz
);
3858 webkit_download_get_total_size(dl
->download
), tot_sz
);
3860 status_html
= g_strdup_printf(
3861 "<div style='width: 100%%' align='center'>"
3862 "<div class='progress-outer'>"
3863 "<div class='progress-inner' style='width: %.2f%%'>"
3864 "</div></div></div>"
3865 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3866 progress
, cur_sz
, tot_sz
, progress
);
3868 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3869 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3873 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3874 status_html
= g_strdup_printf("Cancelled");
3875 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3876 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3878 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3879 status_html
= g_strdup_printf("Error!");
3880 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3881 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3883 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3884 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3885 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3886 status_html
= g_strdup_printf("Starting");
3889 show_oops(t
, "%s: unknown download status", __func__
);
3892 new_html
= g_strdup_printf(
3893 "%s\n<tr><td>%s</td><td>%s</td>"
3894 "<td style='text-align:center'>%s</td></tr>\n",
3895 html
, basename(webkit_download_get_uri(dl
->download
)),
3896 status_html
, cmd_html
);
3900 g_free(status_html
);
3911 * update all download tabs apart from one. Pass NULL if
3912 * you want to update all.
3915 update_download_tabs(struct tab
*apart_from
)
3918 if (!updating_dl_tabs
) {
3919 updating_dl_tabs
= 1; /* stop infinite recursion */
3920 TAILQ_FOREACH(t
, &tabs
, entry
)
3921 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3922 && (t
!= apart_from
))
3923 xtp_page_dl(t
, NULL
);
3924 updating_dl_tabs
= 0;
3929 * update all cookie tabs apart from one. Pass NULL if
3930 * you want to update all.
3933 update_cookie_tabs(struct tab
*apart_from
)
3936 if (!updating_cl_tabs
) {
3937 updating_cl_tabs
= 1; /* stop infinite recursion */
3938 TAILQ_FOREACH(t
, &tabs
, entry
)
3939 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3940 && (t
!= apart_from
))
3941 xtp_page_cl(t
, NULL
);
3942 updating_cl_tabs
= 0;
3947 * update all history tabs apart from one. Pass NULL if
3948 * you want to update all.
3951 update_history_tabs(struct tab
*apart_from
)
3955 if (!updating_hl_tabs
) {
3956 updating_hl_tabs
= 1; /* stop infinite recursion */
3957 TAILQ_FOREACH(t
, &tabs
, entry
)
3958 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3959 && (t
!= apart_from
))
3960 xtp_page_hl(t
, NULL
);
3961 updating_hl_tabs
= 0;
3965 /* cookie management XTP page */
3967 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3969 char *header
, *body
, *footer
, *page
, *tmp
;
3970 int i
= 1; /* all ids start 1 */
3971 GSList
*sc
, *pc
, *pc_start
;
3973 char *type
, *table_headers
;
3974 char *last_domain
= strdup("");
3976 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3979 show_oops_s("%s invalid parameters", __func__
);
3982 /* mark this tab as cookie jar */
3983 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3985 /* Generate a new session key */
3986 if (!updating_cl_tabs
)
3987 generate_xtp_session_key(&cl_session_key
);
3990 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3991 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3992 "</head><body><h1>Cookie Jar</h1>\n");
3995 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
4002 "<th>HTTP<br />only</th>"
4003 "<th>Rm</th></tr>\n");
4005 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4006 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4010 for (; sc
; sc
= sc
->next
) {
4013 if (strcmp(last_domain
, c
->domain
) != 0) {
4016 last_domain
= strdup(c
->domain
);
4020 body
= g_strdup_printf("%s</table></div>"
4022 body
, c
->domain
, table_headers
);
4026 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4027 c
->domain
, table_headers
);
4032 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4033 if (soup_cookie_equal(pc
->data
, c
)) {
4034 type
= "Session + Persistent";
4039 body
= g_strdup_printf(
4041 "<td style='width: text-align: center'>%s</td>"
4042 "<td style='width: 1px'>%s</td>"
4043 "<td style='width=70%%;overflow: visible'>"
4044 " <textarea rows='4'>%s</textarea>"
4048 "<td style='width: 1px; text-align: center'>%d</td>"
4049 "<td style='width: 1px; text-align: center'>%d</td>"
4050 "<td style='width: 1px; text-align: center'>"
4051 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4058 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4073 soup_cookies_free(sc
);
4074 soup_cookies_free(pc
);
4076 /* small message if there are none */
4078 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4079 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4083 footer
= g_strdup_printf("</table></div></body></html>");
4085 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4090 g_free(table_headers
);
4091 g_free(last_domain
);
4093 _load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4094 update_cookie_tabs(t
);
4102 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4104 char *header
, *body
, *footer
, *page
, *tmp
;
4106 int i
= 1; /* all ids start 1 */
4108 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4111 show_oops_s("%s invalid parameters", __func__
);
4115 /* mark this tab as history manager */
4116 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4118 /* Generate a new session key */
4119 if (!updating_hl_tabs
)
4120 generate_xtp_session_key(&hl_session_key
);
4123 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4124 "<title>History</title>\n"
4127 "<h1>History</h1>\n",
4131 body
= g_strdup_printf("<div align='center'><table><tr>"
4132 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4134 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4136 body
= g_strdup_printf(
4138 "<td><a href='%s'>%s</a></td>"
4140 "<td style='text-align: center'>"
4141 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4142 body
, h
->uri
, h
->uri
, h
->title
,
4143 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4144 XT_XTP_HL_REMOVE
, i
);
4150 /* small message if there are none */
4153 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4154 "colspan='3'>No History</td></tr>\n", body
);
4159 footer
= g_strdup_printf("</table></div></body></html>");
4161 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4164 * update all history manager tabs as the xtp session
4165 * key has now changed. No need to update the current tab.
4166 * Already did that above.
4168 update_history_tabs(t
);
4174 _load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4181 * Generate a web page detailing the status of any downloads
4184 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4186 struct download
*dl
;
4187 char *header
, *body
, *footer
, *page
, *tmp
;
4191 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4194 show_oops_s("%s invalid parameters", __func__
);
4197 /* mark as a download manager tab */
4198 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4201 * Generate a new session key for next page instance.
4202 * This only happens for the top level call to xtp_page_dl()
4203 * in which case updating_dl_tabs is 0.
4205 if (!updating_dl_tabs
)
4206 generate_xtp_session_key(&dl_session_key
);
4208 /* header - with refresh so as to update */
4209 if (refresh_interval
>= 1)
4210 ref
= g_strdup_printf(
4211 "<meta http-equiv='refresh' content='%u"
4212 ";url=%s%d/%s/%d' />\n",
4222 header
= g_strdup_printf(
4224 "<title>Downloads</title>\n%s%s</head>\n",
4225 XT_DOCTYPE XT_HTML_TAG
,
4229 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4230 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4231 "</p><table><tr><th style='width: 60%%'>"
4232 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4233 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4235 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4236 body
= xtp_page_dl_row(t
, body
, dl
);
4240 /* message if no downloads in list */
4243 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4244 " style='text-align: center'>"
4245 "No downloads</td></tr>\n", body
);
4250 footer
= g_strdup_printf("</table></div></body></html>");
4252 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4256 * update all download manager tabs as the xtp session
4257 * key has now changed. No need to update the current tab.
4258 * Already did that above.
4260 update_download_tabs(t
);
4267 _load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4274 search(struct tab
*t
, struct karg
*args
)
4278 if (t
== NULL
|| args
== NULL
) {
4279 show_oops_s("search invalid parameters");
4282 if (t
->search_text
== NULL
) {
4283 if (global_search
== NULL
)
4284 return (XT_CB_PASSTHROUGH
);
4286 t
->search_text
= g_strdup(global_search
);
4287 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4288 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4292 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4293 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4296 case XT_SEARCH_NEXT
:
4297 d
= t
->search_forward
;
4299 case XT_SEARCH_PREV
:
4300 d
= !t
->search_forward
;
4303 return (XT_CB_PASSTHROUGH
);
4306 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4308 return (XT_CB_HANDLED
);
4311 struct settings_args
{
4317 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4320 struct settings_args
*sa
= cb_args
;
4325 if (s
->flags
& XT_SF_RUNTIME
)
4331 *sa
->body
= g_strdup_printf(
4333 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4334 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4346 set(struct tab
*t
, struct karg
*args
)
4348 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
4350 struct settings_args sa
;
4352 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
4353 bzero(&sa
, sizeof sa
);
4357 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4358 "\n<head><title>Settings</title>\n"
4359 "</head><body><h1>Settings</h1>\n");
4362 body
= g_strdup_printf("<div align='center'><table><tr>"
4363 "<th align='left'>Setting</th>"
4364 "<th align='left'>Value</th></tr>\n");
4366 settings_walk(print_setting
, &sa
);
4369 /* small message if there are none */
4372 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4373 "colspan='2'>No settings</td></tr>\n", body
);
4378 footer
= g_strdup_printf("</table></div></body></html>");
4380 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4386 _load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4388 show_oops(t
, "Invalid command: %s", pars
);
4390 return (XT_CB_PASSTHROUGH
);
4394 session_save(struct tab
*t
, char *filename
, char **ret
)
4400 f
+= strlen("save");
4401 while (*f
== ' ' && *f
!= '\0')
4407 if (f
[0] == '.' || f
[0] == '/')
4411 if (save_tabs(t
, &a
))
4413 strlcpy(named_session
, f
, sizeof named_session
);
4421 session_open(struct tab
*t
, char *filename
, char **ret
)
4427 f
+= strlen("open");
4428 while (*f
== ' ' && *f
!= '\0')
4434 if (f
[0] == '.' || f
[0] == '/')
4438 a
.i
= XT_SES_CLOSETABS
;
4439 if (open_tabs(t
, &a
))
4442 strlcpy(named_session
, f
, sizeof named_session
);
4450 session_delete(struct tab
*t
, char *filename
, char **ret
)
4452 char file
[PATH_MAX
];
4456 f
+= strlen("delete");
4457 while (*f
== ' ' && *f
!= '\0')
4463 if (f
[0] == '.' || f
[0] == '/')
4466 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, f
);
4470 if (!strcmp(f
, named_session
))
4471 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4472 sizeof named_session
);
4480 session_cmd(struct tab
*t
, struct karg
*args
)
4482 char *action
= NULL
;
4483 char *filename
= NULL
;
4488 if ((action
= getparams(args
->s
, "session")))
4493 if (!strcmp(action
, "show"))
4494 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4495 XT_SAVED_TABS_FILE
: named_session
);
4496 else if (g_str_has_prefix(action
, "save ")) {
4497 if (session_save(t
, action
, &filename
)) {
4498 show_oops(t
, "Can't save session: %s",
4499 filename
? filename
: "INVALID");
4502 } else if (g_str_has_prefix(action
, "open ")) {
4503 if (session_open(t
, action
, &filename
)) {
4504 show_oops(t
, "Can't open session: %s",
4505 filename
? filename
: "INVALID");
4508 } else if (g_str_has_prefix(action
, "delete ")) {
4509 if (session_delete(t
, action
, &filename
)) {
4510 show_oops(t
, "Can't delete session: %s",
4511 filename
? filename
: "INVALID");
4515 show_oops(t
, "Invalid command: %s", action
);
4517 return (XT_CB_PASSTHROUGH
);
4521 * Make a hardcopy of the page
4524 print_page(struct tab
*t
, struct karg
*args
)
4526 WebKitWebFrame
*frame
;
4528 GtkPrintOperation
*op
;
4529 GtkPrintOperationAction action
;
4530 GtkPrintOperationResult print_res
;
4531 GError
*g_err
= NULL
;
4532 int marg_l
, marg_r
, marg_t
, marg_b
;
4534 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4536 ps
= gtk_page_setup_new();
4537 op
= gtk_print_operation_new();
4538 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4539 frame
= webkit_web_view_get_main_frame(t
->wv
);
4541 /* the default margins are too small, so we will bump them */
4542 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4543 XT_PRINT_EXTRA_MARGIN
;
4544 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4545 XT_PRINT_EXTRA_MARGIN
;
4546 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4547 XT_PRINT_EXTRA_MARGIN
;
4548 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4549 XT_PRINT_EXTRA_MARGIN
;
4552 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4553 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4554 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4555 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4557 gtk_print_operation_set_default_page_setup(op
, ps
);
4559 /* this appears to free 'op' and 'ps' */
4560 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4562 /* check it worked */
4563 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4564 show_oops_s("can't print: %s", g_err
->message
);
4565 g_error_free (g_err
);
4573 go_home(struct tab
*t
, struct karg
*args
)
4580 restart(struct tab
*t
, struct karg
*args
)
4584 a
.s
= XT_RESTART_TABS_FILE
;
4586 execvp(start_argv
[0], start_argv
);
4592 #define CTRL GDK_CONTROL_MASK
4593 #define MOD1 GDK_MOD1_MASK
4594 #define SHFT GDK_SHIFT_MASK
4596 /* inherent to GTK not all keys will be caught at all times */
4597 /* XXX sort key bindings */
4598 struct key_binding
{
4603 int (*func
)(struct tab
*, struct karg
*);
4605 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4607 { "cookiejar", MOD1
, 0, GDK_j
, xtp_page_cl
, {0} },
4608 { "downloadmgr", MOD1
, 0, GDK_d
, xtp_page_dl
, {0} },
4609 { "history", MOD1
, 0, GDK_h
, xtp_page_hl
, {0} },
4610 { "print", CTRL
, 0, GDK_p
, print_page
, {0}},
4611 { NULL
, 0, 0, GDK_slash
, command
, {.i
= '/'} },
4612 { NULL
, 0, 0, GDK_question
, command
, {.i
= '?'} },
4613 { NULL
, 0, 0, GDK_colon
, command
, {.i
= ':'} },
4614 { "quit", CTRL
, 0, GDK_q
, quit
, {0} },
4615 { "restart", MOD1
, 0, GDK_q
, restart
, {0} },
4616 { "togglejs", CTRL
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4617 { "togglecookie", MOD1
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4618 { "togglesrc", CTRL
, 0, GDK_s
, toggle_src
, {0} },
4619 { "yankuri", 0, 0, GDK_y
, yank_uri
, {0} },
4620 { "pasteuricur", 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
4621 { "pasteurinew", 0, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
4624 { "searchnext", 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
4625 { "searchprev", 0, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
4628 { "focusaddress", 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
4629 { "focussearch", 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
4631 /* command aliases (handy when -S flag is used) */
4632 { NULL
, 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
4633 { NULL
, 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
4634 { NULL
, 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
4635 { NULL
, 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
4638 { "hinting", 0, 0, GDK_f
, hint
, {.i
= 0} },
4641 { "goback", 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
4642 { "goback", MOD1
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
4643 { "goforward", SHFT
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
4644 { "goforward", MOD1
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
4645 { "reload", 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
4646 { "reload", CTRL
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
4647 { "reloadforce", CTRL
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
4648 { "reload" , CTRL
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
4649 { "favorites", MOD1
, 1, GDK_f
, xtp_page_fl
, {0} },
4651 /* vertical movement */
4652 { "scrolldown", 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
4653 { "scrolldown", 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
4654 { "scrollup", 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
4655 { "scrollup", 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
4656 { "scrollbottom", 0, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
4657 { "scrollbottom", 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
4658 { "scrolltop", 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
4659 { "scrolltop", 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} },
4660 { "scrollpagedown", 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4661 { "scrollpagedown", CTRL
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4662 { "scrollhalfdown", CTRL
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
4663 { "scrollpagedown", 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4664 { "scrollpageup", 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
4665 { "scrollpageup", CTRL
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
4666 { "scrollhalfup", CTRL
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
4667 /* horizontal movement */
4668 { "scrollright", 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
4669 { "scrollright", 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
4670 { "scrollleft", 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
4671 { "scrollleft", 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
4672 { "scrollfarright", 0, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
4673 { "scrollfarleft", 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
4676 { "tabnew", CTRL
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
4677 { "tabclose", CTRL
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
4678 { "tabundoclose", 0, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
4679 { "tabgoto1", CTRL
, 0, GDK_1
, movetab
, {.i
= 1} },
4680 { "tabgoto2", CTRL
, 0, GDK_2
, movetab
, {.i
= 2} },
4681 { "tabgoto3", CTRL
, 0, GDK_3
, movetab
, {.i
= 3} },
4682 { "tabgoto4", CTRL
, 0, GDK_4
, movetab
, {.i
= 4} },
4683 { "tabgoto5", CTRL
, 0, GDK_5
, movetab
, {.i
= 5} },
4684 { "tabgoto6", CTRL
, 0, GDK_6
, movetab
, {.i
= 6} },
4685 { "tabgoto7", CTRL
, 0, GDK_7
, movetab
, {.i
= 7} },
4686 { "tabgoto8", CTRL
, 0, GDK_8
, movetab
, {.i
= 8} },
4687 { "tabgoto9", CTRL
, 0, GDK_9
, movetab
, {.i
= 9} },
4688 { "tabgoto10", CTRL
, 0, GDK_0
, movetab
, {.i
= 10} },
4689 { "tabgotofirst", CTRL
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
4690 { "tabgotolast", CTRL
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
4691 { "tabgotoprev", CTRL
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
4692 { "tabgotonext", CTRL
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
4693 { "focusout", CTRL
, 0, GDK_minus
, resizetab
, {.i
= -1} },
4694 { "focusin", CTRL
, 0, GDK_plus
, resizetab
, {.i
= 1} },
4695 { "focusin", CTRL
, 0, GDK_equal
, resizetab
, {.i
= 1} },
4697 TAILQ_HEAD(keybinding_list
, key_binding
);
4700 walk_kb(struct settings
*s
,
4701 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4703 struct key_binding
*k
;
4706 if (s
== NULL
|| cb
== NULL
) {
4707 show_oops_s("walk_kb invalid parameters");
4711 TAILQ_FOREACH(k
, &kbl
, entry
) {
4712 if (k
->name
== NULL
)
4717 if (gdk_keyval_name(k
->key
) == NULL
)
4720 strlcat(str
, k
->name
, sizeof str
);
4721 strlcat(str
, ",", sizeof str
);
4723 if (k
->mask
& GDK_SHIFT_MASK
)
4724 strlcat(str
, "S-", sizeof str
);
4725 if (k
->mask
& GDK_CONTROL_MASK
)
4726 strlcat(str
, "C-", sizeof str
);
4727 if (k
->mask
& GDK_MOD1_MASK
)
4728 strlcat(str
, "M1-", sizeof str
);
4729 if (k
->mask
& GDK_MOD2_MASK
)
4730 strlcat(str
, "M2-", sizeof str
);
4731 if (k
->mask
& GDK_MOD3_MASK
)
4732 strlcat(str
, "M3-", sizeof str
);
4733 if (k
->mask
& GDK_MOD4_MASK
)
4734 strlcat(str
, "M4-", sizeof str
);
4735 if (k
->mask
& GDK_MOD5_MASK
)
4736 strlcat(str
, "M5-", sizeof str
);
4738 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4739 cb(s
, str
, cb_args
);
4743 init_keybindings(void)
4746 struct key_binding
*k
;
4748 for (i
= 0; i
< LENGTH(keys
); i
++) {
4749 k
= g_malloc0(sizeof *k
);
4750 k
->name
= keys
[i
].name
;
4751 k
->mask
= keys
[i
].mask
;
4752 k
->use_in_entry
= keys
[i
].use_in_entry
;
4753 k
->key
= keys
[i
].key
;
4754 k
->func
= keys
[i
].func
;
4755 bcopy(&keys
[i
].arg
, &k
->arg
, sizeof k
->arg
);
4756 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4758 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4759 k
->name
? k
->name
: "unnamed key");
4764 keybinding_clearall(void)
4766 struct key_binding
*k
, *next
;
4768 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4769 next
= TAILQ_NEXT(k
, entry
);
4770 if (k
->name
== NULL
)
4773 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4774 k
->name
? k
->name
: "unnamed key");
4775 TAILQ_REMOVE(&kbl
, k
, entry
);
4781 keybinding_add(char *kb
, char *value
, struct key_binding
*orig
)
4783 struct key_binding
*k
;
4784 guint keyval
, mask
= 0;
4787 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s %s\n", kb
, value
, orig
->name
);
4791 if (strcmp(kb
, orig
->name
))
4794 /* find modifier keys */
4795 if (strstr(value
, "S-"))
4796 mask
|= GDK_SHIFT_MASK
;
4797 if (strstr(value
, "C-"))
4798 mask
|= GDK_CONTROL_MASK
;
4799 if (strstr(value
, "M1-"))
4800 mask
|= GDK_MOD1_MASK
;
4801 if (strstr(value
, "M2-"))
4802 mask
|= GDK_MOD2_MASK
;
4803 if (strstr(value
, "M3-"))
4804 mask
|= GDK_MOD3_MASK
;
4805 if (strstr(value
, "M4-"))
4806 mask
|= GDK_MOD4_MASK
;
4807 if (strstr(value
, "M5-"))
4808 mask
|= GDK_MOD5_MASK
;
4811 for (i
= strlen(value
) - 1; i
> 0; i
--)
4812 if (value
[i
] == '-')
4813 value
= &value
[i
+ 1];
4815 /* validate keyname */
4816 keyval
= gdk_keyval_from_name(value
);
4817 if (keyval
== GDK_VoidSymbol
) {
4818 warnx("invalid keybinding name %s", value
);
4821 /* must run this test too, gtk+ doesn't handle 10 for example */
4822 if (gdk_keyval_name(keyval
) == NULL
) {
4823 warnx("invalid keybinding name %s", value
);
4827 /* make sure it isn't a dupe */
4828 TAILQ_FOREACH(k
, &kbl
, entry
)
4829 if (k
->key
== keyval
&& k
->mask
== mask
) {
4830 warnx("duplicate keybinding for %s", value
);
4835 k
= g_malloc0(sizeof *k
);
4836 k
->name
= orig
->name
;
4838 k
->use_in_entry
= orig
->use_in_entry
;
4840 k
->func
= orig
->func
;
4841 bcopy(&orig
->arg
, &k
->arg
, sizeof k
->arg
);
4843 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4848 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4849 k
->name
, gdk_keyval_name(keyval
));
4851 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4857 add_kb(struct settings
*s
, char *entry
)
4862 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4864 /* clearall is special */
4865 if (!strcmp(entry
, "clearall")) {
4866 keybinding_clearall();
4870 kb
= strstr(entry
, ",");
4876 /* make sure it is a valid keybinding */
4877 for (i
= 0; i
< LENGTH(keys
); i
++)
4878 if (keys
[i
].name
&& !strcmp(entry
, keys
[i
].name
)) {
4879 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s 0x%x %d 0x%x\n",
4882 keys
[i
].use_in_entry
,
4885 return (keybinding_add(entry
, value
, &keys
[i
]));
4894 int (*func
)(struct tab
*, struct karg
*);
4897 { "q!", 0, quit
, {0} },
4898 { "qa", 0, quit
, {0} },
4899 { "qa!", 0, quit
, {0} },
4900 { "w", 0, save_tabs
, {0} },
4901 { "wq", 0, save_tabs_and_quit
, {0} },
4902 { "wq!", 0, save_tabs_and_quit
, {0} },
4903 { "help", 0, help
, {0} },
4904 { "about", 0, about
, {0} },
4905 { "stats", 0, stats
, {0} },
4906 { "version", 0, about
, {0} },
4907 { "cookies", 0, xtp_page_cl
, {0} },
4908 { "fav", 0, xtp_page_fl
, {0} },
4909 { "favadd", 0, add_favorite
, {0} },
4910 { "js", 2, js_cmd
, {0} },
4911 { "cookie", 2, cookie_cmd
, {0} },
4912 { "cert", 1, cert_cmd
, {0} },
4913 { "ca", 0, ca_cmd
, {0} },
4914 { "dl", 0, xtp_page_dl
, {0} },
4915 { "h", 0, xtp_page_hl
, {0} },
4916 { "hist", 0, xtp_page_hl
, {0} },
4917 { "history", 0, xtp_page_hl
, {0} },
4918 { "home", 0, go_home
, {0} },
4919 { "restart", 0, restart
, {0} },
4920 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4921 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4922 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4923 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4924 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4925 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4926 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4927 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4929 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
4930 { "print", 0, print_page
, {0} },
4933 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4934 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4935 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4936 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4937 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4938 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4939 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4940 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4941 { "tabshow", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4942 { "tabs", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4943 { "tabhide", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4944 { "tabh", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4945 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4946 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4947 /* XXX add count to these commands */
4948 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4949 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4950 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4951 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4952 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
4953 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
4954 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
4955 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
4956 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4957 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4960 { "set", 1, set
, {0} },
4961 { "fullscreen", 0, fullscreen
, {0} },
4962 { "f", 0, fullscreen
, {0} },
4965 { "session", 1, session_cmd
, {0} },
4969 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4977 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4979 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4981 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
4988 * cancel, remove, etc. downloads
4991 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
4993 struct download find
, *d
;
4995 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
4997 /* some commands require a valid download id */
4998 if (cmd
!= XT_XTP_DL_LIST
) {
4999 /* lookup download in question */
5001 d
= RB_FIND(download_list
, &downloads
, &find
);
5004 show_oops(t
, "%s: no such download", __func__
);
5009 /* decide what to do */
5011 case XT_XTP_DL_CANCEL
:
5012 webkit_download_cancel(d
->download
);
5014 case XT_XTP_DL_REMOVE
:
5015 webkit_download_cancel(d
->download
); /* just incase */
5016 g_object_unref(d
->download
);
5017 RB_REMOVE(download_list
, &downloads
, d
);
5019 case XT_XTP_DL_LIST
:
5023 show_oops(t
, "%s: unknown command", __func__
);
5026 xtp_page_dl(t
, NULL
);
5030 * Actions on history, only does one thing for now, but
5031 * we provide the function for future actions
5034 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5036 struct history
*h
, *next
;
5040 case XT_XTP_HL_REMOVE
:
5041 /* walk backwards, as listed in reverse */
5042 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5043 next
= RB_PREV(history_list
, &hl
, h
);
5045 RB_REMOVE(history_list
, &hl
, h
);
5046 g_free((gpointer
) h
->title
);
5047 g_free((gpointer
) h
->uri
);
5054 case XT_XTP_HL_LIST
:
5055 /* Nothing - just xtp_page_hl() below */
5058 show_oops(t
, "%s: unknown command", __func__
);
5062 xtp_page_hl(t
, NULL
);
5065 /* remove a favorite */
5067 remove_favorite(struct tab
*t
, int index
)
5069 char file
[PATH_MAX
], *title
, *uri
;
5070 char *new_favs
, *tmp
;
5075 /* open favorites */
5076 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5078 if ((f
= fopen(file
, "r")) == NULL
) {
5079 show_oops(t
, "%s: can't open favorites: %s",
5080 __func__
, strerror(errno
));
5084 /* build a string which will become the new favroites file */
5085 new_favs
= g_strdup_printf("%s", "");
5088 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5089 if (feof(f
) || ferror(f
))
5091 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5098 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5099 if (feof(f
) || ferror(f
)) {
5100 show_oops(t
, "%s: can't parse favorites %s",
5101 __func__
, strerror(errno
));
5106 /* as long as this isn't the one we are deleting add to file */
5109 new_favs
= g_strdup_printf("%s%s\n%s\n",
5110 new_favs
, title
, uri
);
5122 /* write back new favorites file */
5123 if ((f
= fopen(file
, "w")) == NULL
) {
5124 show_oops(t
, "%s: can't open favorites: %s",
5125 __func__
, strerror(errno
));
5129 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5142 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5145 case XT_XTP_FL_LIST
:
5146 /* nothing, just the below call to xtp_page_fl() */
5148 case XT_XTP_FL_REMOVE
:
5149 remove_favorite(t
, arg
);
5152 show_oops(t
, "%s: invalid favorites command", __func__
);
5156 xtp_page_fl(t
, NULL
);
5160 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5163 case XT_XTP_CL_LIST
:
5164 /* nothing, just xtp_page_cl() */
5166 case XT_XTP_CL_REMOVE
:
5170 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5174 xtp_page_cl(t
, NULL
);
5177 /* link an XTP class to it's session key and handler function */
5178 struct xtp_despatch
{
5181 void (*handle_func
)(struct tab
*, uint8_t, int);
5184 struct xtp_despatch xtp_despatches
[] = {
5185 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5186 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5187 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5188 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5189 { NULL
, NULL
, NULL
}
5193 * is the url xtp protocol? (xxxt://)
5194 * if so, parse and despatch correct bahvior
5197 parse_xtp_url(struct tab
*t
, const char *url
)
5199 char *dup
= NULL
, *p
, *last
;
5200 uint8_t n_tokens
= 0;
5201 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5202 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5206 * tokens array meaning:
5208 * tokens[1] = session key
5209 * tokens[2] = action
5210 * tokens[3] = optional argument
5213 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5215 /*xtp tab meaning is normal unless proven special */
5216 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5218 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5221 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5223 /* split out the url */
5224 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5225 (p
= strtok_r(NULL
, "/", &last
))) {
5227 tokens
[n_tokens
++] = p
;
5230 /* should be atleast three fields 'class/seskey/command/arg' */
5234 dsp
= xtp_despatches
;
5235 req_class
= atoi(tokens
[0]);
5236 while (dsp
->xtp_class
!= NULL
) {
5237 if (dsp
->xtp_class
== req_class
) {
5244 /* did we find one atall? */
5245 if (dsp_match
== NULL
) {
5246 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5250 /* check session key and call despatch function */
5251 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5252 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5265 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5267 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5269 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5272 show_oops_s("activate_uri_entry_cb invalid parameters");
5277 show_oops(t
, "activate_uri_entry_cb no uri");
5281 uri
+= strspn(uri
, "\t ");
5283 /* if xxxt:// treat specially */
5284 if (!parse_xtp_url(t
, uri
)) {
5285 load_uri(t
, (gchar
*)uri
);
5291 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5293 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5294 char *newuri
= NULL
;
5297 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5300 show_oops_s("activate_search_entry_cb invalid parameters");
5304 if (search_string
== NULL
) {
5305 show_oops(t
, "no search_string");
5309 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5310 newuri
= g_strdup_printf(search_string
, enc_search
);
5313 webkit_web_view_load_uri(t
->wv
, newuri
);
5321 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5323 struct domain
*d
= NULL
;
5326 if (uri
== NULL
|| t
== NULL
)
5329 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5334 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5335 es
? "enable" : "disable", uri
);
5337 g_object_set(G_OBJECT(t
->settings
),
5338 "enable-scripts", es
, (char *)NULL
);
5339 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5341 button_set_stockid(t
->js_toggle
,
5342 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5346 show_ca_status(struct tab
*t
, const char *uri
)
5348 WebKitWebFrame
*frame
;
5349 WebKitWebDataSource
*source
;
5350 WebKitNetworkRequest
*request
;
5351 SoupMessage
*message
;
5353 gchar
*col_str
= "white";
5356 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5357 ssl_strict_certs
, ssl_ca_file
, uri
);
5361 if (ssl_ca_file
== NULL
) {
5362 if (g_str_has_prefix(uri
, "http://"))
5364 if (g_str_has_prefix(uri
, "https://")) {
5370 if (g_str_has_prefix(uri
, "http://") ||
5371 !g_str_has_prefix(uri
, "https://"))
5374 frame
= webkit_web_view_get_main_frame(t
->wv
);
5375 source
= webkit_web_frame_get_data_source(frame
);
5376 request
= webkit_web_data_source_get_request(source
);
5377 message
= webkit_network_request_get_message(request
);
5379 if (message
&& (soup_message_get_flags(message
) &
5380 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5384 r
= load_compare_cert(t
, NULL
);
5386 col_str
= "lightblue";
5395 gdk_color_parse(col_str
, &color
);
5396 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5398 if (!strcmp(col_str
, "white")) {
5399 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5401 gdk_color_parse("black", &color
);
5402 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5405 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5407 gdk_color_parse("black", &color
);
5408 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5415 free_favicon(struct tab
*t
)
5417 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5418 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5420 if (t
->icon_request
)
5421 g_object_unref(t
->icon_request
);
5423 g_object_unref(t
->icon_pixbuf
);
5424 if (t
->icon_dest_uri
)
5425 g_free(t
->icon_dest_uri
);
5427 t
->icon_pixbuf
= NULL
;
5428 t
->icon_request
= NULL
;
5429 t
->icon_dest_uri
= NULL
;
5433 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5435 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5436 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5437 if (show_url
== 0) {
5438 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5439 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5441 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5442 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5448 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5450 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5451 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5452 if (show_url
== 0) {
5453 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5454 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5456 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5457 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5462 abort_favicon_download(struct tab
*t
)
5464 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5466 if (t
->icon_download
)
5467 webkit_download_cancel(t
->icon_download
);
5471 xt_icon_from_name(t
, "text-html");
5475 set_favicon_from_file(struct tab
*t
, char *file
)
5478 GdkPixbuf
*pixbuf
, *scaled
;
5481 if (t
== NULL
|| file
== NULL
)
5483 if (t
->icon_pixbuf
) {
5484 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5488 if (g_str_has_prefix(file
, "file://"))
5489 file
+= strlen("file://");
5490 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5492 if (!stat(file
, &sb
)) {
5493 if (sb
.st_size
== 0) {
5494 /* corrupt icon so trash it */
5495 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5498 /* no need to set icon to default here */
5503 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5504 if (pixbuf
== NULL
) {
5505 xt_icon_from_name(t
, "text-html");
5509 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5511 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5512 __func__
, t
->tab_id
, width
, height
);
5514 if (width
> 16 || height
> 16) {
5515 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5516 GDK_INTERP_BILINEAR
);
5517 g_object_unref(pixbuf
);
5521 if (scaled
== NULL
) {
5522 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5523 GDK_INTERP_BILINEAR
);
5527 t
->icon_pixbuf
= scaled
;
5528 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5532 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5535 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5540 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5541 __func__
, t
->tab_id
, status
);
5544 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5546 t
->icon_download
= NULL
;
5549 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5552 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5555 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5557 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5558 __func__
, t
->tab_id
);
5559 t
->icon_download
= NULL
;
5562 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5564 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5565 __func__
, t
->icon_dest_uri
);
5566 set_favicon_from_file(t
, t
->icon_dest_uri
);
5567 /* these will be freed post callback */
5568 t
->icon_request
= NULL
;
5569 t
->icon_download
= NULL
;
5577 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5579 gchar
*name_hash
, file
[PATH_MAX
];
5582 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5584 if (uri
== NULL
|| t
== NULL
)
5587 if (t
->icon_request
) {
5588 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5592 /* check to see if we got the icon in cache */
5593 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5594 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5597 if (!stat(file
, &sb
)) {
5598 if (sb
.st_size
> 0) {
5599 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5601 set_favicon_from_file(t
, file
);
5605 /* corrupt icon so trash it */
5606 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5611 /* create download for icon */
5612 t
->icon_request
= webkit_network_request_new(uri
);
5613 if (t
->icon_request
== NULL
) {
5614 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5619 t
->icon_download
= webkit_download_new(t
->icon_request
);
5621 /* we have to free icon_dest_uri later */
5622 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5623 webkit_download_set_destination_uri(t
->icon_download
,
5626 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5627 G_CALLBACK(favicon_download_status_changed_cb
), t
);
5629 webkit_download_start(t
->icon_download
);
5633 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5635 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5636 struct history
*h
, find
;
5638 const gchar
*s_loading
;
5641 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
5642 webkit_web_view_get_load_status(wview
));
5645 show_oops_s("notify_load_status_cb invalid paramters");
5649 switch (webkit_web_view_get_load_status(wview
)) {
5650 case WEBKIT_LOAD_PROVISIONAL
:
5652 abort_favicon_download(t
);
5653 #if GTK_CHECK_VERSION(2, 20, 0)
5654 gtk_widget_show(t
->spinner
);
5655 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5657 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5659 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5663 case WEBKIT_LOAD_COMMITTED
:
5665 if ((uri
= get_uri(wview
)) != NULL
) {
5666 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5667 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5673 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5676 /* check if js white listing is enabled */
5677 if (enable_js_whitelist
) {
5678 uri
= get_uri(wview
);
5679 check_and_set_js(uri
, t
);
5682 show_ca_status(t
, uri
);
5684 /* we know enough to autosave the session */
5685 if (session_autosave
) {
5691 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5693 title
= webkit_web_view_get_title(wview
);
5694 uri
= get_uri(wview
);
5702 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5703 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5706 if (!strncmp(uri
, "http://", strlen("http://")) ||
5707 !strncmp(uri
, "https://", strlen("https://")) ||
5708 !strncmp(uri
, "file://", strlen("file://")))
5713 h
= RB_FIND(history_list
, &hl
, &find
);
5717 h
= g_malloc(sizeof *h
);
5718 h
->uri
= g_strdup(uri
);
5720 h
->title
= g_strdup(title
);
5722 h
->title
= g_strdup(uri
);
5723 RB_INSERT(history_list
, &hl
, h
);
5724 update_history_tabs(NULL
);
5729 case WEBKIT_LOAD_FINISHED
:
5731 uri
= get_uri(wview
);
5732 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5733 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5734 case WEBKIT_LOAD_FAILED
:
5737 #if GTK_CHECK_VERSION(2, 20, 0)
5738 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5739 gtk_widget_hide(t
->spinner
);
5741 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5742 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5743 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5745 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5750 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5752 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5753 webkit_web_view_can_go_back(wview
));
5755 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5756 webkit_web_view_can_go_forward(wview
));
5758 /* take focus if we are visible */
5764 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5766 run_script(t
, JS_HINTING
);
5770 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5772 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5773 progress
== 100 ? 0 : (double)progress
/ 100);
5774 if (show_url
== 0) {
5775 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5776 progress
== 100 ? 0 : (double)progress
/ 100);
5781 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5782 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5783 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5788 show_oops_s("webview_nw_cb invalid paramters");
5792 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
5793 webkit_network_request_get_uri(request
));
5795 /* open in current tab */
5796 uri
= (char *)webkit_network_request_get_uri(request
);
5797 webkit_web_view_load_uri(t
->wv
, uri
);
5798 webkit_web_policy_decision_ignore(pd
);
5800 return (TRUE
); /* we made the decission */
5804 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5805 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5806 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5811 show_oops_s("webview_npd_cb invalid parameters");
5815 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
5817 webkit_network_request_get_uri(request
));
5819 uri
= (char *)webkit_network_request_get_uri(request
);
5821 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
5823 create_new_tab(uri
, NULL
, ctrl_click_focus
);
5824 webkit_web_policy_decision_ignore(pd
);
5825 return (TRUE
); /* we made the decission */
5828 webkit_web_policy_decision_use(pd
);
5829 return (TRUE
); /* we made the decission */
5833 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5835 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
5836 webkit_web_view_get_uri(wv
));
5842 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
5844 /* we can not eat the event without throwing gtk off so defer it */
5846 /* catch middle click */
5847 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
5852 /* catch ctrl click */
5853 if (e
->type
== GDK_BUTTON_RELEASE
&&
5854 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
5859 return (XT_CB_PASSTHROUGH
);
5863 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
5865 struct mime_type
*m
;
5867 m
= find_mime_type(mime_type
);
5873 show_oops(t
, "can't fork mime handler");
5882 execlp(m
->mt_action
, m
->mt_action
,
5883 webkit_network_request_get_uri(request
), (void *)NULL
);
5892 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
5893 WebKitNetworkRequest
*request
, char *mime_type
,
5894 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
5897 show_oops_s("webview_mimetype_cb invalid parameters");
5901 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
5902 t
->tab_id
, mime_type
);
5904 if (run_mimehandler(t
, mime_type
, request
) == 0) {
5905 webkit_web_policy_decision_ignore(decision
);
5910 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
5911 webkit_web_policy_decision_download(decision
);
5919 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
5922 const gchar
*filename
;
5924 struct download
*download_entry
;
5927 if (wk_download
== NULL
|| t
== NULL
) {
5928 show_oops_s("%s invalid parameters", __func__
);
5932 filename
= webkit_download_get_suggested_filename(wk_download
);
5933 if (filename
== NULL
)
5934 return (FALSE
); /* abort download */
5936 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
5938 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
5939 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
5941 webkit_download_set_destination_uri(wk_download
, uri
);
5943 if (webkit_download_get_status(wk_download
) ==
5944 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5945 show_oops(t
, "%s: download failed to start", __func__
);
5947 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
5949 download_entry
= g_malloc(sizeof(struct download
));
5950 download_entry
->download
= wk_download
;
5951 download_entry
->tab
= t
;
5952 download_entry
->id
= next_download_id
++;
5953 RB_INSERT(download_list
, &downloads
, download_entry
);
5954 /* get from history */
5955 g_object_ref(wk_download
);
5956 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
5962 /* sync other download manager tabs */
5963 update_download_tabs(NULL
);
5966 * NOTE: never redirect/render the current tab before this
5967 * function returns. This will cause the download to never start.
5969 return (ret
); /* start download */
5973 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
5975 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
5978 show_oops_s("webview_hover_cb");
5983 set_status(t
, uri
, XT_STATUS_LINK
);
5986 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
5991 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
5993 char s
[2], buf
[128];
5994 const char *errstr
= NULL
;
5997 /* don't use w directly; use t->whatever instead */
6000 show_oops_s("wv_keypress_after_cb");
6001 return (XT_CB_PASSTHROUGH
);
6004 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6005 e
->keyval
, e
->state
, t
);
6009 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6011 return (XT_CB_HANDLED
);
6015 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6016 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6018 /* we have a string */
6020 /* we have a number */
6021 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6029 /* XXX unfuck this */
6030 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6031 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6032 /* last input was numerical */
6034 l
= strlen(t
->hint_num
);
6041 t
->hint_num
[l
] = '\0';
6045 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6046 /* last input was alphanumerical */
6048 l
= strlen(t
->hint_buf
);
6055 t
->hint_buf
[l
] = '\0';
6065 /* numerical input */
6066 if (CLEAN(e
->state
) == 0 &&
6067 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6068 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6069 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6070 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6073 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6075 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6078 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6080 t
->hint_mode
= XT_HINT_NUMERICAL
;
6084 /* empty the counter buffer */
6085 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6086 return (XT_CB_HANDLED
);
6089 /* alphanumerical input */
6091 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6092 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6093 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6094 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6095 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6096 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6097 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6100 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6103 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6105 t
->hint_mode
= XT_HINT_ALPHANUM
;
6108 /* empty the counter buffer */
6109 bzero(t
->hint_num
, sizeof t
->hint_num
);
6110 return (XT_CB_HANDLED
);
6113 return (XT_CB_HANDLED
);
6116 struct key_binding
*k
;
6117 TAILQ_FOREACH(k
, &kbl
, entry
)
6118 if (e
->keyval
== k
->key
) {
6120 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6121 k
->func(t
, &k
->arg
);
6122 return (XT_CB_HANDLED
);
6125 else if ((e
->state
& k
->mask
) == k
->mask
) {
6126 k
->func(t
, &k
->arg
);
6127 return (XT_CB_HANDLED
);
6131 return (XT_CB_PASSTHROUGH
);
6135 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6139 return (XT_CB_PASSTHROUGH
);
6143 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6145 const gchar
*c
= gtk_entry_get_text(w
);
6149 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6150 e
->keyval
, e
->state
, t
);
6153 show_oops_s("cmd_keyrelease_cb invalid parameters");
6154 return (XT_CB_PASSTHROUGH
);
6157 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6158 e
->keyval
, e
->state
, t
);
6162 if (strlen(c
) == 1) {
6163 webkit_web_view_unmark_text_matches(t
->wv
);
6169 else if (c
[0] == '?')
6175 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6177 /* not found, mark red */
6178 gdk_color_parse("red", &color
);
6179 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6180 /* unmark and remove selection */
6181 webkit_web_view_unmark_text_matches(t
->wv
);
6182 /* my kingdom for a way to unselect text in webview */
6184 /* found, highlight all */
6185 webkit_web_view_unmark_text_matches(t
->wv
);
6186 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6187 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6188 gdk_color_parse("white", &color
);
6189 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6192 return (XT_CB_PASSTHROUGH
);
6197 cmd_complete(struct tab
*t
, char *s
)
6200 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6202 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
6204 for (i
= 0; i
< LENGTH(cmds
); i
++) {
6205 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
6206 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
6208 gtk_entry_set_text(w
, ":");
6209 gtk_entry_append_text(w
, cmds
[i
].cmd
);
6210 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6220 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6223 show_oops_s("entry_key_cb invalid parameters");
6224 return (XT_CB_PASSTHROUGH
);
6227 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6228 e
->keyval
, e
->state
, t
);
6232 if (e
->keyval
== GDK_Escape
) {
6233 /* don't use focus_webview(t) because we want to type :cmds */
6234 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6237 struct key_binding
*k
;
6238 TAILQ_FOREACH(k
, &kbl
, entry
)
6239 if (e
->keyval
== k
->key
&& k
->use_in_entry
) {
6241 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6242 k
->func(t
, &k
->arg
);
6243 return (XT_CB_HANDLED
);
6246 else if ((e
->state
& k
->mask
) == k
->mask
) {
6247 k
->func(t
, &k
->arg
);
6248 return (XT_CB_HANDLED
);
6252 return (XT_CB_PASSTHROUGH
);
6256 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6258 int rv
= XT_CB_HANDLED
;
6259 const gchar
*c
= gtk_entry_get_text(w
);
6262 show_oops_s("cmd_keypress_cb parameters");
6263 return (XT_CB_PASSTHROUGH
);
6266 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6267 e
->keyval
, e
->state
, t
);
6271 e
->keyval
= GDK_Escape
;
6272 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6273 e
->keyval
= GDK_Escape
;
6275 switch (e
->keyval
) {
6281 if (strchr (c
, ' ')) {
6282 /* par completion */
6283 fprintf(stderr
, "completeme par\n");
6287 cmd_complete(t
, (char *)&c
[1]);
6292 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6300 if (c
[0] == '/' || c
[0] == '?')
6301 webkit_web_view_unmark_text_matches(t
->wv
);
6305 rv
= XT_CB_PASSTHROUGH
;
6311 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6314 show_oops_s("cmd_focusout_cb invalid parameters");
6315 return (XT_CB_PASSTHROUGH
);
6317 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6322 if (show_url
== 0 || t
->focus_wv
)
6325 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6327 return (XT_CB_PASSTHROUGH
);
6331 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6335 const gchar
*c
= gtk_entry_get_text(entry
);
6338 show_oops_s("cmd_activate_cb invalid parameters");
6342 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6347 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6353 if (c
[0] == '/' || c
[0] == '?') {
6354 if (t
->search_text
) {
6355 g_free(t
->search_text
);
6356 t
->search_text
= NULL
;
6359 t
->search_text
= g_strdup(s
);
6361 g_free(global_search
);
6362 global_search
= g_strdup(s
);
6363 t
->search_forward
= c
[0] == '/';
6368 for (i
= 0; i
< LENGTH(cmds
); i
++)
6369 if (cmds
[i
].params
) {
6370 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
6371 cmds
[i
].arg
.s
= g_strdup(s
);
6372 goto execute_command
;
6375 if (!strcmp(s
, cmds
[i
].cmd
))
6376 goto execute_command
;
6378 show_oops(t
, "Invalid command: %s", s
);
6385 cmds
[i
].func(t
, &cmds
[i
].arg
);
6388 backward_cb(GtkWidget
*w
, struct tab
*t
)
6393 show_oops_s("backward_cb invalid parameters");
6397 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6404 forward_cb(GtkWidget
*w
, struct tab
*t
)
6409 show_oops_s("forward_cb invalid parameters");
6413 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6415 a
.i
= XT_NAV_FORWARD
;
6420 stop_cb(GtkWidget
*w
, struct tab
*t
)
6422 WebKitWebFrame
*frame
;
6425 show_oops_s("stop_cb invalid parameters");
6429 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6431 frame
= webkit_web_view_get_main_frame(t
->wv
);
6432 if (frame
== NULL
) {
6433 show_oops(t
, "stop_cb: no frame");
6437 webkit_web_frame_stop_loading(frame
);
6438 abort_favicon_download(t
);
6442 setup_webkit(struct tab
*t
)
6444 g_object_set(G_OBJECT(t
->settings
),
6445 "user-agent", t
->user_agent
, (char *)NULL
);
6446 g_object_set(G_OBJECT(t
->settings
),
6447 "enable-scripts", enable_scripts
, (char *)NULL
);
6448 g_object_set(G_OBJECT(t
->settings
),
6449 "enable-plugins", enable_plugins
, (char *)NULL
);
6450 adjustfont_webkit(t
, XT_FONT_SET
);
6452 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6456 create_browser(struct tab
*t
)
6462 show_oops_s("create_browser invalid parameters");
6466 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
6467 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
6468 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
6469 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
6471 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
6472 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
6473 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
6475 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
6476 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
6479 t
->settings
= webkit_web_settings_new();
6481 if (user_agent
== NULL
) {
6482 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
6484 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
6487 t
->user_agent
= g_strdup(user_agent
);
6500 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6501 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6502 gtk_widget_set_name(w
, "xxxterm");
6503 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
6504 g_signal_connect(G_OBJECT(w
), "delete_event",
6505 G_CALLBACK (gtk_main_quit
), NULL
);
6511 create_toolbar(struct tab
*t
)
6513 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
6515 b
= gtk_hbox_new(FALSE
, 0);
6517 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6520 /* backward button */
6521 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
6522 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6523 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6524 G_CALLBACK(backward_cb
), t
);
6525 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
6527 /* forward button */
6528 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
6529 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6530 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6531 G_CALLBACK(forward_cb
), t
);
6532 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
6536 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6537 gtk_widget_set_sensitive(t
->stop
, FALSE
);
6538 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
6539 G_CALLBACK(stop_cb
), t
);
6540 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
6544 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6545 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6546 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
6547 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
6548 G_CALLBACK(js_toggle_cb
), t
);
6549 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
6552 t
->uri_entry
= gtk_entry_new();
6553 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
6554 G_CALLBACK(activate_uri_entry_cb
), t
);
6555 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
6556 G_CALLBACK(entry_key_cb
), t
);
6557 eb1
= gtk_hbox_new(FALSE
, 0);
6558 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
6559 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
6560 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
6563 if (fancy_bar
&& search_string
) {
6565 t
->search_entry
= gtk_entry_new();
6566 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
6567 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
6568 G_CALLBACK(activate_search_entry_cb
), t
);
6569 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
6570 G_CALLBACK(entry_key_cb
), t
);
6571 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
6572 eb2
= gtk_hbox_new(FALSE
, 0);
6573 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
6574 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
6576 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
6586 TAILQ_FOREACH(t
, &tabs
, entry
)
6587 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
6591 undo_close_tab_save(struct tab
*t
)
6595 struct undo
*u1
, *u2
;
6597 WebKitWebHistoryItem
*item
;
6599 if ((uri
= get_uri(t
->wv
)) == NULL
)
6602 u1
= g_malloc0(sizeof(struct undo
));
6603 u1
->uri
= g_strdup(uri
);
6605 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6607 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
6608 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
6611 /* forward history */
6612 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
6616 u1
->history
= g_list_prepend(u1
->history
,
6617 webkit_web_history_item_copy(item
));
6618 items
= g_list_next(items
);
6623 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
6624 u1
->history
= g_list_prepend(u1
->history
,
6625 webkit_web_history_item_copy(item
));
6629 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
6633 u1
->history
= g_list_prepend(u1
->history
,
6634 webkit_web_history_item_copy(item
));
6635 items
= g_list_next(items
);
6638 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
6640 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
6641 u2
= TAILQ_LAST(&undos
, undo_tailq
);
6642 TAILQ_REMOVE(&undos
, u2
, entry
);
6644 g_list_free(u2
->history
);
6653 delete_tab(struct tab
*t
)
6657 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
6662 TAILQ_REMOVE(&tabs
, t
, entry
);
6664 /* halt all webkit activity */
6665 abort_favicon_download(t
);
6666 webkit_web_view_stop_loading(t
->wv
);
6667 undo_close_tab_save(t
);
6669 gtk_widget_destroy(t
->vbox
);
6670 g_free(t
->user_agent
);
6674 if (TAILQ_EMPTY(&tabs
))
6675 create_new_tab(NULL
, NULL
, 1);
6678 /* recreate session */
6679 if (session_autosave
) {
6686 adjustfont_webkit(struct tab
*t
, int adjust
)
6689 show_oops_s("adjustfont_webkit invalid parameters");
6693 if (adjust
== XT_FONT_SET
)
6694 t
->font_size
= default_font_size
;
6696 t
->font_size
+= adjust
;
6697 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
6698 t
->font_size
, (char *)NULL
);
6699 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
6700 &t
->font_size
, (char *)NULL
);
6704 append_tab(struct tab
*t
)
6709 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
6710 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
6714 create_new_tab(char *title
, struct undo
*u
, int focus
)
6717 int load
= 1, id
, notfound
;
6719 WebKitWebHistoryItem
*item
;
6722 PangoFontDescription
*fd
= NULL
;
6724 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
6726 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
6727 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
6731 t
= g_malloc0(sizeof *t
);
6733 if (title
== NULL
) {
6734 title
= "(untitled)";
6738 t
->vbox
= gtk_vbox_new(FALSE
, 0);
6740 /* label + button for tab */
6741 b
= gtk_hbox_new(FALSE
, 0);
6744 #if GTK_CHECK_VERSION(2, 20, 0)
6745 t
->spinner
= gtk_spinner_new ();
6747 t
->label
= gtk_label_new(title
);
6748 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
6749 gtk_widget_set_size_request(t
->label
, 100, 0);
6750 gtk_widget_set_size_request(b
, 130, 0);
6752 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
6753 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
6754 #if GTK_CHECK_VERSION(2, 20, 0)
6755 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
6759 t
->toolbar
= create_toolbar(t
);
6760 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
6763 t
->browser_win
= create_browser(t
);
6764 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
6766 /* oops message for user feedback */
6767 t
->oops
= gtk_entry_new();
6768 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
6769 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
6770 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
6771 gdk_color_parse("red", &color
);
6772 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
6773 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
6776 t
->cmd
= gtk_entry_new();
6777 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
6778 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
6779 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
6782 t
->statusbar
= gtk_entry_new();
6783 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
6784 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
6785 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
6786 gdk_color_parse("black", &color
);
6787 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6788 gdk_color_parse("white", &color
);
6789 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6790 fd
= GTK_WIDGET(t
->statusbar
)->style
->font_desc
;
6791 pango_font_description_set_weight(fd
, PANGO_WEIGHT_SEMIBOLD
);
6792 pango_font_description_set_absolute_size(fd
, 10 * PANGO_SCALE
); /* 10 px font */
6793 gtk_widget_modify_font(t
->statusbar
, fd
);
6794 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
6796 /* xtp meaning is normal by default */
6797 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6799 /* set empty favicon */
6800 xt_icon_from_name(t
, "text-html");
6802 /* and show it all */
6803 gtk_widget_show_all(b
);
6804 gtk_widget_show_all(t
->vbox
);
6806 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
6810 id
= gtk_notebook_get_current_page(notebook
);
6811 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6812 if (tt
->tab_id
== id
) {
6814 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
6815 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
6825 #if GTK_CHECK_VERSION(2, 20, 0)
6826 /* turn spinner off if we are a new tab without uri */
6828 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6829 gtk_widget_hide(t
->spinner
);
6832 /* make notebook tabs reorderable */
6833 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
6835 g_object_connect(G_OBJECT(t
->cmd
),
6836 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
6837 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
6838 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
6839 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
6842 /* reuse wv_button_cb to hide oops */
6843 g_object_connect(G_OBJECT(t
->oops
),
6844 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6847 g_object_connect(G_OBJECT(t
->wv
),
6848 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
6849 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6850 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
6851 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
6852 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
6853 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
6854 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_nw_cb
), t
,
6855 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
6856 "signal::event", G_CALLBACK(webview_event_cb
), t
,
6857 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
6858 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
6859 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6860 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
6862 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6864 g_signal_connect(t
->wv
,
6865 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
6867 /* hijack the unused keys as if we were the browser */
6868 g_object_connect(G_OBJECT(t
->toolbar
),
6869 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6872 g_signal_connect(G_OBJECT(bb
), "button_press_event",
6873 G_CALLBACK(tab_close_cb
), t
);
6878 url_set_visibility();
6879 statusbar_set_visibility();
6882 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6883 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
6888 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
6892 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6897 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6898 /* restore the tab's history */
6899 if (u
&& u
->history
) {
6903 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
6904 items
= g_list_next(items
);
6907 item
= g_list_nth_data(u
->history
, u
->back
);
6909 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
6912 g_list_free(u
->history
);
6914 webkit_web_back_forward_list_clear(t
->bfl
);
6918 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
6924 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
6926 TAILQ_FOREACH(t
, &tabs
, entry
) {
6927 if (t
->tab_id
== pn
) {
6928 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
6931 uri
= webkit_web_view_get_title(t
->wv
);
6934 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
6946 menuitem_response(struct tab
*t
)
6948 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6952 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
6954 GtkWidget
*menu
, *menu_items
;
6955 GdkEventButton
*bevent
;
6959 if (event
->type
== GDK_BUTTON_PRESS
) {
6960 bevent
= (GdkEventButton
*) event
;
6961 menu
= gtk_menu_new();
6963 TAILQ_FOREACH(ti
, &tabs
, entry
) {
6964 if ((uri
= get_uri(ti
->wv
)) == NULL
)
6965 /* XXX make sure there is something to print */
6966 /* XXX add gui pages in here to look purdy */
6968 menu_items
= gtk_menu_item_new_with_label(uri
);
6969 gtk_menu_append(GTK_MENU (menu
), menu_items
);
6970 gtk_widget_show(menu_items
);
6972 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
6973 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
6977 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
6978 bevent
->button
, bevent
->time
);
6980 /* unref object so it'll free itself when popped down */
6981 g_object_ref_sink(menu
);
6982 g_object_unref(menu
);
6984 return (TRUE
/* eat event */);
6987 return (FALSE
/* propagate */);
6991 icon_size_map(int icon_size
)
6993 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
6994 icon_size
> GTK_ICON_SIZE_DIALOG
)
6995 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7001 create_button(char *name
, char *stockid
, int size
)
7003 GtkWidget
*button
, *image
;
7006 rcstring
= g_strdup_printf(
7007 "style \"%s-style\"\n"
7009 " GtkWidget::focus-padding = 0\n"
7010 " GtkWidget::focus-line-width = 0\n"
7014 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
7015 gtk_rc_parse_string(rcstring
);
7017 button
= gtk_button_new();
7018 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7019 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
7021 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7022 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7023 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7024 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7025 gtk_widget_set_name(button
, name
);
7026 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7027 gtk_widget_set_tooltip_text(button
, name
);
7033 button_set_stockid(GtkWidget
*button
, char *stockid
)
7036 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7037 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7038 gtk_button_set_image(GTK_BUTTON(button
), image
);
7047 char file
[PATH_MAX
];
7050 vbox
= gtk_vbox_new(FALSE
, 0);
7051 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7052 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7053 gtk_notebook_set_tab_hborder(notebook
, 0);
7054 gtk_notebook_set_tab_vborder(notebook
, 0);
7055 gtk_notebook_set_scrollable(notebook
, TRUE
);
7056 notebook_tab_set_visibility(notebook
);
7057 gtk_notebook_set_show_border(notebook
, FALSE
);
7058 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7060 abtn
= gtk_button_new();
7061 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7062 gtk_widget_set_size_request(arrow
, -1, -1);
7063 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7064 gtk_widget_set_size_request(abtn
, -1, 20);
7065 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7067 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7068 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7069 gtk_widget_set_size_request(vbox
, -1, -1);
7071 g_object_connect(G_OBJECT(notebook
),
7072 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7074 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7075 G_CALLBACK(arrow_cb
), NULL
);
7077 main_window
= create_window();
7078 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7079 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7082 for (i
= 0; i
< LENGTH(icons
); i
++) {
7083 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7084 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7085 l
= g_list_append(l
, pb
);
7087 gtk_window_set_default_icon_list(l
);
7089 gtk_widget_show_all(abtn
);
7090 gtk_widget_show_all(main_window
);
7094 set_hook(void **hook
, char *name
)
7097 errx(1, "set_hook");
7099 if (*hook
== NULL
) {
7100 *hook
= dlsym(RTLD_NEXT
, name
);
7102 errx(1, "can't hook %s", name
);
7106 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7108 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7110 g_return_val_if_fail(cookie1
, FALSE
);
7111 g_return_val_if_fail(cookie2
, FALSE
);
7113 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7114 !strcmp (cookie1
->value
, cookie2
->value
) &&
7115 !strcmp (cookie1
->path
, cookie2
->path
) &&
7116 !strcmp (cookie1
->domain
, cookie2
->domain
));
7120 transfer_cookies(void)
7123 SoupCookie
*sc
, *pc
;
7125 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7127 for (;cf
; cf
= cf
->next
) {
7129 sc
= soup_cookie_copy(pc
);
7130 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7133 soup_cookies_free(cf
);
7137 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7142 print_cookie("soup_cookie_jar_delete_cookie", c
);
7144 if (cookies_enabled
== 0)
7147 if (jar
== NULL
|| c
== NULL
)
7150 /* find and remove from persistent jar */
7151 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7153 for (;cf
; cf
= cf
->next
) {
7155 if (soup_cookie_equal(ci
, c
)) {
7156 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7161 soup_cookies_free(cf
);
7163 /* delete from session jar */
7164 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7168 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7170 struct domain
*d
= NULL
;
7174 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7175 jar
, p_cookiejar
, s_cookiejar
);
7177 if (cookies_enabled
== 0)
7180 /* see if we are up and running */
7181 if (p_cookiejar
== NULL
) {
7182 _soup_cookie_jar_add_cookie(jar
, cookie
);
7185 /* disallow p_cookiejar adds, shouldn't happen */
7186 if (jar
== p_cookiejar
)
7189 if (enable_cookie_whitelist
&&
7190 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7192 DNPRINTF(XT_D_COOKIE
,
7193 "soup_cookie_jar_add_cookie: reject %s\n",
7195 if (save_rejected_cookies
) {
7196 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7197 show_oops_s("can't open reject cookie file");
7200 fseek(r_cookie_f
, 0, SEEK_END
);
7201 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7202 cookie
->http_only
? "#HttpOnly_" : "",
7204 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7206 cookie
->secure
? "TRUE" : "FALSE",
7208 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7215 if (!allow_volatile_cookies
)
7219 if (cookie
->expires
== NULL
&& session_timeout
) {
7220 soup_cookie_set_expires(cookie
,
7221 soup_date_new_from_now(session_timeout
));
7222 print_cookie("modified add cookie", cookie
);
7225 /* see if we are white listed for persistence */
7226 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7227 /* add to persistent jar */
7228 c
= soup_cookie_copy(cookie
);
7229 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7230 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7233 /* add to session jar */
7234 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7235 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7241 char file
[PATH_MAX
];
7243 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7244 "soup_cookie_jar_add_cookie");
7245 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7246 "soup_cookie_jar_delete_cookie");
7248 if (cookies_enabled
== 0)
7252 * the following code is intricate due to overriding several libsoup
7254 * do not alter order of these operations.
7257 /* rejected cookies */
7258 if (save_rejected_cookies
)
7259 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
7261 /* persistent cookies */
7262 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
7263 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7265 /* session cookies */
7266 s_cookiejar
= soup_cookie_jar_new();
7267 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7268 cookie_policy
, (void *)NULL
);
7271 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7275 setup_proxy(char *uri
)
7278 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7279 soup_uri_free(proxy_uri
);
7283 if (http_proxy
!= uri
) {
7290 http_proxy
= g_strdup(uri
);
7291 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7292 proxy_uri
= soup_uri_new(http_proxy
);
7293 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7298 send_url_to_socket(char *url
)
7300 int s
, len
, rv
= -1;
7301 struct sockaddr_un sa
;
7303 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7304 warnx("send_url_to_socket: socket");
7308 sa
.sun_family
= AF_UNIX
;
7309 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7310 work_dir
, XT_SOCKET_FILE
);
7313 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7314 warnx("send_url_to_socket: connect");
7318 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
7319 warnx("send_url_to_socket: send");
7328 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7331 char str
[XT_MAX_URL_LENGTH
];
7332 socklen_t t
= sizeof(struct sockaddr_un
);
7333 struct sockaddr_un sa
;
7338 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7339 warn("socket_watcher: accept");
7343 if (getpeereid(s
, &uid
, &gid
) == -1) {
7344 warn("socket_watcher: getpeereid");
7347 if (uid
!= getuid() || gid
!= getgid()) {
7348 warnx("socket_watcher: unauthorized user");
7354 warnx("socket_watcher: not a valid user");
7358 n
= recv(s
, str
, sizeof(str
), 0);
7362 create_new_tab(str
, NULL
, 1);
7369 struct sockaddr_un sa
;
7371 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7372 warn("is_running: socket");
7376 sa
.sun_family
= AF_UNIX
;
7377 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7378 work_dir
, XT_SOCKET_FILE
);
7381 /* connect to see if there is a listener */
7382 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7383 rv
= 0; /* not running */
7385 rv
= 1; /* already running */
7396 struct sockaddr_un sa
;
7398 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7399 warn("build_socket: socket");
7403 sa
.sun_family
= AF_UNIX
;
7404 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7405 work_dir
, XT_SOCKET_FILE
);
7408 /* connect to see if there is a listener */
7409 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7410 /* no listener so we will */
7411 unlink(sa
.sun_path
);
7413 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7414 warn("build_socket: bind");
7418 if (listen(s
, 1) == -1) {
7419 warn("build_socket: listen");
7435 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
7440 main(int argc
, char *argv
[])
7443 int c
, s
, optn
= 0, focus
= 1;
7444 char conf
[PATH_MAX
] = { '\0' };
7445 char file
[PATH_MAX
];
7446 char *env_proxy
= NULL
;
7449 struct sigaction sact
;
7453 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
7455 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
7464 errx(0 , "Version: %s", version
);
7467 strlcpy(conf
, optarg
, sizeof(conf
));
7470 strlcpy(named_session
, optarg
, sizeof(named_session
));
7488 RB_INIT(&downloads
);
7492 TAILQ_INIT(&aliases
);
7498 gnutls_global_init();
7500 /* generate session keys for xtp pages */
7501 generate_xtp_session_key(&dl_session_key
);
7502 generate_xtp_session_key(&hl_session_key
);
7503 generate_xtp_session_key(&cl_session_key
);
7504 generate_xtp_session_key(&fl_session_key
);
7507 gtk_init(&argc
, &argv
);
7508 if (!g_thread_supported())
7509 g_thread_init(NULL
);
7512 bzero(&sact
, sizeof(sact
));
7513 sigemptyset(&sact
.sa_mask
);
7514 sact
.sa_handler
= sigchild
;
7515 sact
.sa_flags
= SA_NOCLDSTOP
;
7516 sigaction(SIGCHLD
, &sact
, NULL
);
7518 /* set download dir */
7519 pwd
= getpwuid(getuid());
7521 errx(1, "invalid user %d", getuid());
7522 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
7524 /* set default string settings */
7525 home
= g_strdup("http://www.peereboom.us");
7526 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
7527 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
7528 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
7530 /* read config file */
7531 if (strlen(conf
) == 0)
7532 snprintf(conf
, sizeof conf
, "%s/.%s",
7533 pwd
->pw_dir
, XT_CONF_FILE
);
7534 config_parse(conf
, 0);
7536 /* working directory */
7537 if (strlen(work_dir
) == 0)
7538 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
7539 pwd
->pw_dir
, XT_DIR
);
7540 if (stat(work_dir
, &sb
)) {
7541 if (mkdir(work_dir
, S_IRWXU
) == -1)
7542 err(1, "mkdir work_dir");
7543 if (stat(work_dir
, &sb
))
7544 err(1, "stat work_dir");
7546 if (S_ISDIR(sb
.st_mode
) == 0)
7547 errx(1, "%s not a dir", work_dir
);
7548 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7549 warnx("fixing invalid permissions on %s", work_dir
);
7550 if (chmod(work_dir
, S_IRWXU
) == -1)
7554 /* icon cache dir */
7555 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
7556 if (stat(cache_dir
, &sb
)) {
7557 if (mkdir(cache_dir
, S_IRWXU
) == -1)
7558 err(1, "mkdir cache_dir");
7559 if (stat(cache_dir
, &sb
))
7560 err(1, "stat cache_dir");
7562 if (S_ISDIR(sb
.st_mode
) == 0)
7563 errx(1, "%s not a dir", cache_dir
);
7564 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7565 warnx("fixing invalid permissions on %s", cache_dir
);
7566 if (chmod(cache_dir
, S_IRWXU
) == -1)
7571 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
7572 if (stat(certs_dir
, &sb
)) {
7573 if (mkdir(certs_dir
, S_IRWXU
) == -1)
7574 err(1, "mkdir certs_dir");
7575 if (stat(certs_dir
, &sb
))
7576 err(1, "stat certs_dir");
7578 if (S_ISDIR(sb
.st_mode
) == 0)
7579 errx(1, "%s not a dir", certs_dir
);
7580 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7581 warnx("fixing invalid permissions on %s", certs_dir
);
7582 if (chmod(certs_dir
, S_IRWXU
) == -1)
7587 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
7588 work_dir
, XT_SESSIONS_DIR
);
7589 if (stat(sessions_dir
, &sb
)) {
7590 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
7591 err(1, "mkdir sessions_dir");
7592 if (stat(sessions_dir
, &sb
))
7593 err(1, "stat sessions_dir");
7595 if (S_ISDIR(sb
.st_mode
) == 0)
7596 errx(1, "%s not a dir", sessions_dir
);
7597 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7598 warnx("fixing invalid permissions on %s", sessions_dir
);
7599 if (chmod(sessions_dir
, S_IRWXU
) == -1)
7602 /* runtime settings that can override config file */
7603 if (runtime_settings
[0] != '\0')
7604 config_parse(runtime_settings
, 1);
7607 if (!strcmp(download_dir
, pwd
->pw_dir
))
7608 strlcat(download_dir
, "/downloads", sizeof download_dir
);
7609 if (stat(download_dir
, &sb
)) {
7610 if (mkdir(download_dir
, S_IRWXU
) == -1)
7611 err(1, "mkdir download_dir");
7612 if (stat(download_dir
, &sb
))
7613 err(1, "stat download_dir");
7615 if (S_ISDIR(sb
.st_mode
) == 0)
7616 errx(1, "%s not a dir", download_dir
);
7617 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7618 warnx("fixing invalid permissions on %s", download_dir
);
7619 if (chmod(download_dir
, S_IRWXU
) == -1)
7623 /* favorites file */
7624 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
7625 if (stat(file
, &sb
)) {
7626 warnx("favorites file doesn't exist, creating it");
7627 if ((f
= fopen(file
, "w")) == NULL
)
7628 err(1, "favorites");
7633 session
= webkit_get_default_session();
7638 if (stat(ssl_ca_file
, &sb
)) {
7639 warn("no CA file: %s", ssl_ca_file
);
7640 g_free(ssl_ca_file
);
7643 g_object_set(session
,
7644 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
7645 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
7650 env_proxy
= getenv("http_proxy");
7652 setup_proxy(env_proxy
);
7654 setup_proxy(http_proxy
);
7656 /* see if there is already an xxxterm running */
7657 if (single_instance
&& is_running()) {
7659 warnx("already running");
7664 send_url_to_socket(argv
[0]);
7675 if (save_global_history
)
7676 restore_global_history();
7678 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
7679 restore_saved_tabs();
7681 a
.s
= named_session
;
7682 a
.i
= XT_SES_DONOTHING
;
7683 open_tabs(NULL
, &a
);
7687 create_new_tab(argv
[0], NULL
, focus
);
7694 if (TAILQ_EMPTY(&tabs
))
7695 create_new_tab(home
, NULL
, 1);
7698 if ((s
= build_socket()) != -1)
7699 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
7703 gnutls_global_deinit();