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")
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")
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 set_status(struct tab
*t
, gchar
*s
, int status
)
767 case XT_STATUS_LOADING
:
768 type
= g_strdup_printf("Loading: %s", s
);
772 type
= g_strdup_printf("Link: %s", s
);
774 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
778 type
= g_strdup_printf("%s", s
);
780 t
->status
= g_strdup(type
);
784 t
->status
= g_strdup(s
);
786 case XT_STATUS_NOTHING
:
791 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
797 hide_oops(struct tab
*t
)
799 gtk_widget_hide(t
->oops
);
803 hide_cmd(struct tab
*t
)
805 gtk_widget_hide(t
->cmd
);
809 show_cmd(struct tab
*t
)
811 gtk_widget_hide(t
->oops
);
812 gtk_widget_show(t
->cmd
);
816 show_oops(struct tab
*t
, const char *fmt
, ...)
825 if (vasprintf(&msg
, fmt
, ap
) == -1)
826 errx(1, "show_oops failed");
829 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
830 gtk_widget_hide(t
->cmd
);
831 gtk_widget_show(t
->oops
);
834 /* XXX collapse with show_oops */
836 show_oops_s(const char *fmt
, ...)
840 struct tab
*ti
, *t
= NULL
;
845 TAILQ_FOREACH(ti
, &tabs
, entry
)
846 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
854 if (vasprintf(&msg
, fmt
, ap
) == -1)
855 errx(1, "show_oops_s failed");
858 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
859 gtk_widget_hide(t
->cmd
);
860 gtk_widget_show(t
->oops
);
864 get_as_string(struct settings
*s
)
875 warnx("get_as_string skip %s\n", s
->name
);
876 } else if (s
->type
== XT_S_INT
)
877 r
= g_strdup_printf("%d", *s
->ival
);
878 else if (s
->type
== XT_S_STR
)
879 r
= g_strdup(*s
->sval
);
881 r
= g_strdup_printf("INVALID TYPE");
887 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
892 for (i
= 0; i
< LENGTH(rs
); i
++) {
893 if (rs
[i
].s
&& rs
[i
].s
->walk
)
894 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
896 s
= get_as_string(&rs
[i
]);
897 cb(&rs
[i
], s
, cb_args
);
904 set_browser_mode(struct settings
*s
, char *val
)
906 if (!strcmp(val
, "whitelist")) {
907 browser_mode
= XT_COOKIE_WHITELIST
;
908 allow_volatile_cookies
= 0;
909 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
911 enable_cookie_whitelist
= 1;
912 read_only_cookies
= 0;
913 save_rejected_cookies
= 0;
914 session_timeout
= 3600;
916 enable_js_whitelist
= 1;
917 } else if (!strcmp(val
, "normal")) {
918 browser_mode
= XT_COOKIE_NORMAL
;
919 allow_volatile_cookies
= 0;
920 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
922 enable_cookie_whitelist
= 0;
923 read_only_cookies
= 0;
924 save_rejected_cookies
= 0;
925 session_timeout
= 3600;
927 enable_js_whitelist
= 0;
935 get_browser_mode(struct settings
*s
)
939 if (browser_mode
== XT_COOKIE_WHITELIST
)
940 r
= g_strdup("whitelist");
941 else if (browser_mode
== XT_COOKIE_NORMAL
)
942 r
= g_strdup("normal");
950 set_cookie_policy(struct settings
*s
, char *val
)
952 if (!strcmp(val
, "no3rdparty"))
953 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
954 else if (!strcmp(val
, "accept"))
955 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
956 else if (!strcmp(val
, "reject"))
957 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
965 get_cookie_policy(struct settings
*s
)
969 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
970 r
= g_strdup("no3rdparty");
971 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
972 r
= g_strdup("accept");
973 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
974 r
= g_strdup("reject");
982 get_download_dir(struct settings
*s
)
984 if (download_dir
[0] == '\0')
986 return (g_strdup(download_dir
));
990 set_download_dir(struct settings
*s
, char *val
)
993 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
994 pwd
->pw_dir
, &val
[1]);
996 strlcpy(download_dir
, val
, sizeof download_dir
);
1003 * We use these to prevent people putting xxxt:// URLs on
1004 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1006 #define XT_XTP_SES_KEY_SZ 8
1007 #define XT_XTP_SES_KEY_HEX_FMT \
1008 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1009 char *dl_session_key
; /* downloads */
1010 char *hl_session_key
; /* history list */
1011 char *cl_session_key
; /* cookie list */
1012 char *fl_session_key
; /* favorites list */
1014 char work_dir
[PATH_MAX
];
1015 char certs_dir
[PATH_MAX
];
1016 char cache_dir
[PATH_MAX
];
1017 char sessions_dir
[PATH_MAX
];
1018 char cookie_file
[PATH_MAX
];
1019 SoupURI
*proxy_uri
= NULL
;
1020 SoupSession
*session
;
1021 SoupCookieJar
*s_cookiejar
;
1022 SoupCookieJar
*p_cookiejar
;
1023 char rc_fname
[PATH_MAX
];
1025 struct mime_type_list mtl
;
1026 struct alias_list aliases
;
1029 void create_new_tab(char *, struct undo
*, int);
1030 void delete_tab(struct tab
*);
1031 void adjustfont_webkit(struct tab
*, int);
1032 int run_script(struct tab
*, char *);
1033 int download_rb_cmp(struct download
*, struct download
*);
1036 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1038 return (strcmp(h1
->uri
, h2
->uri
));
1040 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1043 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1045 return (strcmp(d1
->d
, d2
->d
));
1047 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1050 get_work_dir(struct settings
*s
)
1052 if (work_dir
[0] == '\0')
1054 return (g_strdup(work_dir
));
1058 set_work_dir(struct settings
*s
, char *val
)
1061 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1062 pwd
->pw_dir
, &val
[1]);
1064 strlcpy(work_dir
, val
, sizeof work_dir
);
1070 * generate a session key to secure xtp commands.
1071 * pass in a ptr to the key in question and it will
1072 * be modified in place.
1075 generate_xtp_session_key(char **key
)
1077 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1083 /* make a new one */
1084 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1085 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1086 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1087 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1089 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1093 * validate a xtp session key.
1097 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1099 if (strcmp(trusted
, untrusted
) != 0) {
1100 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1109 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1111 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1113 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1115 struct valid_url_types
{
1126 valid_url_type(char *url
)
1130 for (i
= 0; i
< LENGTH(vut
); i
++)
1131 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1138 print_cookie(char *msg
, SoupCookie
*c
)
1144 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1145 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1146 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1147 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1148 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1149 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1150 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1151 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1152 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1153 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1157 walk_alias(struct settings
*s
,
1158 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1163 if (s
== NULL
|| cb
== NULL
) {
1164 show_oops_s("walk_alias invalid parameters");
1168 TAILQ_FOREACH(a
, &aliases
, entry
) {
1169 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1170 cb(s
, str
, cb_args
);
1176 match_alias(char *url_in
)
1180 char *url_out
= NULL
, *search
, *enc_arg
;
1182 search
= g_strdup(url_in
);
1184 if (strsep(&arg
, " \t") == NULL
) {
1185 show_oops_s("match_alias: NULL URL");
1189 TAILQ_FOREACH(a
, &aliases
, entry
) {
1190 if (!strcmp(search
, a
->a_name
))
1195 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1198 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1199 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1202 url_out
= g_strdup(a
->a_uri
);
1210 guess_url_type(char *url_in
)
1213 char *url_out
= NULL
, *enc_search
= NULL
;
1215 url_out
= match_alias(url_in
);
1216 if (url_out
!= NULL
)
1221 * If there is no dot nor slash in the string and it isn't a
1222 * path to a local file and doesn't resolves to an IP, assume
1223 * that the user wants to search for the string.
1226 if (strchr(url_in
, '.') == NULL
&&
1227 strchr(url_in
, '/') == NULL
&&
1228 stat(url_in
, &sb
) != 0 &&
1229 gethostbyname(url_in
) == NULL
) {
1231 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1232 url_out
= g_strdup_printf(search_string
, enc_search
);
1238 /* XXX not sure about this heuristic */
1239 if (stat(url_in
, &sb
) == 0)
1240 url_out
= g_strdup_printf("file://%s", url_in
);
1242 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1244 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1250 load_uri(struct tab
*t
, gchar
*uri
)
1253 gchar
*newuri
= NULL
;
1259 /* Strip leading spaces. */
1260 while(*uri
&& isspace(*uri
))
1263 if (strlen(uri
) == 0) {
1268 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1269 for (i
= 0; i
< LENGTH(about_list
); i
++)
1270 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1271 bzero(&args
, sizeof args
);
1272 about_list
[i
].func(t
, &args
);
1275 show_oops(t
, "invalid about page");
1279 if (valid_url_type(uri
)) {
1280 newuri
= guess_url_type(uri
);
1284 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1285 webkit_web_view_load_uri(t
->wv
, uri
);
1292 get_uri(WebKitWebView
*wv
)
1294 WebKitWebFrame
*frame
;
1297 frame
= webkit_web_view_get_main_frame(wv
);
1298 uri
= webkit_web_frame_get_uri(frame
);
1300 if (uri
&& strlen(uri
) > 0)
1307 add_alias(struct settings
*s
, char *line
)
1310 struct alias
*a
= NULL
;
1312 if (s
== NULL
|| line
== NULL
) {
1313 show_oops_s("add_alias invalid parameters");
1318 a
= g_malloc(sizeof(*a
));
1320 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1321 show_oops_s("add_alias: incomplete alias definition");
1324 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1325 show_oops_s("add_alias: invalid alias definition");
1329 a
->a_name
= g_strdup(alias
);
1330 a
->a_uri
= g_strdup(l
);
1332 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1334 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1344 add_mime_type(struct settings
*s
, char *line
)
1348 struct mime_type
*m
= NULL
;
1350 /* XXX this could be smarter */
1353 show_oops_s("add_mime_type invalid parameters");
1358 m
= g_malloc(sizeof(*m
));
1360 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1361 show_oops_s("add_mime_type: invalid mime_type");
1364 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1365 mime_type
[strlen(mime_type
) - 1] = '\0';
1370 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1371 show_oops_s("add_mime_type: invalid mime_type");
1375 m
->mt_type
= g_strdup(mime_type
);
1376 m
->mt_action
= g_strdup(l
);
1378 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1379 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1381 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1391 find_mime_type(char *mime_type
)
1393 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1395 TAILQ_FOREACH(m
, &mtl
, entry
) {
1396 if (m
->mt_default
&&
1397 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1400 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1413 walk_mime_type(struct settings
*s
,
1414 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1416 struct mime_type
*m
;
1419 if (s
== NULL
|| cb
== NULL
)
1420 show_oops_s("walk_mime_type invalid parameters");
1422 TAILQ_FOREACH(m
, &mtl
, entry
) {
1423 str
= g_strdup_printf("%s%s --> %s",
1425 m
->mt_default
? "*" : "",
1427 cb(s
, str
, cb_args
);
1433 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1438 if (str
== NULL
|| wl
== NULL
)
1440 if (strlen(str
) < 2)
1443 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1445 /* treat *.moo.com the same as .moo.com */
1446 if (str
[0] == '*' && str
[1] == '.')
1448 else if (str
[0] == '.')
1453 d
= g_malloc(sizeof *d
);
1455 d
->d
= g_strdup_printf(".%s", str
);
1457 d
->d
= g_strdup(str
);
1460 if (RB_INSERT(domain_list
, wl
, d
))
1463 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1474 add_cookie_wl(struct settings
*s
, char *entry
)
1476 wl_add(entry
, &c_wl
, 1);
1481 walk_cookie_wl(struct settings
*s
,
1482 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1486 if (s
== NULL
|| cb
== NULL
) {
1487 show_oops_s("walk_cookie_wl invalid parameters");
1491 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1492 cb(s
, d
->d
, cb_args
);
1496 walk_js_wl(struct settings
*s
,
1497 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1501 if (s
== NULL
|| cb
== NULL
) {
1502 show_oops_s("walk_js_wl invalid parameters");
1506 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1507 cb(s
, d
->d
, cb_args
);
1511 add_js_wl(struct settings
*s
, char *entry
)
1513 wl_add(entry
, &js_wl
, 1 /* persistent */);
1518 wl_find(const gchar
*search
, struct domain_list
*wl
)
1521 struct domain
*d
= NULL
, dfind
;
1524 if (search
== NULL
|| wl
== NULL
)
1526 if (strlen(search
) < 2)
1529 if (search
[0] != '.')
1530 s
= g_strdup_printf(".%s", search
);
1532 s
= g_strdup(search
);
1534 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1537 d
= RB_FIND(domain_list
, wl
, &dfind
);
1551 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1557 if (s
== NULL
|| wl
== NULL
)
1560 if (!strncmp(s
, "http://", strlen("http://")))
1561 s
= &s
[strlen("http://")];
1562 else if (!strncmp(s
, "https://", strlen("https://")))
1563 s
= &s
[strlen("https://")];
1568 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1569 /* chop string at first slash */
1570 if (s
[i
] == '/' || s
[i
] == '\0') {
1573 r
= wl_find(ss
, wl
);
1582 get_toplevel_domain(char *domain
)
1589 if (strlen(domain
) < 2)
1592 s
= &domain
[strlen(domain
) - 1];
1593 while (s
!= domain
) {
1609 settings_add(char *var
, char *val
)
1615 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1616 if (strcmp(var
, rs
[i
].name
))
1620 if (rs
[i
].s
->set(&rs
[i
], val
))
1621 errx(1, "invalid value for %s", var
);
1625 switch (rs
[i
].type
) {
1634 errx(1, "invalid sval for %s",
1643 errx(1, "invalid type for %s", var
);
1652 config_parse(char *filename
, int runtime
)
1655 char *line
, *cp
, *var
, *val
;
1656 size_t len
, lineno
= 0;
1658 char file
[PATH_MAX
];
1661 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1663 if (filename
== NULL
)
1666 if (runtime
&& runtime_settings
[0] != '\0') {
1667 snprintf(file
, sizeof file
, "%s/%s",
1668 work_dir
, runtime_settings
);
1669 if (stat(file
, &sb
)) {
1670 warnx("runtime file doesn't exist, creating it");
1671 if ((f
= fopen(file
, "w")) == NULL
)
1673 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1677 strlcpy(file
, filename
, sizeof file
);
1679 if ((config
= fopen(file
, "r")) == NULL
) {
1680 warn("config_parse: cannot open %s", filename
);
1685 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1686 if (feof(config
) || ferror(config
))
1690 cp
+= (long)strspn(cp
, WS
);
1691 if (cp
[0] == '\0') {
1697 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1698 errx(1, "invalid config file entry: %s", line
);
1700 cp
+= (long)strspn(cp
, WS
);
1702 if ((val
= strsep(&cp
, "\0")) == NULL
)
1705 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1706 handled
= settings_add(var
, val
);
1708 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1717 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1723 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1727 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1730 JSStringGetUTF8CString(jsref
, s
, l
);
1731 JSStringRelease(jsref
);
1737 disable_hints(struct tab
*t
)
1739 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1740 bzero(t
->hint_num
, sizeof t
->hint_num
);
1741 run_script(t
, "vimprobable_clear()");
1743 t
->hint_mode
= XT_HINT_NONE
;
1747 enable_hints(struct tab
*t
)
1749 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1750 run_script(t
, "vimprobable_show_hints()");
1752 t
->hint_mode
= XT_HINT_NONE
;
1755 #define XT_JS_OPEN ("open;")
1756 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1757 #define XT_JS_FIRE ("fire;")
1758 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1759 #define XT_JS_FOUND ("found;")
1760 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1763 run_script(struct tab
*t
, char *s
)
1765 JSGlobalContextRef ctx
;
1766 WebKitWebFrame
*frame
;
1768 JSValueRef val
, exception
;
1771 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1772 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1774 frame
= webkit_web_view_get_main_frame(t
->wv
);
1775 ctx
= webkit_web_frame_get_global_context(frame
);
1777 str
= JSStringCreateWithUTF8CString(s
);
1778 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1779 NULL
, 0, &exception
);
1780 JSStringRelease(str
);
1782 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1784 es
= js_ref_to_string(ctx
, exception
);
1785 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1789 es
= js_ref_to_string(ctx
, val
);
1790 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1792 /* handle return value right here */
1793 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1795 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1798 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1799 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1800 &es
[XT_JS_FIRE_LEN
]);
1805 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1806 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1817 hint(struct tab
*t
, struct karg
*args
)
1820 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1822 if (t
->hints_on
== 0)
1831 * Doesn't work fully, due to the following bug:
1832 * https://bugs.webkit.org/show_bug.cgi?id=51747
1835 restore_global_history(void)
1837 char file
[PATH_MAX
];
1843 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1845 if ((f
= fopen(file
, "r")) == NULL
) {
1846 warnx("%s: fopen", __func__
);
1851 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1852 if (feof(f
) || ferror(f
))
1855 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1856 if (feof(f
) || ferror(f
)) {
1858 warnx("%s: broken history file\n", __func__
);
1862 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1863 webkit_web_history_item_new_with_data(uri
, title
);
1864 h
= g_malloc(sizeof(struct history
));
1865 h
->uri
= g_strdup(uri
);
1866 h
->title
= g_strdup(title
);
1867 RB_INSERT(history_list
, &hl
, h
);
1869 warnx("%s: failed to restore history\n", __func__
);
1885 save_global_history_to_disk(struct tab
*t
)
1887 char file
[PATH_MAX
];
1891 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1893 if ((f
= fopen(file
, "w")) == NULL
) {
1894 show_oops(t
, "%s: global history file: %s",
1895 __func__
, strerror(errno
));
1899 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1900 if (h
->uri
&& h
->title
)
1901 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1910 quit(struct tab
*t
, struct karg
*args
)
1912 if (save_global_history
)
1913 save_global_history_to_disk(t
);
1921 open_tabs(struct tab
*t
, struct karg
*a
)
1923 char file
[PATH_MAX
];
1927 struct tab
*ti
, *tt
;
1932 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1933 if ((f
= fopen(file
, "r")) == NULL
)
1936 ti
= TAILQ_LAST(&tabs
, tab_list
);
1939 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1940 if (feof(f
) || ferror(f
))
1943 /* retrieve session name */
1944 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
1945 strlcpy(named_session
,
1946 &uri
[strlen(XT_SAVE_SESSION_ID
)],
1947 sizeof named_session
);
1951 if (uri
&& strlen(uri
))
1952 create_new_tab(uri
, NULL
, 1);
1958 /* close open tabs */
1959 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
1961 tt
= TAILQ_FIRST(&tabs
);
1980 restore_saved_tabs(void)
1982 char file
[PATH_MAX
];
1983 int unlink_file
= 0;
1988 snprintf(file
, sizeof file
, "%s/%s",
1989 sessions_dir
, XT_RESTART_TABS_FILE
);
1990 if (stat(file
, &sb
) == -1)
1991 a
.s
= XT_SAVED_TABS_FILE
;
1994 a
.s
= XT_RESTART_TABS_FILE
;
1997 a
.i
= XT_SES_DONOTHING
;
1998 rv
= open_tabs(NULL
, &a
);
2007 save_tabs(struct tab
*t
, struct karg
*a
)
2009 char file
[PATH_MAX
];
2014 const gchar
**arr
= NULL
;
2019 snprintf(file
, sizeof file
, "%s/%s",
2020 sessions_dir
, named_session
);
2022 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2024 if ((f
= fopen(file
, "w")) == NULL
) {
2025 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2029 /* save session name */
2030 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2032 /* save tabs, in the order they are arranged in the notebook */
2033 TAILQ_FOREACH(ti
, &tabs
, entry
)
2036 arr
= g_malloc0(len
* sizeof(gchar
*));
2038 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2039 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2040 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2043 for (i
= 0; i
< len
; i
++)
2045 fprintf(f
, "%s\n", arr
[i
]);
2054 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2066 yank_uri(struct tab
*t
, struct karg
*args
)
2069 GtkClipboard
*clipboard
;
2071 if ((uri
= get_uri(t
->wv
)) == NULL
)
2074 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2075 gtk_clipboard_set_text(clipboard
, uri
, -1);
2086 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
2088 struct paste_args
*pap
;
2090 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
2093 pap
= (struct paste_args
*)data
;
2096 case XT_PASTE_CURRENT_TAB
:
2097 load_uri(pap
->t
, (gchar
*)text
);
2099 case XT_PASTE_NEW_TAB
:
2100 create_new_tab((gchar
*)text
, NULL
, 1);
2108 paste_uri(struct tab
*t
, struct karg
*args
)
2110 GtkClipboard
*clipboard
;
2111 struct paste_args
*pap
;
2113 pap
= g_malloc(sizeof(struct paste_args
));
2118 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2119 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
2125 find_domain(const gchar
*s
, int add_dot
)
2128 char *r
= NULL
, *ss
= NULL
;
2133 if (!strncmp(s
, "http://", strlen("http://")))
2134 s
= &s
[strlen("http://")];
2135 else if (!strncmp(s
, "https://", strlen("https://")))
2136 s
= &s
[strlen("https://")];
2142 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2143 /* chop string at first slash */
2144 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2147 r
= g_strdup_printf(".%s", ss
);
2158 toggle_cwl(struct tab
*t
, struct karg
*args
)
2162 char *dom
= NULL
, *dom_toggle
= NULL
;
2168 uri
= get_uri(t
->wv
);
2169 dom
= find_domain(uri
, 1);
2170 d
= wl_find(dom
, &c_wl
);
2177 if (args
->i
& XT_WL_TOGGLE
)
2179 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2181 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2184 if (args
->i
& XT_WL_TOPLEVEL
)
2185 dom_toggle
= get_toplevel_domain(dom
);
2190 /* enable cookies for domain */
2191 wl_add(dom_toggle
, &c_wl
, 0);
2193 /* disable cookies for domain */
2194 RB_REMOVE(domain_list
, &c_wl
, d
);
2196 webkit_web_view_reload(t
->wv
);
2203 toggle_js(struct tab
*t
, struct karg
*args
)
2208 char *dom
= NULL
, *dom_toggle
= NULL
;
2213 g_object_get(G_OBJECT(t
->settings
),
2214 "enable-scripts", &es
, (char *)NULL
);
2215 if (args
->i
& XT_WL_TOGGLE
)
2217 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2219 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2224 uri
= get_uri(t
->wv
);
2225 dom
= find_domain(uri
, 1);
2227 if (uri
== NULL
|| dom
== NULL
) {
2228 show_oops(t
, "Can't toggle domain in JavaScript white list");
2232 if (args
->i
& XT_WL_TOPLEVEL
)
2233 dom_toggle
= get_toplevel_domain(dom
);
2238 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2239 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2241 d
= wl_find(dom_toggle
, &js_wl
);
2243 RB_REMOVE(domain_list
, &js_wl
, d
);
2244 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2246 g_object_set(G_OBJECT(t
->settings
),
2247 "enable-scripts", es
, (char *)NULL
);
2248 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2249 webkit_web_view_reload(t
->wv
);
2257 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2261 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2266 toggle_src(struct tab
*t
, struct karg
*args
)
2273 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2274 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2275 webkit_web_view_reload(t
->wv
);
2281 focus_webview(struct tab
*t
)
2286 /* only grab focus if we are visible */
2287 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2288 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2292 focus(struct tab
*t
, struct karg
*args
)
2294 if (t
== NULL
|| args
== NULL
)
2300 if (args
->i
== XT_FOCUS_URI
)
2301 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2302 else if (args
->i
== XT_FOCUS_SEARCH
)
2303 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2309 stats(struct tab
*t
, struct karg
*args
)
2311 char *stats
, *s
, line
[64 * 1024];
2312 uint64_t line_count
= 0;
2316 show_oops_s("stats invalid parameters");
2319 if (save_rejected_cookies
) {
2320 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2322 s
= fgets(line
, sizeof line
, r_cookie_f
);
2323 if (s
== NULL
|| feof(r_cookie_f
) ||
2329 snprintf(line
, sizeof line
,
2330 "<br>Cookies blocked(*) total: %llu", line_count
);
2332 show_oops(t
, "Can't open blocked cookies file: %s",
2336 stats
= g_strdup_printf(XT_DOCTYPE
2339 "<title>Statistics</title>"
2341 "<h1>Statistics</h1>"
2343 "Cookies blocked(*) this session: %llu"
2345 "<p><small><b>*</b> results vary based on settings"
2351 load_webkit_string(t
, stats
, XT_URI_ABOUT_STATS
);
2358 blank(struct tab
*t
, struct karg
*args
)
2361 show_oops_s("about invalid parameters");
2363 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2368 about(struct tab
*t
, struct karg
*args
)
2373 show_oops_s("about invalid parameters");
2375 about
= g_strdup_printf(XT_DOCTYPE
2378 "<title>About</title>"
2382 "<b>Version: %s</b><p>"
2385 "<li>Marco Peereboom <marco@peereboom.us></li>"
2386 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2387 "<li>Edd Barrett <vext01@gmail.com> </li>"
2388 "<li>Todd T. Fries <todd@fries.net> </li>"
2390 "Copyrights and licenses can be found on the XXXterm "
2391 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2397 load_webkit_string(t
, about
, XT_URI_ABOUT_ABOUT
);
2404 help(struct tab
*t
, struct karg
*args
)
2409 show_oops_s("help invalid parameters");
2414 "<title>XXXterm</title>"
2415 "<meta http-equiv=\"REFRESH\" content=\"0;"
2416 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2419 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2420 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2421 "cgi-bin/man-cgi?xxxterm</a>"
2426 load_webkit_string(t
, help
, XT_URI_ABOUT_HELP
);
2432 * update all favorite tabs apart from one. Pass NULL if
2433 * you want to update all.
2436 update_favorite_tabs(struct tab
*apart_from
)
2439 if (!updating_fl_tabs
) {
2440 updating_fl_tabs
= 1; /* stop infinite recursion */
2441 TAILQ_FOREACH(t
, &tabs
, entry
)
2442 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2443 && (t
!= apart_from
))
2444 xtp_page_fl(t
, NULL
);
2445 updating_fl_tabs
= 0;
2449 /* show a list of favorites (bookmarks) */
2451 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2453 char file
[PATH_MAX
];
2455 char *uri
= NULL
, *title
= NULL
;
2456 size_t len
, lineno
= 0;
2458 char *header
, *body
, *tmp
, *html
= NULL
;
2460 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2463 warn("%s: bad param", __func__
);
2465 /* mark tab as favorite list */
2466 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2468 /* new session key */
2469 if (!updating_fl_tabs
)
2470 generate_xtp_session_key(&fl_session_key
);
2472 /* open favorites */
2473 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2474 if ((f
= fopen(file
, "r")) == NULL
) {
2475 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2480 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2481 "<title>Favorites</title>\n"
2484 "<h1>Favorites</h1>\n",
2488 body
= g_strdup_printf("<div align='center'><table><tr>"
2489 "<th style='width: 4%%'>#</th><th>Link</th>"
2490 "<th style='width: 15%%'>Remove</th></tr>\n");
2493 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2494 if (feof(f
) || ferror(f
))
2502 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2503 if (feof(f
) || ferror(f
)) {
2504 show_oops(t
, "favorites file corrupt");
2510 body
= g_strdup_printf("%s<tr>"
2512 "<td><a href='%s'>%s</a></td>"
2513 "<td style='text-align: center'>"
2514 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2516 body
, i
, uri
, title
,
2517 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2529 /* if none, say so */
2532 body
= g_strdup_printf("%s<tr>"
2533 "<td colspan='3' style='text-align: center'>"
2534 "No favorites - To add one use the 'favadd' command."
2535 "</td></tr>", body
);
2546 html
= g_strdup_printf("%s%s</table></div></html>",
2548 load_webkit_string(t
, html
, XT_URI_ABOUT_FAVORITES
);
2551 update_favorite_tabs(t
);
2564 getparams(char *cmd
, char *cmp
)
2569 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2570 rv
= cmd
+ strlen(cmp
);
2573 if (strlen(rv
) == 0)
2582 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2583 size_t cert_count
, char *title
)
2585 gnutls_datum_t cinfo
;
2586 char *tmp
, *header
, *body
, *footer
;
2589 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2590 footer
= g_strdup("</body></html>");
2591 body
= g_strdup("");
2593 for (i
= 0; i
< cert_count
; i
++) {
2594 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2599 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2600 body
, i
, cinfo
.data
);
2601 gnutls_free(cinfo
.data
);
2605 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2609 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2614 ca_cmd(struct tab
*t
, struct karg
*args
)
2617 int rv
= 1, certs
= 0, certs_read
;
2620 gnutls_x509_crt_t
*c
= NULL
;
2621 char *certs_buf
= NULL
, *s
;
2623 /* yeah yeah stat race */
2624 if (stat(ssl_ca_file
, &sb
)) {
2625 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2629 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2630 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2634 certs_buf
= g_malloc(sb
.st_size
+ 1);
2635 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2636 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2639 certs_buf
[sb
.st_size
] = '\0';
2642 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2644 s
+= strlen("BEGIN CERTIFICATE");
2647 bzero(&dt
, sizeof dt
);
2648 dt
.data
= certs_buf
;
2649 dt
.size
= sb
.st_size
;
2650 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2651 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2652 GNUTLS_X509_FMT_PEM
, 0);
2653 if (certs_read
<= 0) {
2654 show_oops(t
, "No cert(s) available");
2657 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2670 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2673 struct addrinfo hints
, *res
= NULL
, *ai
;
2677 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2680 su
= soup_uri_new(uri
);
2683 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2686 snprintf(port
, sizeof port
, "%d", su
->port
);
2687 bzero(&hints
, sizeof(struct addrinfo
));
2688 hints
.ai_flags
= AI_CANONNAME
;
2689 hints
.ai_family
= AF_UNSPEC
;
2690 hints
.ai_socktype
= SOCK_STREAM
;
2692 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2695 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2696 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2699 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2702 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2706 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2711 strlcpy(domain
, su
->host
, domain_sz
);
2722 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2725 gnutls_deinit(gsession
);
2727 gnutls_certificate_free_credentials(xcred
);
2733 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2734 gnutls_certificate_credentials_t
*xc
)
2736 gnutls_certificate_credentials_t xcred
;
2737 gnutls_session_t gsession
;
2740 if (gs
== NULL
|| xc
== NULL
)
2746 gnutls_certificate_allocate_credentials(&xcred
);
2747 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2748 GNUTLS_X509_FMT_PEM
);
2749 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2750 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2751 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2752 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2753 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2754 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2756 gnutls_error_is_fatal(rv
),
2757 gnutls_strerror_name(rv
));
2758 stop_tls(gsession
, xcred
);
2762 gnutls_credentials_type_t cred
;
2763 cred
= gnutls_auth_get_type(gsession
);
2764 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2765 stop_tls(gsession
, xcred
);
2777 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2781 const gnutls_datum_t
*cl
;
2782 gnutls_x509_crt_t
*all_certs
;
2785 if (certs
== NULL
|| cert_count
== NULL
)
2787 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2789 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2793 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2794 for (i
= 0; i
< len
; i
++) {
2795 gnutls_x509_crt_init(&all_certs
[i
]);
2796 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2797 GNUTLS_X509_FMT_PEM
< 0)) {
2811 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2815 for (i
= 0; i
< cert_count
; i
++)
2816 gnutls_x509_crt_deinit(certs
[i
]);
2821 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2822 size_t cert_count
, char *domain
)
2825 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2830 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2833 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2834 if ((f
= fopen(file
, "w")) == NULL
) {
2835 show_oops(t
, "Can't create cert file %s %s",
2836 file
, strerror(errno
));
2840 for (i
= 0; i
< cert_count
; i
++) {
2841 cert_buf_sz
= sizeof cert_buf
;
2842 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2843 cert_buf
, &cert_buf_sz
)) {
2844 show_oops(t
, "gnutls_x509_crt_export failed");
2847 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2848 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2853 /* not the best spot but oh well */
2854 gdk_color_parse("lightblue", &color
);
2855 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2856 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2857 gdk_color_parse("black", &color
);
2858 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2864 load_compare_cert(struct tab
*t
, struct karg
*args
)
2867 char domain
[8182], file
[PATH_MAX
];
2868 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2869 int s
= -1, rv
= 1, i
;
2873 gnutls_session_t gsession
;
2874 gnutls_x509_crt_t
*certs
;
2875 gnutls_certificate_credentials_t xcred
;
2880 if ((uri
= get_uri(t
->wv
)) == NULL
)
2883 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2887 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2888 show_oops(t
, "Start TLS failed");
2893 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2894 show_oops(t
, "Can't get connection certificates");
2898 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2899 if ((f
= fopen(file
, "r")) == NULL
)
2902 for (i
= 0; i
< cert_count
; i
++) {
2903 cert_buf_sz
= sizeof cert_buf
;
2904 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2905 cert_buf
, &cert_buf_sz
)) {
2908 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2909 rv
= -1; /* critical */
2912 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2913 rv
= -1; /* critical */
2922 free_connection_certs(certs
, cert_count
);
2924 /* we close the socket first for speed */
2927 stop_tls(gsession
, xcred
);
2933 cert_cmd(struct tab
*t
, struct karg
*args
)
2936 char *action
, domain
[8182];
2939 gnutls_session_t gsession
;
2940 gnutls_x509_crt_t
*certs
;
2941 gnutls_certificate_credentials_t xcred
;
2946 if ((action
= getparams(args
->s
, "cert")))
2951 if ((uri
= get_uri(t
->wv
)) == NULL
) {
2952 show_oops(t
, "Invalid URI");
2956 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2957 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2962 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2963 show_oops(t
, "Start TLS failed");
2968 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2969 show_oops(t
, "get_connection_certs failed");
2973 if (!strcmp(action
, "show"))
2974 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2975 else if (!strcmp(action
, "save"))
2976 save_certs(t
, certs
, cert_count
, domain
);
2978 show_oops(t
, "Invalid command: %s", action
);
2980 free_connection_certs(certs
, cert_count
);
2982 /* we close the socket first for speed */
2985 stop_tls(gsession
, xcred
);
2991 remove_cookie(int index
)
2997 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
2999 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3001 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3005 print_cookie("remove cookie", c
);
3006 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3011 soup_cookies_free(cf
);
3017 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
3020 char *tmp
, *header
, *body
, *footer
;
3021 int p_js
= 0, s_js
= 0;
3023 /* we set this to indicate we want to manually do navaction */
3024 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3026 if (g_str_has_prefix(args
, "show a") ||
3027 !strcmp(args
, "show")) {
3031 } else if (g_str_has_prefix(args
, "show p")) {
3032 /* show persistent */
3034 } else if (g_str_has_prefix(args
, "show s")) {
3040 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
3042 footer
= g_strdup("</body></html>");
3043 body
= g_strdup("");
3048 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3050 RB_FOREACH(d
, domain_list
, wl
) {
3054 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3062 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3064 RB_FOREACH(d
, domain_list
, wl
) {
3068 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3073 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3078 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3080 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3086 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3088 char file
[PATH_MAX
];
3090 char *line
= NULL
, *lt
= NULL
;
3093 char *dom
= NULL
, *dom_save
= NULL
;
3100 if (t
== NULL
|| args
== NULL
)
3103 if (runtime_settings
[0] == '\0')
3106 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3107 if ((f
= fopen(file
, "r+")) == NULL
)
3110 uri
= get_uri(t
->wv
);
3111 dom
= find_domain(uri
, 1);
3112 if (uri
== NULL
|| dom
== NULL
) {
3113 show_oops(t
, "Can't add domain to %s white list",
3114 js
? "JavaScript" : "cookie");
3118 if (g_str_has_prefix(args
->s
, "save d")) {
3120 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3121 show_oops(t
, "invalid domain: %s", dom
);
3124 flags
= XT_WL_TOPLEVEL
;
3125 } else if (g_str_has_prefix(args
->s
, "save f") ||
3126 !strcmp(args
->s
, "save")) {
3131 show_oops(t
, "invalid command: %s", args
->s
);
3135 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3138 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3141 if (!strcmp(line
, lt
))
3147 fprintf(f
, "%s\n", lt
);
3152 d
= wl_find(dom_save
, &js_wl
);
3154 settings_add("js_wl", dom_save
);
3155 d
= wl_find(dom_save
, &js_wl
);
3159 d
= wl_find(dom_save
, &c_wl
);
3161 settings_add("cookie_wl", dom_save
);
3162 d
= wl_find(dom_save
, &c_wl
);
3166 /* find and add to persistent jar */
3167 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3168 for (;cf
; cf
= cf
->next
) {
3170 if (!strcmp(dom_save
, ci
->domain
) ||
3171 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3172 c
= soup_cookie_copy(ci
);
3173 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3176 soup_cookies_free(cf
);
3194 js_show_wl(struct tab
*t
, struct karg
*args
)
3196 wl_show(t
, "show all", "JavaScript White List", &js_wl
);
3202 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3204 wl_show(t
, "show all", "Cookie White List", &c_wl
);
3210 cookie_cmd(struct tab
*t
, struct karg
*args
)
3215 if ((cmd
= getparams(args
->s
, "cookie")))
3221 if (g_str_has_prefix(cmd
, "show")) {
3222 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
3223 } else if (g_str_has_prefix(cmd
, "save")) {
3226 } else if (g_str_has_prefix(cmd
, "toggle")) {
3228 if (g_str_has_prefix(cmd
, "toggle d"))
3229 a
.i
|= XT_WL_TOPLEVEL
;
3233 } else if (g_str_has_prefix(cmd
, "delete")) {
3234 show_oops(t
, "'cookie delete' currently unimplemented");
3236 show_oops(t
, "unknown cookie command: %s", cmd
);
3242 js_cmd(struct tab
*t
, struct karg
*args
)
3247 if ((cmd
= getparams(args
->s
, "js")))
3252 if (g_str_has_prefix(cmd
, "show")) {
3253 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
3254 } else if (g_str_has_prefix(cmd
, "save")) {
3257 } else if (g_str_has_prefix(cmd
, "toggle")) {
3259 if (g_str_has_prefix(cmd
, "toggle d"))
3260 a
.i
|= XT_WL_TOPLEVEL
;
3264 } else if (g_str_has_prefix(cmd
, "delete")) {
3265 show_oops(t
, "'js delete' currently unimplemented");
3267 show_oops(t
, "unknown js command: %s", cmd
);
3273 add_favorite(struct tab
*t
, struct karg
*args
)
3275 char file
[PATH_MAX
];
3278 size_t urilen
, linelen
;
3279 const gchar
*uri
, *title
;
3284 /* don't allow adding of xtp pages to favorites */
3285 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3286 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3290 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3291 if ((f
= fopen(file
, "r+")) == NULL
) {
3292 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3296 title
= webkit_web_view_get_title(t
->wv
);
3297 uri
= get_uri(t
->wv
);
3302 if (title
== NULL
|| uri
== NULL
) {
3303 show_oops(t
, "can't add page to favorites");
3307 urilen
= strlen(uri
);
3310 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3311 if (feof(f
) || ferror(f
))
3314 if (linelen
== urilen
&& !strcmp(line
, uri
))
3321 fprintf(f
, "\n%s\n%s", title
, uri
);
3327 update_favorite_tabs(NULL
);
3333 navaction(struct tab
*t
, struct karg
*args
)
3335 WebKitWebHistoryItem
*item
;
3337 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3338 t
->tab_id
, args
->i
);
3341 if (args
->i
== XT_NAV_BACK
)
3342 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3344 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3346 return (XT_CB_PASSTHROUGH
);
3347 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3349 return (XT_CB_PASSTHROUGH
);
3354 webkit_web_view_go_back(t
->wv
);
3356 case XT_NAV_FORWARD
:
3357 webkit_web_view_go_forward(t
->wv
);
3360 webkit_web_view_reload(t
->wv
);
3362 case XT_NAV_RELOAD_CACHE
:
3363 webkit_web_view_reload_bypass_cache(t
->wv
);
3366 return (XT_CB_PASSTHROUGH
);
3370 move(struct tab
*t
, struct karg
*args
)
3372 GtkAdjustment
*adjust
;
3373 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3378 case XT_MOVE_BOTTOM
:
3380 case XT_MOVE_PAGEDOWN
:
3381 case XT_MOVE_PAGEUP
:
3382 case XT_MOVE_HALFDOWN
:
3383 case XT_MOVE_HALFUP
:
3384 adjust
= t
->adjust_v
;
3387 adjust
= t
->adjust_h
;
3391 pos
= gtk_adjustment_get_value(adjust
);
3392 ps
= gtk_adjustment_get_page_size(adjust
);
3393 upper
= gtk_adjustment_get_upper(adjust
);
3394 lower
= gtk_adjustment_get_lower(adjust
);
3395 si
= gtk_adjustment_get_step_increment(adjust
);
3396 pi
= gtk_adjustment_get_page_increment(adjust
);
3399 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3400 "max %f si %f pi %f\n",
3401 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3402 pos
, ps
, upper
, lower
, max
, si
, pi
);
3408 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3413 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3415 case XT_MOVE_BOTTOM
:
3416 case XT_MOVE_FARRIGHT
:
3417 gtk_adjustment_set_value(adjust
, max
);
3420 case XT_MOVE_FARLEFT
:
3421 gtk_adjustment_set_value(adjust
, lower
);
3423 case XT_MOVE_PAGEDOWN
:
3425 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3427 case XT_MOVE_PAGEUP
:
3429 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3431 case XT_MOVE_HALFDOWN
:
3433 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3435 case XT_MOVE_HALFUP
:
3437 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3440 return (XT_CB_PASSTHROUGH
);
3443 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3445 return (XT_CB_HANDLED
);
3449 url_set_visibility(void)
3453 TAILQ_FOREACH(t
, &tabs
, entry
) {
3454 if (show_url
== 0) {
3455 gtk_widget_hide(t
->toolbar
);
3458 gtk_widget_show(t
->toolbar
);
3463 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3466 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3468 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3472 statusbar_set_visibility(void)
3476 TAILQ_FOREACH(t
, &tabs
, entry
) {
3477 if (show_statusbar
== 0) {
3478 gtk_widget_hide(t
->statusbar
);
3481 gtk_widget_show(t
->statusbar
);
3486 url_set(struct tab
*t
, int enable_url_entry
)
3491 show_url
= enable_url_entry
;
3493 if (enable_url_entry
) {
3494 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3495 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3496 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3498 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3499 GTK_ENTRY_ICON_PRIMARY
);
3501 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3502 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3503 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3504 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3510 fullscreen(struct tab
*t
, struct karg
*args
)
3512 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3515 return (XT_CB_PASSTHROUGH
);
3517 if (show_url
== 0) {
3525 url_set_visibility();
3526 notebook_tab_set_visibility(notebook
);
3528 return (XT_CB_HANDLED
);
3532 statusaction(struct tab
*t
, struct karg
*args
)
3534 int rv
= XT_CB_HANDLED
;
3536 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3539 return (XT_CB_PASSTHROUGH
);
3542 case XT_STATUSBAR_SHOW
:
3543 if (show_statusbar
== 0) {
3545 statusbar_set_visibility();
3548 case XT_STATUSBAR_HIDE
:
3549 if (show_statusbar
== 1) {
3551 statusbar_set_visibility();
3559 urlaction(struct tab
*t
, struct karg
*args
)
3561 int rv
= XT_CB_HANDLED
;
3563 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3566 return (XT_CB_PASSTHROUGH
);
3570 if (show_url
== 0) {
3572 url_set_visibility();
3576 if (show_url
== 1) {
3578 url_set_visibility();
3586 tabaction(struct tab
*t
, struct karg
*args
)
3588 int rv
= XT_CB_HANDLED
;
3592 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3595 return (XT_CB_PASSTHROUGH
);
3599 if ((url
= getparams(args
->s
, "tabnew")))
3600 create_new_tab(url
, NULL
, 1);
3602 create_new_tab(NULL
, NULL
, 1);
3607 case XT_TAB_DELQUIT
:
3608 if (gtk_notebook_get_n_pages(notebook
) > 1)
3614 if ((url
= getparams(args
->s
, "open")) ||
3615 ((url
= getparams(args
->s
, "op"))) ||
3616 ((url
= getparams(args
->s
, "o"))))
3619 rv
= XT_CB_PASSTHROUGH
;
3625 if (show_tabs
== 0) {
3627 notebook_tab_set_visibility(notebook
);
3631 if (show_tabs
== 1) {
3633 notebook_tab_set_visibility(notebook
);
3636 case XT_TAB_UNDO_CLOSE
:
3637 if (undo_count
== 0) {
3638 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3642 u
= TAILQ_FIRST(&undos
);
3643 create_new_tab(u
->uri
, u
, 1);
3645 TAILQ_REMOVE(&undos
, u
, entry
);
3647 /* u->history is freed in create_new_tab() */
3652 rv
= XT_CB_PASSTHROUGH
;
3666 resizetab(struct tab
*t
, struct karg
*args
)
3668 if (t
== NULL
|| args
== NULL
) {
3669 show_oops_s("resizetab invalid parameters");
3670 return (XT_CB_PASSTHROUGH
);
3673 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3674 t
->tab_id
, args
->i
);
3676 adjustfont_webkit(t
, args
->i
);
3678 return (XT_CB_HANDLED
);
3682 movetab(struct tab
*t
, struct karg
*args
)
3687 if (t
== NULL
|| args
== NULL
) {
3688 show_oops_s("movetab invalid parameters");
3689 return (XT_CB_PASSTHROUGH
);
3692 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3693 t
->tab_id
, args
->i
);
3695 if (args
->i
== XT_TAB_INVALID
)
3696 return (XT_CB_PASSTHROUGH
);
3698 if (args
->i
< XT_TAB_INVALID
) {
3699 /* next or previous tab */
3700 if (TAILQ_EMPTY(&tabs
))
3701 return (XT_CB_PASSTHROUGH
);
3705 /* if at the last page, loop around to the first */
3706 if (gtk_notebook_get_current_page(notebook
) ==
3707 gtk_notebook_get_n_pages(notebook
) - 1)
3708 gtk_notebook_set_current_page(notebook
, 0);
3710 gtk_notebook_next_page(notebook
);
3713 /* if at the first page, loop around to the last */
3714 if (gtk_notebook_current_page(notebook
) == 0)
3715 gtk_notebook_set_current_page(notebook
,
3716 gtk_notebook_get_n_pages(notebook
) - 1);
3718 gtk_notebook_prev_page(notebook
);
3721 gtk_notebook_set_current_page(notebook
, 0);
3724 gtk_notebook_set_current_page(notebook
, -1);
3727 return (XT_CB_PASSTHROUGH
);
3730 return (XT_CB_HANDLED
);
3735 if (t
->tab_id
== x
) {
3736 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3737 return (XT_CB_HANDLED
);
3740 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3741 if (tt
->tab_id
== x
) {
3742 gtk_notebook_set_current_page(notebook
, x
);
3743 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3749 return (XT_CB_HANDLED
);
3753 command(struct tab
*t
, struct karg
*args
)
3755 char *s
= NULL
, *ss
= NULL
;
3759 if (t
== NULL
|| args
== NULL
) {
3760 show_oops_s("command invalid parameters");
3761 return (XT_CB_PASSTHROUGH
);
3780 case XT_CMD_OPEN_CURRENT
:
3783 case XT_CMD_TABNEW_CURRENT
:
3784 if (!s
) /* FALL THROUGH? */
3786 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3787 ss
= g_strdup_printf("%s%s", s
, uri
);
3792 show_oops(t
, "command: invalid opcode %d", args
->i
);
3793 return (XT_CB_PASSTHROUGH
);
3796 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3798 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3799 gdk_color_parse("white", &color
);
3800 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3802 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3803 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3808 return (XT_CB_HANDLED
);
3812 * Return a new string with a download row (in html)
3813 * appended. Old string is freed.
3816 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3819 WebKitDownloadStatus stat
;
3820 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3822 char cur_sz
[FMT_SCALED_STRSIZE
];
3823 char tot_sz
[FMT_SCALED_STRSIZE
];
3826 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3828 /* All actions wil take this form:
3829 * xxxt://class/seskey
3831 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3832 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3834 stat
= webkit_download_get_status(dl
->download
);
3837 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3838 status_html
= g_strdup_printf("Finished");
3839 cmd_html
= g_strdup_printf(
3840 "<a href='%s%d/%d'>Remove</a>",
3841 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3843 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3844 /* gather size info */
3845 progress
= 100 * webkit_download_get_progress(dl
->download
);
3848 webkit_download_get_current_size(dl
->download
), cur_sz
);
3850 webkit_download_get_total_size(dl
->download
), tot_sz
);
3852 status_html
= g_strdup_printf(
3853 "<div style='width: 100%%' align='center'>"
3854 "<div class='progress-outer'>"
3855 "<div class='progress-inner' style='width: %.2f%%'>"
3856 "</div></div></div>"
3857 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3858 progress
, cur_sz
, tot_sz
, progress
);
3860 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3861 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3865 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3866 status_html
= g_strdup_printf("Cancelled");
3867 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3868 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3870 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3871 status_html
= g_strdup_printf("Error!");
3872 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3873 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3875 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3876 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3877 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3878 status_html
= g_strdup_printf("Starting");
3881 show_oops(t
, "%s: unknown download status", __func__
);
3884 new_html
= g_strdup_printf(
3885 "%s\n<tr><td>%s</td><td>%s</td>"
3886 "<td style='text-align:center'>%s</td></tr>\n",
3887 html
, basename(webkit_download_get_uri(dl
->download
)),
3888 status_html
, cmd_html
);
3892 g_free(status_html
);
3903 * update all download tabs apart from one. Pass NULL if
3904 * you want to update all.
3907 update_download_tabs(struct tab
*apart_from
)
3910 if (!updating_dl_tabs
) {
3911 updating_dl_tabs
= 1; /* stop infinite recursion */
3912 TAILQ_FOREACH(t
, &tabs
, entry
)
3913 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3914 && (t
!= apart_from
))
3915 xtp_page_dl(t
, NULL
);
3916 updating_dl_tabs
= 0;
3921 * update all cookie tabs apart from one. Pass NULL if
3922 * you want to update all.
3925 update_cookie_tabs(struct tab
*apart_from
)
3928 if (!updating_cl_tabs
) {
3929 updating_cl_tabs
= 1; /* stop infinite recursion */
3930 TAILQ_FOREACH(t
, &tabs
, entry
)
3931 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3932 && (t
!= apart_from
))
3933 xtp_page_cl(t
, NULL
);
3934 updating_cl_tabs
= 0;
3939 * update all history tabs apart from one. Pass NULL if
3940 * you want to update all.
3943 update_history_tabs(struct tab
*apart_from
)
3947 if (!updating_hl_tabs
) {
3948 updating_hl_tabs
= 1; /* stop infinite recursion */
3949 TAILQ_FOREACH(t
, &tabs
, entry
)
3950 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3951 && (t
!= apart_from
))
3952 xtp_page_hl(t
, NULL
);
3953 updating_hl_tabs
= 0;
3957 /* cookie management XTP page */
3959 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3961 char *header
, *body
, *footer
, *page
, *tmp
;
3962 int i
= 1; /* all ids start 1 */
3963 GSList
*sc
, *pc
, *pc_start
;
3965 char *type
, *table_headers
;
3966 char *last_domain
= strdup("");
3968 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3971 show_oops_s("%s invalid parameters", __func__
);
3974 /* mark this tab as cookie jar */
3975 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3977 /* Generate a new session key */
3978 if (!updating_cl_tabs
)
3979 generate_xtp_session_key(&cl_session_key
);
3982 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3983 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3984 "</head><body><h1>Cookie Jar</h1>\n");
3987 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
3994 "<th>HTTP<br />only</th>"
3995 "<th>Rm</th></tr>\n");
3997 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
3998 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4002 for (; sc
; sc
= sc
->next
) {
4005 if (strcmp(last_domain
, c
->domain
) != 0) {
4008 last_domain
= strdup(c
->domain
);
4012 body
= g_strdup_printf("%s</table></div>"
4014 body
, c
->domain
, table_headers
);
4018 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4019 c
->domain
, table_headers
);
4024 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4025 if (soup_cookie_equal(pc
->data
, c
)) {
4026 type
= "Session + Persistent";
4031 body
= g_strdup_printf(
4033 "<td style='width: text-align: center'>%s</td>"
4034 "<td style='width: 1px'>%s</td>"
4035 "<td style='width=70%%;overflow: visible'>"
4036 " <textarea rows='4'>%s</textarea>"
4040 "<td style='width: 1px; text-align: center'>%d</td>"
4041 "<td style='width: 1px; text-align: center'>%d</td>"
4042 "<td style='width: 1px; text-align: center'>"
4043 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4050 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4065 soup_cookies_free(sc
);
4066 soup_cookies_free(pc
);
4068 /* small message if there are none */
4070 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4071 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4075 footer
= g_strdup_printf("</table></div></body></html>");
4077 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4082 g_free(table_headers
);
4083 g_free(last_domain
);
4085 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4086 update_cookie_tabs(t
);
4094 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4096 char *header
, *body
, *footer
, *page
, *tmp
;
4098 int i
= 1; /* all ids start 1 */
4100 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4103 show_oops_s("%s invalid parameters", __func__
);
4107 /* mark this tab as history manager */
4108 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4110 /* Generate a new session key */
4111 if (!updating_hl_tabs
)
4112 generate_xtp_session_key(&hl_session_key
);
4115 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4116 "<title>History</title>\n"
4119 "<h1>History</h1>\n",
4123 body
= g_strdup_printf("<div align='center'><table><tr>"
4124 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4126 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4128 body
= g_strdup_printf(
4130 "<td><a href='%s'>%s</a></td>"
4132 "<td style='text-align: center'>"
4133 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4134 body
, h
->uri
, h
->uri
, h
->title
,
4135 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4136 XT_XTP_HL_REMOVE
, i
);
4142 /* small message if there are none */
4145 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4146 "colspan='3'>No History</td></tr>\n", body
);
4151 footer
= g_strdup_printf("</table></div></body></html>");
4153 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4156 * update all history manager tabs as the xtp session
4157 * key has now changed. No need to update the current tab.
4158 * Already did that above.
4160 update_history_tabs(t
);
4166 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4173 * Generate a web page detailing the status of any downloads
4176 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4178 struct download
*dl
;
4179 char *header
, *body
, *footer
, *page
, *tmp
;
4183 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4186 show_oops_s("%s invalid parameters", __func__
);
4189 /* mark as a download manager tab */
4190 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4193 * Generate a new session key for next page instance.
4194 * This only happens for the top level call to xtp_page_dl()
4195 * in which case updating_dl_tabs is 0.
4197 if (!updating_dl_tabs
)
4198 generate_xtp_session_key(&dl_session_key
);
4200 /* header - with refresh so as to update */
4201 if (refresh_interval
>= 1)
4202 ref
= g_strdup_printf(
4203 "<meta http-equiv='refresh' content='%u"
4204 ";url=%s%d/%s/%d' />\n",
4214 header
= g_strdup_printf(
4216 "<title>Downloads</title>\n%s%s</head>\n",
4217 XT_DOCTYPE XT_HTML_TAG
,
4221 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4222 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4223 "</p><table><tr><th style='width: 60%%'>"
4224 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4225 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4227 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4228 body
= xtp_page_dl_row(t
, body
, dl
);
4232 /* message if no downloads in list */
4235 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4236 " style='text-align: center'>"
4237 "No downloads</td></tr>\n", body
);
4242 footer
= g_strdup_printf("</table></div></body></html>");
4244 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4248 * update all download manager tabs as the xtp session
4249 * key has now changed. No need to update the current tab.
4250 * Already did that above.
4252 update_download_tabs(t
);
4259 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4266 search(struct tab
*t
, struct karg
*args
)
4270 if (t
== NULL
|| args
== NULL
) {
4271 show_oops_s("search invalid parameters");
4274 if (t
->search_text
== NULL
) {
4275 if (global_search
== NULL
)
4276 return (XT_CB_PASSTHROUGH
);
4278 t
->search_text
= g_strdup(global_search
);
4279 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4280 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4284 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4285 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4288 case XT_SEARCH_NEXT
:
4289 d
= t
->search_forward
;
4291 case XT_SEARCH_PREV
:
4292 d
= !t
->search_forward
;
4295 return (XT_CB_PASSTHROUGH
);
4298 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4300 return (XT_CB_HANDLED
);
4303 struct settings_args
{
4309 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4312 struct settings_args
*sa
= cb_args
;
4317 if (s
->flags
& XT_SF_RUNTIME
)
4323 *sa
->body
= g_strdup_printf(
4325 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4326 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4338 set(struct tab
*t
, struct karg
*args
)
4340 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
4342 struct settings_args sa
;
4344 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
4345 bzero(&sa
, sizeof sa
);
4349 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4350 "\n<head><title>Settings</title>\n"
4351 "</head><body><h1>Settings</h1>\n");
4354 body
= g_strdup_printf("<div align='center'><table><tr>"
4355 "<th align='left'>Setting</th>"
4356 "<th align='left'>Value</th></tr>\n");
4358 settings_walk(print_setting
, &sa
);
4361 /* small message if there are none */
4364 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4365 "colspan='2'>No settings</td></tr>\n", body
);
4370 footer
= g_strdup_printf("</table></div></body></html>");
4372 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4378 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4380 show_oops(t
, "Invalid command: %s", pars
);
4382 return (XT_CB_PASSTHROUGH
);
4386 session_save(struct tab
*t
, char *filename
, char **ret
)
4392 f
+= strlen("save");
4393 while (*f
== ' ' && *f
!= '\0')
4399 if (f
[0] == '.' || f
[0] == '/')
4403 if (save_tabs(t
, &a
))
4405 strlcpy(named_session
, f
, sizeof named_session
);
4413 session_open(struct tab
*t
, char *filename
, char **ret
)
4419 f
+= strlen("open");
4420 while (*f
== ' ' && *f
!= '\0')
4426 if (f
[0] == '.' || f
[0] == '/')
4430 a
.i
= XT_SES_CLOSETABS
;
4431 if (open_tabs(t
, &a
))
4434 strlcpy(named_session
, f
, sizeof named_session
);
4442 session_delete(struct tab
*t
, char *filename
, char **ret
)
4444 char file
[PATH_MAX
];
4448 f
+= strlen("delete");
4449 while (*f
== ' ' && *f
!= '\0')
4455 if (f
[0] == '.' || f
[0] == '/')
4458 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, f
);
4462 if (!strcmp(f
, named_session
))
4463 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4464 sizeof named_session
);
4472 session_cmd(struct tab
*t
, struct karg
*args
)
4474 char *action
= NULL
;
4475 char *filename
= NULL
;
4480 if ((action
= getparams(args
->s
, "session")))
4485 if (!strcmp(action
, "show"))
4486 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4487 XT_SAVED_TABS_FILE
: named_session
);
4488 else if (g_str_has_prefix(action
, "save ")) {
4489 if (session_save(t
, action
, &filename
)) {
4490 show_oops(t
, "Can't save session: %s",
4491 filename
? filename
: "INVALID");
4494 } else if (g_str_has_prefix(action
, "open ")) {
4495 if (session_open(t
, action
, &filename
)) {
4496 show_oops(t
, "Can't open session: %s",
4497 filename
? filename
: "INVALID");
4500 } else if (g_str_has_prefix(action
, "delete ")) {
4501 if (session_delete(t
, action
, &filename
)) {
4502 show_oops(t
, "Can't delete session: %s",
4503 filename
? filename
: "INVALID");
4507 show_oops(t
, "Invalid command: %s", action
);
4509 return (XT_CB_PASSTHROUGH
);
4513 * Make a hardcopy of the page
4516 print_page(struct tab
*t
, struct karg
*args
)
4518 WebKitWebFrame
*frame
;
4520 GtkPrintOperation
*op
;
4521 GtkPrintOperationAction action
;
4522 GtkPrintOperationResult print_res
;
4523 GError
*g_err
= NULL
;
4524 int marg_l
, marg_r
, marg_t
, marg_b
;
4526 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4528 ps
= gtk_page_setup_new();
4529 op
= gtk_print_operation_new();
4530 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4531 frame
= webkit_web_view_get_main_frame(t
->wv
);
4533 /* the default margins are too small, so we will bump them */
4534 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4535 XT_PRINT_EXTRA_MARGIN
;
4536 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4537 XT_PRINT_EXTRA_MARGIN
;
4538 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4539 XT_PRINT_EXTRA_MARGIN
;
4540 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4541 XT_PRINT_EXTRA_MARGIN
;
4544 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4545 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4546 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4547 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4549 gtk_print_operation_set_default_page_setup(op
, ps
);
4551 /* this appears to free 'op' and 'ps' */
4552 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4554 /* check it worked */
4555 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4556 show_oops_s("can't print: %s", g_err
->message
);
4557 g_error_free (g_err
);
4565 go_home(struct tab
*t
, struct karg
*args
)
4572 restart(struct tab
*t
, struct karg
*args
)
4576 a
.s
= XT_RESTART_TABS_FILE
;
4578 execvp(start_argv
[0], start_argv
);
4584 #define CTRL GDK_CONTROL_MASK
4585 #define MOD1 GDK_MOD1_MASK
4586 #define SHFT GDK_SHIFT_MASK
4588 /* inherent to GTK not all keys will be caught at all times */
4589 /* XXX sort key bindings */
4590 struct key_binding
{
4595 int (*func
)(struct tab
*, struct karg
*);
4597 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4599 { "cookiejar", MOD1
, 0, GDK_j
, xtp_page_cl
, {0} },
4600 { "downloadmgr", MOD1
, 0, GDK_d
, xtp_page_dl
, {0} },
4601 { "history", MOD1
, 0, GDK_h
, xtp_page_hl
, {0} },
4602 { "print", CTRL
, 0, GDK_p
, print_page
, {0}},
4603 { NULL
, 0, 0, GDK_slash
, command
, {.i
= '/'} },
4604 { NULL
, 0, 0, GDK_question
, command
, {.i
= '?'} },
4605 { NULL
, 0, 0, GDK_colon
, command
, {.i
= ':'} },
4606 { "quit", CTRL
, 0, GDK_q
, quit
, {0} },
4607 { "restart", MOD1
, 0, GDK_q
, restart
, {0} },
4608 { "togglejs", CTRL
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4609 { "togglecookie", MOD1
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4610 { "togglesrc", CTRL
, 0, GDK_s
, toggle_src
, {0} },
4611 { "yankuri", 0, 0, GDK_y
, yank_uri
, {0} },
4612 { "pasteuricur", 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
4613 { "pasteurinew", 0, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
4616 { "searchnext", 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
4617 { "searchprev", 0, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
4620 { "focusaddress", 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
4621 { "focussearch", 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
4623 /* command aliases (handy when -S flag is used) */
4624 { NULL
, 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
4625 { NULL
, 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
4626 { NULL
, 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
4627 { NULL
, 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
4630 { "hinting", 0, 0, GDK_f
, hint
, {.i
= 0} },
4633 { "goback", 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
4634 { "goback", MOD1
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
4635 { "goforward", SHFT
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
4636 { "goforward", MOD1
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
4637 { "reload", 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
4638 { "reload", CTRL
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
4639 { "reloadforce", CTRL
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
4640 { "reload" , CTRL
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
4641 { "favorites", MOD1
, 1, GDK_f
, xtp_page_fl
, {0} },
4643 /* vertical movement */
4644 { "scrolldown", 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
4645 { "scrolldown", 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
4646 { "scrollup", 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
4647 { "scrollup", 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
4648 { "scrollbottom", 0, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
4649 { "scrollbottom", 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
4650 { "scrolltop", 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
4651 { "scrolltop", 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} },
4652 { "scrollpagedown", 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4653 { "scrollpagedown", CTRL
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4654 { "scrollhalfdown", CTRL
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
4655 { "scrollpagedown", 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4656 { "scrollpageup", 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
4657 { "scrollpageup", CTRL
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
4658 { "scrollhalfup", CTRL
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
4659 /* horizontal movement */
4660 { "scrollright", 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
4661 { "scrollright", 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
4662 { "scrollleft", 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
4663 { "scrollleft", 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
4664 { "scrollfarright", 0, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
4665 { "scrollfarleft", 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
4668 { "tabnew", CTRL
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
4669 { "tabclose", CTRL
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
4670 { "tabundoclose", 0, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
4671 { "tabgoto1", CTRL
, 0, GDK_1
, movetab
, {.i
= 1} },
4672 { "tabgoto2", CTRL
, 0, GDK_2
, movetab
, {.i
= 2} },
4673 { "tabgoto3", CTRL
, 0, GDK_3
, movetab
, {.i
= 3} },
4674 { "tabgoto4", CTRL
, 0, GDK_4
, movetab
, {.i
= 4} },
4675 { "tabgoto5", CTRL
, 0, GDK_5
, movetab
, {.i
= 5} },
4676 { "tabgoto6", CTRL
, 0, GDK_6
, movetab
, {.i
= 6} },
4677 { "tabgoto7", CTRL
, 0, GDK_7
, movetab
, {.i
= 7} },
4678 { "tabgoto8", CTRL
, 0, GDK_8
, movetab
, {.i
= 8} },
4679 { "tabgoto9", CTRL
, 0, GDK_9
, movetab
, {.i
= 9} },
4680 { "tabgoto10", CTRL
, 0, GDK_0
, movetab
, {.i
= 10} },
4681 { "tabgotofirst", CTRL
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
4682 { "tabgotolast", CTRL
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
4683 { "tabgotoprev", CTRL
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
4684 { "tabgotonext", CTRL
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
4685 { "focusout", CTRL
, 0, GDK_minus
, resizetab
, {.i
= -1} },
4686 { "focusin", CTRL
, 0, GDK_plus
, resizetab
, {.i
= 1} },
4687 { "focusin", CTRL
, 0, GDK_equal
, resizetab
, {.i
= 1} },
4689 TAILQ_HEAD(keybinding_list
, key_binding
);
4692 walk_kb(struct settings
*s
,
4693 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4695 struct key_binding
*k
;
4698 if (s
== NULL
|| cb
== NULL
) {
4699 show_oops_s("walk_kb invalid parameters");
4703 TAILQ_FOREACH(k
, &kbl
, entry
) {
4704 if (k
->name
== NULL
)
4709 if (gdk_keyval_name(k
->key
) == NULL
)
4712 strlcat(str
, k
->name
, sizeof str
);
4713 strlcat(str
, ",", sizeof str
);
4715 if (k
->mask
& GDK_SHIFT_MASK
)
4716 strlcat(str
, "S-", sizeof str
);
4717 if (k
->mask
& GDK_CONTROL_MASK
)
4718 strlcat(str
, "C-", sizeof str
);
4719 if (k
->mask
& GDK_MOD1_MASK
)
4720 strlcat(str
, "M1-", sizeof str
);
4721 if (k
->mask
& GDK_MOD2_MASK
)
4722 strlcat(str
, "M2-", sizeof str
);
4723 if (k
->mask
& GDK_MOD3_MASK
)
4724 strlcat(str
, "M3-", sizeof str
);
4725 if (k
->mask
& GDK_MOD4_MASK
)
4726 strlcat(str
, "M4-", sizeof str
);
4727 if (k
->mask
& GDK_MOD5_MASK
)
4728 strlcat(str
, "M5-", sizeof str
);
4730 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4731 cb(s
, str
, cb_args
);
4735 init_keybindings(void)
4738 struct key_binding
*k
;
4740 for (i
= 0; i
< LENGTH(keys
); i
++) {
4741 k
= g_malloc0(sizeof *k
);
4742 k
->name
= keys
[i
].name
;
4743 k
->mask
= keys
[i
].mask
;
4744 k
->use_in_entry
= keys
[i
].use_in_entry
;
4745 k
->key
= keys
[i
].key
;
4746 k
->func
= keys
[i
].func
;
4747 bcopy(&keys
[i
].arg
, &k
->arg
, sizeof k
->arg
);
4748 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4750 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4751 k
->name
? k
->name
: "unnamed key");
4756 keybinding_clearall(void)
4758 struct key_binding
*k
, *next
;
4760 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4761 next
= TAILQ_NEXT(k
, entry
);
4762 if (k
->name
== NULL
)
4765 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4766 k
->name
? k
->name
: "unnamed key");
4767 TAILQ_REMOVE(&kbl
, k
, entry
);
4773 keybinding_add(char *kb
, char *value
, struct key_binding
*orig
)
4775 struct key_binding
*k
;
4776 guint keyval
, mask
= 0;
4779 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s %s\n", kb
, value
, orig
->name
);
4783 if (strcmp(kb
, orig
->name
))
4786 /* find modifier keys */
4787 if (strstr(value
, "S-"))
4788 mask
|= GDK_SHIFT_MASK
;
4789 if (strstr(value
, "C-"))
4790 mask
|= GDK_CONTROL_MASK
;
4791 if (strstr(value
, "M1-"))
4792 mask
|= GDK_MOD1_MASK
;
4793 if (strstr(value
, "M2-"))
4794 mask
|= GDK_MOD2_MASK
;
4795 if (strstr(value
, "M3-"))
4796 mask
|= GDK_MOD3_MASK
;
4797 if (strstr(value
, "M4-"))
4798 mask
|= GDK_MOD4_MASK
;
4799 if (strstr(value
, "M5-"))
4800 mask
|= GDK_MOD5_MASK
;
4803 for (i
= strlen(value
) - 1; i
> 0; i
--)
4804 if (value
[i
] == '-')
4805 value
= &value
[i
+ 1];
4807 /* validate keyname */
4808 keyval
= gdk_keyval_from_name(value
);
4809 if (keyval
== GDK_VoidSymbol
) {
4810 warnx("invalid keybinding name %s", value
);
4813 /* must run this test too, gtk+ doesn't handle 10 for example */
4814 if (gdk_keyval_name(keyval
) == NULL
) {
4815 warnx("invalid keybinding name %s", value
);
4819 /* make sure it isn't a dupe */
4820 TAILQ_FOREACH(k
, &kbl
, entry
)
4821 if (k
->key
== keyval
&& k
->mask
== mask
) {
4822 warnx("duplicate keybinding for %s", value
);
4827 k
= g_malloc0(sizeof *k
);
4828 k
->name
= orig
->name
;
4830 k
->use_in_entry
= orig
->use_in_entry
;
4832 k
->func
= orig
->func
;
4833 bcopy(&orig
->arg
, &k
->arg
, sizeof k
->arg
);
4835 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4840 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4841 k
->name
, gdk_keyval_name(keyval
));
4843 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4849 add_kb(struct settings
*s
, char *entry
)
4854 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4856 /* clearall is special */
4857 if (!strcmp(entry
, "clearall")) {
4858 keybinding_clearall();
4862 kb
= strstr(entry
, ",");
4868 /* make sure it is a valid keybinding */
4869 for (i
= 0; i
< LENGTH(keys
); i
++)
4870 if (keys
[i
].name
&& !strcmp(entry
, keys
[i
].name
)) {
4871 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s 0x%x %d 0x%x\n",
4874 keys
[i
].use_in_entry
,
4877 return (keybinding_add(entry
, value
, &keys
[i
]));
4886 int (*func
)(struct tab
*, struct karg
*);
4889 { "q!", 0, quit
, {0} },
4890 { "qa", 0, quit
, {0} },
4891 { "qa!", 0, quit
, {0} },
4892 { "w", 0, save_tabs
, {0} },
4893 { "wq", 0, save_tabs_and_quit
, {0} },
4894 { "wq!", 0, save_tabs_and_quit
, {0} },
4895 { "help", 0, help
, {0} },
4896 { "about", 0, about
, {0} },
4897 { "stats", 0, stats
, {0} },
4898 { "version", 0, about
, {0} },
4899 { "cookies", 0, xtp_page_cl
, {0} },
4900 { "fav", 0, xtp_page_fl
, {0} },
4901 { "favadd", 0, add_favorite
, {0} },
4902 { "js", 2, js_cmd
, {0} },
4903 { "cookie", 2, cookie_cmd
, {0} },
4904 { "cert", 1, cert_cmd
, {0} },
4905 { "ca", 0, ca_cmd
, {0} },
4906 { "dl", 0, xtp_page_dl
, {0} },
4907 { "h", 0, xtp_page_hl
, {0} },
4908 { "hist", 0, xtp_page_hl
, {0} },
4909 { "history", 0, xtp_page_hl
, {0} },
4910 { "home", 0, go_home
, {0} },
4911 { "restart", 0, restart
, {0} },
4912 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4913 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4914 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4915 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4916 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4917 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4918 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4919 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4921 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
4922 { "print", 0, print_page
, {0} },
4925 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4926 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4927 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4928 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4929 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4930 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4931 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4932 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4933 { "tabshow", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4934 { "tabs", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4935 { "tabhide", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4936 { "tabh", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4937 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4938 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4939 /* XXX add count to these commands */
4940 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4941 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4942 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4943 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4944 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
4945 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
4946 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
4947 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
4948 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4949 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4952 { "set", 1, set
, {0} },
4953 { "fullscreen", 0, fullscreen
, {0} },
4954 { "f", 0, fullscreen
, {0} },
4957 { "session", 1, session_cmd
, {0} },
4961 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4969 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4971 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4973 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
4980 * cancel, remove, etc. downloads
4983 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
4985 struct download find
, *d
;
4987 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
4989 /* some commands require a valid download id */
4990 if (cmd
!= XT_XTP_DL_LIST
) {
4991 /* lookup download in question */
4993 d
= RB_FIND(download_list
, &downloads
, &find
);
4996 show_oops(t
, "%s: no such download", __func__
);
5001 /* decide what to do */
5003 case XT_XTP_DL_CANCEL
:
5004 webkit_download_cancel(d
->download
);
5006 case XT_XTP_DL_REMOVE
:
5007 webkit_download_cancel(d
->download
); /* just incase */
5008 g_object_unref(d
->download
);
5009 RB_REMOVE(download_list
, &downloads
, d
);
5011 case XT_XTP_DL_LIST
:
5015 show_oops(t
, "%s: unknown command", __func__
);
5018 xtp_page_dl(t
, NULL
);
5022 * Actions on history, only does one thing for now, but
5023 * we provide the function for future actions
5026 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5028 struct history
*h
, *next
;
5032 case XT_XTP_HL_REMOVE
:
5033 /* walk backwards, as listed in reverse */
5034 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5035 next
= RB_PREV(history_list
, &hl
, h
);
5037 RB_REMOVE(history_list
, &hl
, h
);
5038 g_free((gpointer
) h
->title
);
5039 g_free((gpointer
) h
->uri
);
5046 case XT_XTP_HL_LIST
:
5047 /* Nothing - just xtp_page_hl() below */
5050 show_oops(t
, "%s: unknown command", __func__
);
5054 xtp_page_hl(t
, NULL
);
5057 /* remove a favorite */
5059 remove_favorite(struct tab
*t
, int index
)
5061 char file
[PATH_MAX
], *title
, *uri
;
5062 char *new_favs
, *tmp
;
5067 /* open favorites */
5068 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5070 if ((f
= fopen(file
, "r")) == NULL
) {
5071 show_oops(t
, "%s: can't open favorites: %s",
5072 __func__
, strerror(errno
));
5076 /* build a string which will become the new favroites file */
5077 new_favs
= g_strdup_printf("%s", "");
5080 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5081 if (feof(f
) || ferror(f
))
5083 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5090 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5091 if (feof(f
) || ferror(f
)) {
5092 show_oops(t
, "%s: can't parse favorites %s",
5093 __func__
, strerror(errno
));
5098 /* as long as this isn't the one we are deleting add to file */
5101 new_favs
= g_strdup_printf("%s%s\n%s\n",
5102 new_favs
, title
, uri
);
5114 /* write back new favorites file */
5115 if ((f
= fopen(file
, "w")) == NULL
) {
5116 show_oops(t
, "%s: can't open favorites: %s",
5117 __func__
, strerror(errno
));
5121 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5134 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5137 case XT_XTP_FL_LIST
:
5138 /* nothing, just the below call to xtp_page_fl() */
5140 case XT_XTP_FL_REMOVE
:
5141 remove_favorite(t
, arg
);
5144 show_oops(t
, "%s: invalid favorites command", __func__
);
5148 xtp_page_fl(t
, NULL
);
5152 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5155 case XT_XTP_CL_LIST
:
5156 /* nothing, just xtp_page_cl() */
5158 case XT_XTP_CL_REMOVE
:
5162 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5166 xtp_page_cl(t
, NULL
);
5169 /* link an XTP class to it's session key and handler function */
5170 struct xtp_despatch
{
5173 void (*handle_func
)(struct tab
*, uint8_t, int);
5176 struct xtp_despatch xtp_despatches
[] = {
5177 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5178 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5179 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5180 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5181 { NULL
, NULL
, NULL
}
5185 * is the url xtp protocol? (xxxt://)
5186 * if so, parse and despatch correct bahvior
5189 parse_xtp_url(struct tab
*t
, const char *url
)
5191 char *dup
= NULL
, *p
, *last
;
5192 uint8_t n_tokens
= 0;
5193 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5194 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5198 * tokens array meaning:
5200 * tokens[1] = session key
5201 * tokens[2] = action
5202 * tokens[3] = optional argument
5205 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5207 /*xtp tab meaning is normal unless proven special */
5208 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5210 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5213 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5215 /* split out the url */
5216 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5217 (p
= strtok_r(NULL
, "/", &last
))) {
5219 tokens
[n_tokens
++] = p
;
5222 /* should be atleast three fields 'class/seskey/command/arg' */
5226 dsp
= xtp_despatches
;
5227 req_class
= atoi(tokens
[0]);
5228 while (dsp
->xtp_class
!= NULL
) {
5229 if (dsp
->xtp_class
== req_class
) {
5236 /* did we find one atall? */
5237 if (dsp_match
== NULL
) {
5238 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5242 /* check session key and call despatch function */
5243 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5244 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5257 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5259 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5261 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5264 show_oops_s("activate_uri_entry_cb invalid parameters");
5269 show_oops(t
, "activate_uri_entry_cb no uri");
5273 uri
+= strspn(uri
, "\t ");
5275 /* if xxxt:// treat specially */
5276 if (!parse_xtp_url(t
, uri
)) {
5277 load_uri(t
, (gchar
*)uri
);
5283 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5285 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5286 char *newuri
= NULL
;
5289 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5292 show_oops_s("activate_search_entry_cb invalid parameters");
5296 if (search_string
== NULL
) {
5297 show_oops(t
, "no search_string");
5301 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5302 newuri
= g_strdup_printf(search_string
, enc_search
);
5305 webkit_web_view_load_uri(t
->wv
, newuri
);
5313 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5315 struct domain
*d
= NULL
;
5318 if (uri
== NULL
|| t
== NULL
)
5321 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5326 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5327 es
? "enable" : "disable", uri
);
5329 g_object_set(G_OBJECT(t
->settings
),
5330 "enable-scripts", es
, (char *)NULL
);
5331 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5333 button_set_stockid(t
->js_toggle
,
5334 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5338 show_ca_status(struct tab
*t
, const char *uri
)
5340 WebKitWebFrame
*frame
;
5341 WebKitWebDataSource
*source
;
5342 WebKitNetworkRequest
*request
;
5343 SoupMessage
*message
;
5345 gchar
*col_str
= "white";
5348 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5349 ssl_strict_certs
, ssl_ca_file
, uri
);
5353 if (ssl_ca_file
== NULL
) {
5354 if (g_str_has_prefix(uri
, "http://"))
5356 if (g_str_has_prefix(uri
, "https://")) {
5362 if (g_str_has_prefix(uri
, "http://") ||
5363 !g_str_has_prefix(uri
, "https://"))
5366 frame
= webkit_web_view_get_main_frame(t
->wv
);
5367 source
= webkit_web_frame_get_data_source(frame
);
5368 request
= webkit_web_data_source_get_request(source
);
5369 message
= webkit_network_request_get_message(request
);
5371 if (message
&& (soup_message_get_flags(message
) &
5372 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5376 r
= load_compare_cert(t
, NULL
);
5378 col_str
= "lightblue";
5387 gdk_color_parse(col_str
, &color
);
5388 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5390 if (!strcmp(col_str
, "white")) {
5391 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5393 gdk_color_parse("black", &color
);
5394 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5397 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5399 gdk_color_parse("black", &color
);
5400 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5407 free_favicon(struct tab
*t
)
5409 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5410 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5412 if (t
->icon_request
)
5413 g_object_unref(t
->icon_request
);
5415 g_object_unref(t
->icon_pixbuf
);
5416 if (t
->icon_dest_uri
)
5417 g_free(t
->icon_dest_uri
);
5419 t
->icon_pixbuf
= NULL
;
5420 t
->icon_request
= NULL
;
5421 t
->icon_dest_uri
= NULL
;
5425 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5427 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5428 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5429 if (show_url
== 0) {
5430 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5431 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5433 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5434 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5440 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5442 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5443 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5444 if (show_url
== 0) {
5445 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5446 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5448 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5449 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5454 abort_favicon_download(struct tab
*t
)
5456 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5458 if (t
->icon_download
)
5459 webkit_download_cancel(t
->icon_download
);
5463 xt_icon_from_name(t
, "text-html");
5467 set_favicon_from_file(struct tab
*t
, char *file
)
5470 GdkPixbuf
*pixbuf
, *scaled
;
5473 if (t
== NULL
|| file
== NULL
)
5475 if (t
->icon_pixbuf
) {
5476 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5480 if (g_str_has_prefix(file
, "file://"))
5481 file
+= strlen("file://");
5482 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5484 if (!stat(file
, &sb
)) {
5485 if (sb
.st_size
== 0) {
5486 /* corrupt icon so trash it */
5487 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5490 /* no need to set icon to default here */
5495 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5496 if (pixbuf
== NULL
) {
5497 xt_icon_from_name(t
, "text-html");
5501 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5503 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5504 __func__
, t
->tab_id
, width
, height
);
5506 if (width
> 16 || height
> 16) {
5507 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5508 GDK_INTERP_BILINEAR
);
5509 g_object_unref(pixbuf
);
5513 if (scaled
== NULL
) {
5514 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5515 GDK_INTERP_BILINEAR
);
5519 t
->icon_pixbuf
= scaled
;
5520 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5524 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5527 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5532 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5533 __func__
, t
->tab_id
, status
);
5536 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5538 t
->icon_download
= NULL
;
5541 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5544 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5547 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5549 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5550 __func__
, t
->tab_id
);
5551 t
->icon_download
= NULL
;
5554 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5556 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5557 __func__
, t
->icon_dest_uri
);
5558 set_favicon_from_file(t
, t
->icon_dest_uri
);
5559 /* these will be freed post callback */
5560 t
->icon_request
= NULL
;
5561 t
->icon_download
= NULL
;
5569 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5571 gchar
*name_hash
, file
[PATH_MAX
];
5574 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5576 if (uri
== NULL
|| t
== NULL
)
5579 if (t
->icon_request
) {
5580 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5584 /* check to see if we got the icon in cache */
5585 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5586 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5589 if (!stat(file
, &sb
)) {
5590 if (sb
.st_size
> 0) {
5591 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5593 set_favicon_from_file(t
, file
);
5597 /* corrupt icon so trash it */
5598 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5603 /* create download for icon */
5604 t
->icon_request
= webkit_network_request_new(uri
);
5605 if (t
->icon_request
== NULL
) {
5606 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5611 t
->icon_download
= webkit_download_new(t
->icon_request
);
5613 /* we have to free icon_dest_uri later */
5614 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5615 webkit_download_set_destination_uri(t
->icon_download
,
5618 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5619 G_CALLBACK(favicon_download_status_changed_cb
), t
);
5621 webkit_download_start(t
->icon_download
);
5625 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5627 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5628 struct history
*h
, find
;
5630 const gchar
*s_loading
;
5633 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
5634 webkit_web_view_get_load_status(wview
));
5637 show_oops_s("notify_load_status_cb invalid paramters");
5641 switch (webkit_web_view_get_load_status(wview
)) {
5642 case WEBKIT_LOAD_PROVISIONAL
:
5644 abort_favicon_download(t
);
5645 #if GTK_CHECK_VERSION(2, 20, 0)
5646 gtk_widget_show(t
->spinner
);
5647 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5649 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5651 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5655 case WEBKIT_LOAD_COMMITTED
:
5657 if ((uri
= get_uri(wview
)) != NULL
) {
5658 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5659 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5665 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5668 /* check if js white listing is enabled */
5669 if (enable_js_whitelist
) {
5670 uri
= get_uri(wview
);
5671 check_and_set_js(uri
, t
);
5674 show_ca_status(t
, uri
);
5676 /* we know enough to autosave the session */
5677 if (session_autosave
) {
5683 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5685 title
= webkit_web_view_get_title(wview
);
5686 uri
= get_uri(wview
);
5694 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5695 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5698 if (!strncmp(uri
, "http://", strlen("http://")) ||
5699 !strncmp(uri
, "https://", strlen("https://")) ||
5700 !strncmp(uri
, "file://", strlen("file://")))
5705 h
= RB_FIND(history_list
, &hl
, &find
);
5709 h
= g_malloc(sizeof *h
);
5710 h
->uri
= g_strdup(uri
);
5712 h
->title
= g_strdup(title
);
5714 h
->title
= g_strdup(uri
);
5715 RB_INSERT(history_list
, &hl
, h
);
5716 update_history_tabs(NULL
);
5721 case WEBKIT_LOAD_FINISHED
:
5723 uri
= get_uri(wview
);
5724 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5725 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5726 case WEBKIT_LOAD_FAILED
:
5729 #if GTK_CHECK_VERSION(2, 20, 0)
5730 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5731 gtk_widget_hide(t
->spinner
);
5733 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5734 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5735 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5737 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5742 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5744 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5745 webkit_web_view_can_go_back(wview
));
5747 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5748 webkit_web_view_can_go_forward(wview
));
5750 /* take focus if we are visible */
5756 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5758 run_script(t
, JS_HINTING
);
5762 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5764 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5765 progress
== 100 ? 0 : (double)progress
/ 100);
5766 if (show_url
== 0) {
5767 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5768 progress
== 100 ? 0 : (double)progress
/ 100);
5773 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5774 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5775 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5780 show_oops_s("webview_nw_cb invalid paramters");
5784 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
5785 webkit_network_request_get_uri(request
));
5787 /* open in current tab */
5788 uri
= (char *)webkit_network_request_get_uri(request
);
5789 webkit_web_view_load_uri(t
->wv
, uri
);
5790 webkit_web_policy_decision_ignore(pd
);
5792 return (TRUE
); /* we made the decission */
5796 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5797 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5798 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5803 show_oops_s("webview_npd_cb invalid parameters");
5807 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
5809 webkit_network_request_get_uri(request
));
5811 uri
= (char *)webkit_network_request_get_uri(request
);
5813 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
5815 create_new_tab(uri
, NULL
, ctrl_click_focus
);
5816 webkit_web_policy_decision_ignore(pd
);
5817 return (TRUE
); /* we made the decission */
5820 webkit_web_policy_decision_use(pd
);
5821 return (TRUE
); /* we made the decission */
5825 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5827 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
5828 webkit_web_view_get_uri(wv
));
5834 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
5836 /* we can not eat the event without throwing gtk off so defer it */
5838 /* catch middle click */
5839 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
5844 /* catch ctrl click */
5845 if (e
->type
== GDK_BUTTON_RELEASE
&&
5846 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
5851 return (XT_CB_PASSTHROUGH
);
5855 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
5857 struct mime_type
*m
;
5859 m
= find_mime_type(mime_type
);
5865 show_oops(t
, "can't fork mime handler");
5874 execlp(m
->mt_action
, m
->mt_action
,
5875 webkit_network_request_get_uri(request
), (void *)NULL
);
5884 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
5885 WebKitNetworkRequest
*request
, char *mime_type
,
5886 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
5889 show_oops_s("webview_mimetype_cb invalid parameters");
5893 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
5894 t
->tab_id
, mime_type
);
5896 if (run_mimehandler(t
, mime_type
, request
) == 0) {
5897 webkit_web_policy_decision_ignore(decision
);
5902 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
5903 webkit_web_policy_decision_download(decision
);
5911 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
5914 const gchar
*filename
;
5916 struct download
*download_entry
;
5919 if (wk_download
== NULL
|| t
== NULL
) {
5920 show_oops_s("%s invalid parameters", __func__
);
5924 filename
= webkit_download_get_suggested_filename(wk_download
);
5925 if (filename
== NULL
)
5926 return (FALSE
); /* abort download */
5928 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
5930 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
5931 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
5933 webkit_download_set_destination_uri(wk_download
, uri
);
5935 if (webkit_download_get_status(wk_download
) ==
5936 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5937 show_oops(t
, "%s: download failed to start", __func__
);
5939 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
5941 download_entry
= g_malloc(sizeof(struct download
));
5942 download_entry
->download
= wk_download
;
5943 download_entry
->tab
= t
;
5944 download_entry
->id
= next_download_id
++;
5945 RB_INSERT(download_list
, &downloads
, download_entry
);
5946 /* get from history */
5947 g_object_ref(wk_download
);
5948 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
5954 /* sync other download manager tabs */
5955 update_download_tabs(NULL
);
5958 * NOTE: never redirect/render the current tab before this
5959 * function returns. This will cause the download to never start.
5961 return (ret
); /* start download */
5965 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
5967 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
5970 show_oops_s("webview_hover_cb");
5975 set_status(t
, uri
, XT_STATUS_LINK
);
5978 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
5983 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
5985 char s
[2], buf
[128];
5986 const char *errstr
= NULL
;
5989 /* don't use w directly; use t->whatever instead */
5992 show_oops_s("wv_keypress_after_cb");
5993 return (XT_CB_PASSTHROUGH
);
5996 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
5997 e
->keyval
, e
->state
, t
);
6001 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6003 return (XT_CB_HANDLED
);
6007 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6008 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6010 /* we have a string */
6012 /* we have a number */
6013 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6021 /* XXX unfuck this */
6022 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6023 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6024 /* last input was numerical */
6026 l
= strlen(t
->hint_num
);
6033 t
->hint_num
[l
] = '\0';
6037 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6038 /* last input was alphanumerical */
6040 l
= strlen(t
->hint_buf
);
6047 t
->hint_buf
[l
] = '\0';
6057 /* numerical input */
6058 if (CLEAN(e
->state
) == 0 &&
6059 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6060 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6061 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6062 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6065 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6067 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6070 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6072 t
->hint_mode
= XT_HINT_NUMERICAL
;
6076 /* empty the counter buffer */
6077 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6078 return (XT_CB_HANDLED
);
6081 /* alphanumerical input */
6083 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6084 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6085 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6086 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6087 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6088 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6089 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6092 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6095 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6097 t
->hint_mode
= XT_HINT_ALPHANUM
;
6100 /* empty the counter buffer */
6101 bzero(t
->hint_num
, sizeof t
->hint_num
);
6102 return (XT_CB_HANDLED
);
6105 return (XT_CB_HANDLED
);
6108 struct key_binding
*k
;
6109 TAILQ_FOREACH(k
, &kbl
, entry
)
6110 if (e
->keyval
== k
->key
) {
6112 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6113 k
->func(t
, &k
->arg
);
6114 return (XT_CB_HANDLED
);
6117 else if ((e
->state
& k
->mask
) == k
->mask
) {
6118 k
->func(t
, &k
->arg
);
6119 return (XT_CB_HANDLED
);
6123 return (XT_CB_PASSTHROUGH
);
6127 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6131 return (XT_CB_PASSTHROUGH
);
6135 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6137 const gchar
*c
= gtk_entry_get_text(w
);
6141 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6142 e
->keyval
, e
->state
, t
);
6145 show_oops_s("cmd_keyrelease_cb invalid parameters");
6146 return (XT_CB_PASSTHROUGH
);
6149 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6150 e
->keyval
, e
->state
, t
);
6154 if (strlen(c
) == 1) {
6155 webkit_web_view_unmark_text_matches(t
->wv
);
6161 else if (c
[0] == '?')
6167 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6169 /* not found, mark red */
6170 gdk_color_parse("red", &color
);
6171 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6172 /* unmark and remove selection */
6173 webkit_web_view_unmark_text_matches(t
->wv
);
6174 /* my kingdom for a way to unselect text in webview */
6176 /* found, highlight all */
6177 webkit_web_view_unmark_text_matches(t
->wv
);
6178 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6179 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6180 gdk_color_parse("white", &color
);
6181 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6184 return (XT_CB_PASSTHROUGH
);
6189 cmd_complete(struct tab
*t
, char *s
)
6192 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6194 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
6196 for (i
= 0; i
< LENGTH(cmds
); i
++) {
6197 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
6198 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
6200 gtk_entry_set_text(w
, ":");
6201 gtk_entry_append_text(w
, cmds
[i
].cmd
);
6202 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6212 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6215 show_oops_s("entry_key_cb invalid parameters");
6216 return (XT_CB_PASSTHROUGH
);
6219 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6220 e
->keyval
, e
->state
, t
);
6224 if (e
->keyval
== GDK_Escape
) {
6225 /* don't use focus_webview(t) because we want to type :cmds */
6226 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6229 struct key_binding
*k
;
6230 TAILQ_FOREACH(k
, &kbl
, entry
)
6231 if (e
->keyval
== k
->key
&& k
->use_in_entry
) {
6233 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6234 k
->func(t
, &k
->arg
);
6235 return (XT_CB_HANDLED
);
6238 else if ((e
->state
& k
->mask
) == k
->mask
) {
6239 k
->func(t
, &k
->arg
);
6240 return (XT_CB_HANDLED
);
6244 return (XT_CB_PASSTHROUGH
);
6248 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6250 int rv
= XT_CB_HANDLED
;
6251 const gchar
*c
= gtk_entry_get_text(w
);
6254 show_oops_s("cmd_keypress_cb parameters");
6255 return (XT_CB_PASSTHROUGH
);
6258 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6259 e
->keyval
, e
->state
, t
);
6263 e
->keyval
= GDK_Escape
;
6264 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6265 e
->keyval
= GDK_Escape
;
6267 switch (e
->keyval
) {
6273 if (strchr (c
, ' ')) {
6274 /* par completion */
6275 fprintf(stderr
, "completeme par\n");
6279 cmd_complete(t
, (char *)&c
[1]);
6284 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6292 if (c
[0] == '/' || c
[0] == '?')
6293 webkit_web_view_unmark_text_matches(t
->wv
);
6297 rv
= XT_CB_PASSTHROUGH
;
6303 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6306 show_oops_s("cmd_focusout_cb invalid parameters");
6307 return (XT_CB_PASSTHROUGH
);
6309 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6314 if (show_url
== 0 || t
->focus_wv
)
6317 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6319 return (XT_CB_PASSTHROUGH
);
6323 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6327 const gchar
*c
= gtk_entry_get_text(entry
);
6330 show_oops_s("cmd_activate_cb invalid parameters");
6334 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6339 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6345 if (c
[0] == '/' || c
[0] == '?') {
6346 if (t
->search_text
) {
6347 g_free(t
->search_text
);
6348 t
->search_text
= NULL
;
6351 t
->search_text
= g_strdup(s
);
6353 g_free(global_search
);
6354 global_search
= g_strdup(s
);
6355 t
->search_forward
= c
[0] == '/';
6360 for (i
= 0; i
< LENGTH(cmds
); i
++)
6361 if (cmds
[i
].params
) {
6362 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
6363 cmds
[i
].arg
.s
= g_strdup(s
);
6364 goto execute_command
;
6367 if (!strcmp(s
, cmds
[i
].cmd
))
6368 goto execute_command
;
6370 show_oops(t
, "Invalid command: %s", s
);
6377 cmds
[i
].func(t
, &cmds
[i
].arg
);
6380 backward_cb(GtkWidget
*w
, struct tab
*t
)
6385 show_oops_s("backward_cb invalid parameters");
6389 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6396 forward_cb(GtkWidget
*w
, struct tab
*t
)
6401 show_oops_s("forward_cb invalid parameters");
6405 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6407 a
.i
= XT_NAV_FORWARD
;
6412 stop_cb(GtkWidget
*w
, struct tab
*t
)
6414 WebKitWebFrame
*frame
;
6417 show_oops_s("stop_cb invalid parameters");
6421 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6423 frame
= webkit_web_view_get_main_frame(t
->wv
);
6424 if (frame
== NULL
) {
6425 show_oops(t
, "stop_cb: no frame");
6429 webkit_web_frame_stop_loading(frame
);
6430 abort_favicon_download(t
);
6434 setup_webkit(struct tab
*t
)
6436 g_object_set(G_OBJECT(t
->settings
),
6437 "user-agent", t
->user_agent
, (char *)NULL
);
6438 g_object_set(G_OBJECT(t
->settings
),
6439 "enable-scripts", enable_scripts
, (char *)NULL
);
6440 g_object_set(G_OBJECT(t
->settings
),
6441 "enable-plugins", enable_plugins
, (char *)NULL
);
6442 adjustfont_webkit(t
, XT_FONT_SET
);
6444 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6448 create_browser(struct tab
*t
)
6454 show_oops_s("create_browser invalid parameters");
6458 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
6459 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
6460 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
6461 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
6463 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
6464 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
6465 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
6467 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
6468 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
6471 t
->settings
= webkit_web_settings_new();
6473 if (user_agent
== NULL
) {
6474 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
6476 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
6479 t
->user_agent
= g_strdup(user_agent
);
6492 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6493 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6494 gtk_widget_set_name(w
, "xxxterm");
6495 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
6496 g_signal_connect(G_OBJECT(w
), "delete_event",
6497 G_CALLBACK (gtk_main_quit
), NULL
);
6503 create_toolbar(struct tab
*t
)
6505 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
6507 b
= gtk_hbox_new(FALSE
, 0);
6509 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6512 /* backward button */
6513 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
6514 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6515 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6516 G_CALLBACK(backward_cb
), t
);
6517 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
6519 /* forward button */
6520 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
6521 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6522 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6523 G_CALLBACK(forward_cb
), t
);
6524 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
6528 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6529 gtk_widget_set_sensitive(t
->stop
, FALSE
);
6530 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
6531 G_CALLBACK(stop_cb
), t
);
6532 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
6536 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6537 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6538 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
6539 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
6540 G_CALLBACK(js_toggle_cb
), t
);
6541 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
6544 t
->uri_entry
= gtk_entry_new();
6545 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
6546 G_CALLBACK(activate_uri_entry_cb
), t
);
6547 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
6548 G_CALLBACK(entry_key_cb
), t
);
6549 eb1
= gtk_hbox_new(FALSE
, 0);
6550 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
6551 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
6552 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
6555 if (fancy_bar
&& search_string
) {
6557 t
->search_entry
= gtk_entry_new();
6558 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
6559 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
6560 G_CALLBACK(activate_search_entry_cb
), t
);
6561 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
6562 G_CALLBACK(entry_key_cb
), t
);
6563 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
6564 eb2
= gtk_hbox_new(FALSE
, 0);
6565 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
6566 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
6568 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
6578 TAILQ_FOREACH(t
, &tabs
, entry
)
6579 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
6583 undo_close_tab_save(struct tab
*t
)
6587 struct undo
*u1
, *u2
;
6589 WebKitWebHistoryItem
*item
;
6591 if ((uri
= get_uri(t
->wv
)) == NULL
)
6594 u1
= g_malloc0(sizeof(struct undo
));
6595 u1
->uri
= g_strdup(uri
);
6597 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6599 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
6600 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
6603 /* forward history */
6604 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
6608 u1
->history
= g_list_prepend(u1
->history
,
6609 webkit_web_history_item_copy(item
));
6610 items
= g_list_next(items
);
6615 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
6616 u1
->history
= g_list_prepend(u1
->history
,
6617 webkit_web_history_item_copy(item
));
6621 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
6625 u1
->history
= g_list_prepend(u1
->history
,
6626 webkit_web_history_item_copy(item
));
6627 items
= g_list_next(items
);
6630 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
6632 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
6633 u2
= TAILQ_LAST(&undos
, undo_tailq
);
6634 TAILQ_REMOVE(&undos
, u2
, entry
);
6636 g_list_free(u2
->history
);
6645 delete_tab(struct tab
*t
)
6649 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
6654 TAILQ_REMOVE(&tabs
, t
, entry
);
6656 /* halt all webkit activity */
6657 abort_favicon_download(t
);
6658 webkit_web_view_stop_loading(t
->wv
);
6659 undo_close_tab_save(t
);
6661 gtk_widget_destroy(t
->vbox
);
6662 g_free(t
->user_agent
);
6666 if (TAILQ_EMPTY(&tabs
))
6667 create_new_tab(NULL
, NULL
, 1);
6670 /* recreate session */
6671 if (session_autosave
) {
6678 adjustfont_webkit(struct tab
*t
, int adjust
)
6681 show_oops_s("adjustfont_webkit invalid parameters");
6685 if (adjust
== XT_FONT_SET
)
6686 t
->font_size
= default_font_size
;
6688 t
->font_size
+= adjust
;
6689 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
6690 t
->font_size
, (char *)NULL
);
6691 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
6692 &t
->font_size
, (char *)NULL
);
6696 append_tab(struct tab
*t
)
6701 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
6702 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
6706 create_new_tab(char *title
, struct undo
*u
, int focus
)
6709 int load
= 1, id
, notfound
;
6711 WebKitWebHistoryItem
*item
;
6714 PangoFontDescription
*fd
= NULL
;
6716 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
6718 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
6719 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
6723 t
= g_malloc0(sizeof *t
);
6725 if (title
== NULL
) {
6726 title
= "(untitled)";
6730 t
->vbox
= gtk_vbox_new(FALSE
, 0);
6732 /* label + button for tab */
6733 b
= gtk_hbox_new(FALSE
, 0);
6736 #if GTK_CHECK_VERSION(2, 20, 0)
6737 t
->spinner
= gtk_spinner_new ();
6739 t
->label
= gtk_label_new(title
);
6740 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
6741 gtk_widget_set_size_request(t
->label
, 100, 0);
6742 gtk_widget_set_size_request(b
, 130, 0);
6744 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
6745 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
6746 #if GTK_CHECK_VERSION(2, 20, 0)
6747 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
6751 t
->toolbar
= create_toolbar(t
);
6752 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
6755 t
->browser_win
= create_browser(t
);
6756 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
6758 /* oops message for user feedback */
6759 t
->oops
= gtk_entry_new();
6760 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
6761 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
6762 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
6763 gdk_color_parse("red", &color
);
6764 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
6765 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
6768 t
->cmd
= gtk_entry_new();
6769 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
6770 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
6771 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
6774 t
->statusbar
= gtk_entry_new();
6775 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
6776 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
6777 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
6778 gdk_color_parse("black", &color
);
6779 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6780 gdk_color_parse("white", &color
);
6781 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6782 fd
= GTK_WIDGET(t
->statusbar
)->style
->font_desc
;
6783 pango_font_description_set_weight(fd
, PANGO_WEIGHT_SEMIBOLD
);
6784 pango_font_description_set_absolute_size(fd
, 10 * PANGO_SCALE
); /* 10 px font */
6785 gtk_widget_modify_font(t
->statusbar
, fd
);
6786 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
6788 /* xtp meaning is normal by default */
6789 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6791 /* set empty favicon */
6792 xt_icon_from_name(t
, "text-html");
6794 /* and show it all */
6795 gtk_widget_show_all(b
);
6796 gtk_widget_show_all(t
->vbox
);
6798 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
6802 id
= gtk_notebook_get_current_page(notebook
);
6803 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6804 if (tt
->tab_id
== id
) {
6806 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
6807 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
6817 #if GTK_CHECK_VERSION(2, 20, 0)
6818 /* turn spinner off if we are a new tab without uri */
6820 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6821 gtk_widget_hide(t
->spinner
);
6824 /* make notebook tabs reorderable */
6825 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
6827 g_object_connect(G_OBJECT(t
->cmd
),
6828 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
6829 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
6830 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
6831 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
6834 /* reuse wv_button_cb to hide oops */
6835 g_object_connect(G_OBJECT(t
->oops
),
6836 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6839 g_object_connect(G_OBJECT(t
->wv
),
6840 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
6841 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6842 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
6843 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
6844 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
6845 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
6846 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_nw_cb
), t
,
6847 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
6848 "signal::event", G_CALLBACK(webview_event_cb
), t
,
6849 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
6850 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
6851 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6852 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
6854 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6856 g_signal_connect(t
->wv
,
6857 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
6859 /* hijack the unused keys as if we were the browser */
6860 g_object_connect(G_OBJECT(t
->toolbar
),
6861 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6864 g_signal_connect(G_OBJECT(bb
), "button_press_event",
6865 G_CALLBACK(tab_close_cb
), t
);
6870 url_set_visibility();
6871 statusbar_set_visibility();
6874 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6875 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
6880 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
6884 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6889 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6890 /* restore the tab's history */
6891 if (u
&& u
->history
) {
6895 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
6896 items
= g_list_next(items
);
6899 item
= g_list_nth_data(u
->history
, u
->back
);
6901 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
6904 g_list_free(u
->history
);
6906 webkit_web_back_forward_list_clear(t
->bfl
);
6910 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
6916 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
6918 TAILQ_FOREACH(t
, &tabs
, entry
) {
6919 if (t
->tab_id
== pn
) {
6920 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
6923 uri
= webkit_web_view_get_title(t
->wv
);
6926 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
6938 menuitem_response(struct tab
*t
)
6940 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6944 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
6946 GtkWidget
*menu
, *menu_items
;
6947 GdkEventButton
*bevent
;
6951 if (event
->type
== GDK_BUTTON_PRESS
) {
6952 bevent
= (GdkEventButton
*) event
;
6953 menu
= gtk_menu_new();
6955 TAILQ_FOREACH(ti
, &tabs
, entry
) {
6956 if ((uri
= get_uri(ti
->wv
)) == NULL
)
6957 /* XXX make sure there is something to print */
6958 /* XXX add gui pages in here to look purdy */
6960 menu_items
= gtk_menu_item_new_with_label(uri
);
6961 gtk_menu_append(GTK_MENU (menu
), menu_items
);
6962 gtk_widget_show(menu_items
);
6964 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
6965 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
6969 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
6970 bevent
->button
, bevent
->time
);
6972 /* unref object so it'll free itself when popped down */
6973 g_object_ref_sink(menu
);
6974 g_object_unref(menu
);
6976 return (TRUE
/* eat event */);
6979 return (FALSE
/* propagate */);
6983 icon_size_map(int icon_size
)
6985 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
6986 icon_size
> GTK_ICON_SIZE_DIALOG
)
6987 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
6993 create_button(char *name
, char *stockid
, int size
)
6995 GtkWidget
*button
, *image
;
6998 rcstring
= g_strdup_printf(
6999 "style \"%s-style\"\n"
7001 " GtkWidget::focus-padding = 0\n"
7002 " GtkWidget::focus-line-width = 0\n"
7006 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
7007 gtk_rc_parse_string(rcstring
);
7009 button
= gtk_button_new();
7010 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7011 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
7013 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7014 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7015 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7016 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7017 gtk_widget_set_name(button
, name
);
7018 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7019 gtk_widget_set_tooltip_text(button
, name
);
7025 button_set_stockid(GtkWidget
*button
, char *stockid
)
7028 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7029 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7030 gtk_button_set_image(GTK_BUTTON(button
), image
);
7039 char file
[PATH_MAX
];
7042 vbox
= gtk_vbox_new(FALSE
, 0);
7043 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7044 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7045 gtk_notebook_set_tab_hborder(notebook
, 0);
7046 gtk_notebook_set_tab_vborder(notebook
, 0);
7047 gtk_notebook_set_scrollable(notebook
, TRUE
);
7048 notebook_tab_set_visibility(notebook
);
7049 gtk_notebook_set_show_border(notebook
, FALSE
);
7050 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7052 abtn
= gtk_button_new();
7053 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7054 gtk_widget_set_size_request(arrow
, -1, -1);
7055 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7056 gtk_widget_set_size_request(abtn
, -1, 20);
7057 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7059 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7060 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7061 gtk_widget_set_size_request(vbox
, -1, -1);
7063 g_object_connect(G_OBJECT(notebook
),
7064 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7066 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7067 G_CALLBACK(arrow_cb
), NULL
);
7069 main_window
= create_window();
7070 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7071 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7074 for (i
= 0; i
< LENGTH(icons
); i
++) {
7075 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7076 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7077 l
= g_list_append(l
, pb
);
7079 gtk_window_set_default_icon_list(l
);
7081 gtk_widget_show_all(abtn
);
7082 gtk_widget_show_all(main_window
);
7086 set_hook(void **hook
, char *name
)
7089 errx(1, "set_hook");
7091 if (*hook
== NULL
) {
7092 *hook
= dlsym(RTLD_NEXT
, name
);
7094 errx(1, "can't hook %s", name
);
7098 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7100 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7102 g_return_val_if_fail(cookie1
, FALSE
);
7103 g_return_val_if_fail(cookie2
, FALSE
);
7105 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7106 !strcmp (cookie1
->value
, cookie2
->value
) &&
7107 !strcmp (cookie1
->path
, cookie2
->path
) &&
7108 !strcmp (cookie1
->domain
, cookie2
->domain
));
7112 transfer_cookies(void)
7115 SoupCookie
*sc
, *pc
;
7117 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7119 for (;cf
; cf
= cf
->next
) {
7121 sc
= soup_cookie_copy(pc
);
7122 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7125 soup_cookies_free(cf
);
7129 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7134 print_cookie("soup_cookie_jar_delete_cookie", c
);
7136 if (cookies_enabled
== 0)
7139 if (jar
== NULL
|| c
== NULL
)
7142 /* find and remove from persistent jar */
7143 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7145 for (;cf
; cf
= cf
->next
) {
7147 if (soup_cookie_equal(ci
, c
)) {
7148 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7153 soup_cookies_free(cf
);
7155 /* delete from session jar */
7156 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7160 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7162 struct domain
*d
= NULL
;
7166 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7167 jar
, p_cookiejar
, s_cookiejar
);
7169 if (cookies_enabled
== 0)
7172 /* see if we are up and running */
7173 if (p_cookiejar
== NULL
) {
7174 _soup_cookie_jar_add_cookie(jar
, cookie
);
7177 /* disallow p_cookiejar adds, shouldn't happen */
7178 if (jar
== p_cookiejar
)
7181 if (enable_cookie_whitelist
&&
7182 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7184 DNPRINTF(XT_D_COOKIE
,
7185 "soup_cookie_jar_add_cookie: reject %s\n",
7187 if (save_rejected_cookies
) {
7188 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7189 show_oops_s("can't open reject cookie file");
7192 fseek(r_cookie_f
, 0, SEEK_END
);
7193 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7194 cookie
->http_only
? "#HttpOnly_" : "",
7196 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7198 cookie
->secure
? "TRUE" : "FALSE",
7200 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7207 if (!allow_volatile_cookies
)
7211 if (cookie
->expires
== NULL
&& session_timeout
) {
7212 soup_cookie_set_expires(cookie
,
7213 soup_date_new_from_now(session_timeout
));
7214 print_cookie("modified add cookie", cookie
);
7217 /* see if we are white listed for persistence */
7218 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7219 /* add to persistent jar */
7220 c
= soup_cookie_copy(cookie
);
7221 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7222 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7225 /* add to session jar */
7226 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7227 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7233 char file
[PATH_MAX
];
7235 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7236 "soup_cookie_jar_add_cookie");
7237 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7238 "soup_cookie_jar_delete_cookie");
7240 if (cookies_enabled
== 0)
7244 * the following code is intricate due to overriding several libsoup
7246 * do not alter order of these operations.
7249 /* rejected cookies */
7250 if (save_rejected_cookies
)
7251 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
7253 /* persistent cookies */
7254 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
7255 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7257 /* session cookies */
7258 s_cookiejar
= soup_cookie_jar_new();
7259 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7260 cookie_policy
, (void *)NULL
);
7263 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7267 setup_proxy(char *uri
)
7270 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7271 soup_uri_free(proxy_uri
);
7275 if (http_proxy
!= uri
) {
7282 http_proxy
= g_strdup(uri
);
7283 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7284 proxy_uri
= soup_uri_new(http_proxy
);
7285 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7290 send_url_to_socket(char *url
)
7292 int s
, len
, rv
= -1;
7293 struct sockaddr_un sa
;
7295 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7296 warnx("send_url_to_socket: socket");
7300 sa
.sun_family
= AF_UNIX
;
7301 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7302 work_dir
, XT_SOCKET_FILE
);
7305 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7306 warnx("send_url_to_socket: connect");
7310 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
7311 warnx("send_url_to_socket: send");
7320 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7323 char str
[XT_MAX_URL_LENGTH
];
7324 socklen_t t
= sizeof(struct sockaddr_un
);
7325 struct sockaddr_un sa
;
7330 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7331 warn("socket_watcher: accept");
7335 if (getpeereid(s
, &uid
, &gid
) == -1) {
7336 warn("socket_watcher: getpeereid");
7339 if (uid
!= getuid() || gid
!= getgid()) {
7340 warnx("socket_watcher: unauthorized user");
7346 warnx("socket_watcher: not a valid user");
7350 n
= recv(s
, str
, sizeof(str
), 0);
7354 create_new_tab(str
, NULL
, 1);
7361 struct sockaddr_un sa
;
7363 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7364 warn("is_running: socket");
7368 sa
.sun_family
= AF_UNIX
;
7369 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7370 work_dir
, XT_SOCKET_FILE
);
7373 /* connect to see if there is a listener */
7374 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7375 rv
= 0; /* not running */
7377 rv
= 1; /* already running */
7388 struct sockaddr_un sa
;
7390 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7391 warn("build_socket: socket");
7395 sa
.sun_family
= AF_UNIX
;
7396 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7397 work_dir
, XT_SOCKET_FILE
);
7400 /* connect to see if there is a listener */
7401 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7402 /* no listener so we will */
7403 unlink(sa
.sun_path
);
7405 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7406 warn("build_socket: bind");
7410 if (listen(s
, 1) == -1) {
7411 warn("build_socket: listen");
7427 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
7432 main(int argc
, char *argv
[])
7435 int c
, s
, optn
= 0, focus
= 1;
7436 char conf
[PATH_MAX
] = { '\0' };
7437 char file
[PATH_MAX
];
7438 char *env_proxy
= NULL
;
7441 struct sigaction sact
;
7445 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
7447 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
7456 errx(0 , "Version: %s", version
);
7459 strlcpy(conf
, optarg
, sizeof(conf
));
7462 strlcpy(named_session
, optarg
, sizeof(named_session
));
7480 RB_INIT(&downloads
);
7484 TAILQ_INIT(&aliases
);
7490 gnutls_global_init();
7492 /* generate session keys for xtp pages */
7493 generate_xtp_session_key(&dl_session_key
);
7494 generate_xtp_session_key(&hl_session_key
);
7495 generate_xtp_session_key(&cl_session_key
);
7496 generate_xtp_session_key(&fl_session_key
);
7499 gtk_init(&argc
, &argv
);
7500 if (!g_thread_supported())
7501 g_thread_init(NULL
);
7504 bzero(&sact
, sizeof(sact
));
7505 sigemptyset(&sact
.sa_mask
);
7506 sact
.sa_handler
= sigchild
;
7507 sact
.sa_flags
= SA_NOCLDSTOP
;
7508 sigaction(SIGCHLD
, &sact
, NULL
);
7510 /* set download dir */
7511 pwd
= getpwuid(getuid());
7513 errx(1, "invalid user %d", getuid());
7514 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
7516 /* set default string settings */
7517 home
= g_strdup("http://www.peereboom.us");
7518 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
7519 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
7520 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
7522 /* read config file */
7523 if (strlen(conf
) == 0)
7524 snprintf(conf
, sizeof conf
, "%s/.%s",
7525 pwd
->pw_dir
, XT_CONF_FILE
);
7526 config_parse(conf
, 0);
7528 /* working directory */
7529 if (strlen(work_dir
) == 0)
7530 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
7531 pwd
->pw_dir
, XT_DIR
);
7532 if (stat(work_dir
, &sb
)) {
7533 if (mkdir(work_dir
, S_IRWXU
) == -1)
7534 err(1, "mkdir work_dir");
7535 if (stat(work_dir
, &sb
))
7536 err(1, "stat work_dir");
7538 if (S_ISDIR(sb
.st_mode
) == 0)
7539 errx(1, "%s not a dir", work_dir
);
7540 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7541 warnx("fixing invalid permissions on %s", work_dir
);
7542 if (chmod(work_dir
, S_IRWXU
) == -1)
7546 /* icon cache dir */
7547 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
7548 if (stat(cache_dir
, &sb
)) {
7549 if (mkdir(cache_dir
, S_IRWXU
) == -1)
7550 err(1, "mkdir cache_dir");
7551 if (stat(cache_dir
, &sb
))
7552 err(1, "stat cache_dir");
7554 if (S_ISDIR(sb
.st_mode
) == 0)
7555 errx(1, "%s not a dir", cache_dir
);
7556 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7557 warnx("fixing invalid permissions on %s", cache_dir
);
7558 if (chmod(cache_dir
, S_IRWXU
) == -1)
7563 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
7564 if (stat(certs_dir
, &sb
)) {
7565 if (mkdir(certs_dir
, S_IRWXU
) == -1)
7566 err(1, "mkdir certs_dir");
7567 if (stat(certs_dir
, &sb
))
7568 err(1, "stat certs_dir");
7570 if (S_ISDIR(sb
.st_mode
) == 0)
7571 errx(1, "%s not a dir", certs_dir
);
7572 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7573 warnx("fixing invalid permissions on %s", certs_dir
);
7574 if (chmod(certs_dir
, S_IRWXU
) == -1)
7579 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
7580 work_dir
, XT_SESSIONS_DIR
);
7581 if (stat(sessions_dir
, &sb
)) {
7582 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
7583 err(1, "mkdir sessions_dir");
7584 if (stat(sessions_dir
, &sb
))
7585 err(1, "stat sessions_dir");
7587 if (S_ISDIR(sb
.st_mode
) == 0)
7588 errx(1, "%s not a dir", sessions_dir
);
7589 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7590 warnx("fixing invalid permissions on %s", sessions_dir
);
7591 if (chmod(sessions_dir
, S_IRWXU
) == -1)
7594 /* runtime settings that can override config file */
7595 if (runtime_settings
[0] != '\0')
7596 config_parse(runtime_settings
, 1);
7599 if (!strcmp(download_dir
, pwd
->pw_dir
))
7600 strlcat(download_dir
, "/downloads", sizeof download_dir
);
7601 if (stat(download_dir
, &sb
)) {
7602 if (mkdir(download_dir
, S_IRWXU
) == -1)
7603 err(1, "mkdir download_dir");
7604 if (stat(download_dir
, &sb
))
7605 err(1, "stat download_dir");
7607 if (S_ISDIR(sb
.st_mode
) == 0)
7608 errx(1, "%s not a dir", download_dir
);
7609 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7610 warnx("fixing invalid permissions on %s", download_dir
);
7611 if (chmod(download_dir
, S_IRWXU
) == -1)
7615 /* favorites file */
7616 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
7617 if (stat(file
, &sb
)) {
7618 warnx("favorites file doesn't exist, creating it");
7619 if ((f
= fopen(file
, "w")) == NULL
)
7620 err(1, "favorites");
7625 session
= webkit_get_default_session();
7630 if (stat(ssl_ca_file
, &sb
)) {
7631 warn("no CA file: %s", ssl_ca_file
);
7632 g_free(ssl_ca_file
);
7635 g_object_set(session
,
7636 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
7637 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
7642 env_proxy
= getenv("http_proxy");
7644 setup_proxy(env_proxy
);
7646 setup_proxy(http_proxy
);
7648 /* see if there is already an xxxterm running */
7649 if (single_instance
&& is_running()) {
7651 warnx("already running");
7656 send_url_to_socket(argv
[0]);
7667 if (save_global_history
)
7668 restore_global_history();
7670 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
7671 restore_saved_tabs();
7673 a
.s
= named_session
;
7674 a
.i
= XT_SES_DONOTHING
;
7675 open_tabs(NULL
, &a
);
7679 create_new_tab(argv
[0], NULL
, focus
);
7686 if (TAILQ_EMPTY(&tabs
))
7687 create_new_tab(home
, NULL
, 1);
7690 if ((s
= build_socket()) != -1)
7691 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
7695 gnutls_global_deinit();