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 * multi letter commands
24 * pre and post counts for commands
25 * autocompletion on various inputs
26 * create privacy browsing
27 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #elif defined(__FreeBSD__)
50 #include "freebsd/util.h"
56 #include <sys/queue.h>
58 #include <sys/socket.h>
62 #include <gdk/gdkkeysyms.h>
63 #include <webkit/webkit.h>
64 #include <libsoup/soup.h>
65 #include <gnutls/gnutls.h>
66 #include <JavaScriptCore/JavaScript.h>
67 #include <gnutls/x509.h>
69 #include "javascript.h"
72 javascript.h borrowed from vimprobable2 under the following license:
74 Copyright (c) 2009 Leon Winter
75 Copyright (c) 2009 Hannes Schueller
76 Copyright (c) 2009 Matto Fransen
78 Permission is hereby granted, free of charge, to any person obtaining a copy
79 of this software and associated documentation files (the "Software"), to deal
80 in the Software without restriction, including without limitation the rights
81 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
82 copies of the Software, and to permit persons to whom the Software is
83 furnished to do so, subject to the following conditions:
85 The above copyright notice and this permission notice shall be included in
86 all copies or substantial portions of the Software.
88 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
89 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
91 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
92 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
93 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
97 static char *version
= "$xxxterm$";
99 /* hooked functions */
100 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
101 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
106 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
107 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
108 #define XT_D_MOVE 0x0001
109 #define XT_D_KEY 0x0002
110 #define XT_D_TAB 0x0004
111 #define XT_D_URL 0x0008
112 #define XT_D_CMD 0x0010
113 #define XT_D_NAV 0x0020
114 #define XT_D_DOWNLOAD 0x0040
115 #define XT_D_CONFIG 0x0080
116 #define XT_D_JS 0x0100
117 #define XT_D_FAVORITE 0x0200
118 #define XT_D_PRINTING 0x0400
119 #define XT_D_COOKIE 0x0800
120 #define XT_D_KEYBINDING 0x1000
121 u_int32_t swm_debug
= 0
137 #define DPRINTF(x...)
138 #define DNPRINTF(n,x...)
141 #define LENGTH(x) (sizeof x / sizeof x[0])
142 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
143 ~(GDK_BUTTON1_MASK) & \
144 ~(GDK_BUTTON2_MASK) & \
145 ~(GDK_BUTTON3_MASK) & \
146 ~(GDK_BUTTON4_MASK) & \
158 TAILQ_ENTRY(tab
) entry
;
160 GtkWidget
*tab_content
;
163 GtkWidget
*uri_entry
;
164 GtkWidget
*search_entry
;
166 GtkWidget
*browser_win
;
167 GtkWidget
*statusbar
;
174 GtkWidget
*js_toggle
;
175 GtkEntryCompletion
*completion
;
179 WebKitWebHistoryItem
*item
;
180 WebKitWebBackForwardList
*bfl
;
183 WebKitNetworkRequest
*icon_request
;
184 WebKitDownload
*icon_download
;
185 GdkPixbuf
*icon_pixbuf
;
186 gchar
*icon_dest_uri
;
188 /* adjustments for browser */
191 GtkAdjustment
*adjust_h
;
192 GtkAdjustment
*adjust_v
;
198 int xtp_meaning
; /* identifies dls/favorites */
203 #define XT_HINT_NONE (0)
204 #define XT_HINT_NUMERICAL (1)
205 #define XT_HINT_ALPHANUM (2)
209 /* custom stylesheet */
218 WebKitWebSettings
*settings
;
222 TAILQ_HEAD(tab_list
, tab
);
225 RB_ENTRY(history
) entry
;
229 RB_HEAD(history_list
, history
);
232 RB_ENTRY(download
) entry
;
234 WebKitDownload
*download
;
237 RB_HEAD(download_list
, download
);
240 RB_ENTRY(domain
) entry
;
242 int handy
; /* app use */
244 RB_HEAD(domain_list
, domain
);
247 TAILQ_ENTRY(undo
) entry
;
250 int back
; /* Keeps track of how many back
251 * history items there are. */
253 TAILQ_HEAD(undo_tailq
, undo
);
255 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
256 int next_download_id
= 1;
264 #define XT_NAME ("XXXTerm")
265 #define XT_DIR (".xxxterm")
266 #define XT_CACHE_DIR ("cache")
267 #define XT_CERT_DIR ("certs/")
268 #define XT_SESSIONS_DIR ("sessions/")
269 #define XT_CONF_FILE ("xxxterm.conf")
270 #define XT_FAVS_FILE ("favorites")
271 #define XT_SAVED_TABS_FILE ("main_session")
272 #define XT_RESTART_TABS_FILE ("restart_tabs")
273 #define XT_SOCKET_FILE ("socket")
274 #define XT_HISTORY_FILE ("history")
275 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
276 #define XT_CB_HANDLED (TRUE)
277 #define XT_CB_PASSTHROUGH (FALSE)
278 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
279 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
280 #define XT_DLMAN_REFRESH "10"
281 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
282 "td {overflow: hidden;" \
283 " padding: 2px 2px 2px 2px;" \
284 " border: 1px solid black}\n" \
285 "tr:hover {background: #ffff99 ;}\n" \
286 "th {background-color: #cccccc;" \
287 " border: 1px solid black}" \
288 "table {border-spacing: 0; " \
290 " border: 1px black solid;}\n" \
292 " border: 1px solid black;" \
298 " background: green;}" \
300 " font-size: small;" \
301 " text-align: center;}" \
303 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
304 #define XT_MAX_UNDO_CLOSE_TAB (32)
305 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
306 #define XT_PRINT_EXTRA_MARGIN 10
309 #define XT_COLOR_RED "#cc0000"
310 #define XT_COLOR_YELLOW "#ffff66"
311 #define XT_COLOR_BLUE "lightblue"
312 #define XT_COLOR_GREEN "#99ff66"
313 #define XT_COLOR_WHITE "white"
314 #define XT_COLOR_BLACK "black"
317 * xxxterm "protocol" (xtp)
318 * We use this for managing stuff like downloads and favorites. They
319 * make magical HTML pages in memory which have xxxt:// links in order
320 * to communicate with xxxterm's internals. These links take the format:
321 * xxxt://class/session_key/action/arg
323 * Don't begin xtp class/actions as 0. atoi returns that on error.
325 * Typically we have not put addition of items in this framework, as
326 * adding items is either done via an ex-command or via a keybinding instead.
329 #define XT_XTP_STR "xxxt://"
331 /* XTP classes (xxxt://<class>) */
332 #define XT_XTP_DL 1 /* downloads */
333 #define XT_XTP_HL 2 /* history */
334 #define XT_XTP_CL 3 /* cookies */
335 #define XT_XTP_FL 4 /* favorites */
337 /* XTP download actions */
338 #define XT_XTP_DL_LIST 1
339 #define XT_XTP_DL_CANCEL 2
340 #define XT_XTP_DL_REMOVE 3
342 /* XTP history actions */
343 #define XT_XTP_HL_LIST 1
344 #define XT_XTP_HL_REMOVE 2
346 /* XTP cookie actions */
347 #define XT_XTP_CL_LIST 1
348 #define XT_XTP_CL_REMOVE 2
350 /* XTP cookie actions */
351 #define XT_XTP_FL_LIST 1
352 #define XT_XTP_FL_REMOVE 2
354 /* xtp tab meanings - identifies which tabs have xtp pages in */
355 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
356 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
357 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
358 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
359 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
362 #define XT_MOVE_INVALID (0)
363 #define XT_MOVE_DOWN (1)
364 #define XT_MOVE_UP (2)
365 #define XT_MOVE_BOTTOM (3)
366 #define XT_MOVE_TOP (4)
367 #define XT_MOVE_PAGEDOWN (5)
368 #define XT_MOVE_PAGEUP (6)
369 #define XT_MOVE_HALFDOWN (7)
370 #define XT_MOVE_HALFUP (8)
371 #define XT_MOVE_LEFT (9)
372 #define XT_MOVE_FARLEFT (10)
373 #define XT_MOVE_RIGHT (11)
374 #define XT_MOVE_FARRIGHT (12)
376 #define XT_TAB_LAST (-4)
377 #define XT_TAB_FIRST (-3)
378 #define XT_TAB_PREV (-2)
379 #define XT_TAB_NEXT (-1)
380 #define XT_TAB_INVALID (0)
381 #define XT_TAB_NEW (1)
382 #define XT_TAB_DELETE (2)
383 #define XT_TAB_DELQUIT (3)
384 #define XT_TAB_OPEN (4)
385 #define XT_TAB_UNDO_CLOSE (5)
386 #define XT_TAB_SHOW (6)
387 #define XT_TAB_HIDE (7)
389 #define XT_NAV_INVALID (0)
390 #define XT_NAV_BACK (1)
391 #define XT_NAV_FORWARD (2)
392 #define XT_NAV_RELOAD (3)
393 #define XT_NAV_RELOAD_CACHE (4)
395 #define XT_FOCUS_INVALID (0)
396 #define XT_FOCUS_URI (1)
397 #define XT_FOCUS_SEARCH (2)
399 #define XT_SEARCH_INVALID (0)
400 #define XT_SEARCH_NEXT (1)
401 #define XT_SEARCH_PREV (2)
403 #define XT_PASTE_CURRENT_TAB (0)
404 #define XT_PASTE_NEW_TAB (1)
406 #define XT_FONT_SET (0)
408 #define XT_URL_SHOW (1)
409 #define XT_URL_HIDE (2)
411 #define XT_STATUSBAR_SHOW (1)
412 #define XT_STATUSBAR_HIDE (2)
414 #define XT_WL_TOGGLE (1<<0)
415 #define XT_WL_ENABLE (1<<1)
416 #define XT_WL_DISABLE (1<<2)
417 #define XT_WL_FQDN (1<<3) /* default */
418 #define XT_WL_TOPLEVEL (1<<4)
419 #define XT_WL_PERSISTENT (1<<5)
420 #define XT_WL_SESSION (1<<6)
422 #define XT_SHOW (1<<7)
423 #define XT_DELETE (1<<8)
424 #define XT_SAVE (1<<9)
425 #define XT_OPEN (1<<10)
427 #define XT_CMD_OPEN (0)
428 #define XT_CMD_OPEN_CURRENT (1)
429 #define XT_CMD_TABNEW (2)
430 #define XT_CMD_TABNEW_CURRENT (3)
432 #define XT_STATUS_NOTHING (0)
433 #define XT_STATUS_LINK (1)
434 #define XT_STATUS_URI (2)
435 #define XT_STATUS_LOADING (3)
437 #define XT_SES_DONOTHING (0)
438 #define XT_SES_CLOSETABS (1)
440 #define XT_BM_NORMAL (0)
441 #define XT_BM_WHITELIST (1)
442 #define XT_BM_KIOSK (2)
450 TAILQ_ENTRY(mime_type
) entry
;
452 TAILQ_HEAD(mime_type_list
, mime_type
);
458 TAILQ_ENTRY(alias
) entry
;
460 TAILQ_HEAD(alias_list
, alias
);
462 /* settings that require restart */
463 int tabless
= 0; /* allow only 1 tab */
464 int enable_socket
= 0;
465 int single_instance
= 0; /* only allow one xxxterm to run */
466 int fancy_bar
= 1; /* fancy toolbar */
467 int browser_mode
= XT_BM_NORMAL
;
469 /* runtime settings */
470 int show_tabs
= 1; /* show tabs on notebook */
471 int show_url
= 1; /* show url toolbar on notebook */
472 int show_statusbar
= 0; /* vimperator style status bar */
473 int ctrl_click_focus
= 0; /* ctrl click gets focus */
474 int cookies_enabled
= 1; /* enable cookies */
475 int read_only_cookies
= 0; /* enable to not write cookies */
476 int enable_scripts
= 1;
477 int enable_plugins
= 0;
478 int default_font_size
= 12;
479 gfloat default_zoom_level
= 1.0;
480 int window_height
= 768;
481 int window_width
= 1024;
482 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
483 unsigned refresh_interval
= 10; /* download refresh interval */
484 int enable_cookie_whitelist
= 0;
485 int enable_js_whitelist
= 0;
486 time_t session_timeout
= 3600; /* cookie session timeout */
487 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
488 char *ssl_ca_file
= NULL
;
489 char *resource_dir
= NULL
;
490 gboolean ssl_strict_certs
= FALSE
;
491 int append_next
= 1; /* append tab after current tab */
493 char *search_string
= NULL
;
494 char *http_proxy
= NULL
;
495 char download_dir
[PATH_MAX
];
496 char runtime_settings
[PATH_MAX
]; /* override of settings */
497 int allow_volatile_cookies
= 0;
498 int save_global_history
= 0; /* save global history to disk */
499 char *user_agent
= NULL
;
500 int save_rejected_cookies
= 0;
501 time_t session_autosave
= 0;
502 int guess_search
= 0;
503 int dns_prefetch
= FALSE
;
504 gint max_connections
= 25;
505 gint max_host_connections
= 5;
509 int set_download_dir(struct settings
*, char *);
510 int set_work_dir(struct settings
*, char *);
511 int set_runtime_dir(struct settings
*, char *);
512 int set_browser_mode(struct settings
*, char *);
513 int set_cookie_policy(struct settings
*, char *);
514 int add_alias(struct settings
*, char *);
515 int add_mime_type(struct settings
*, char *);
516 int add_cookie_wl(struct settings
*, char *);
517 int add_js_wl(struct settings
*, char *);
518 int add_kb(struct settings
*, char *);
519 void button_set_stockid(GtkWidget
*, char *);
520 GtkWidget
* create_button(char *, char *, int);
522 char *get_browser_mode(struct settings
*);
523 char *get_cookie_policy(struct settings
*);
525 char *get_download_dir(struct settings
*);
526 char *get_work_dir(struct settings
*);
527 char *get_runtime_dir(struct settings
*);
529 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
530 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
531 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
532 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
533 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
536 int (*set
)(struct settings
*, char *);
537 char *(*get
)(struct settings
*);
538 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
541 struct special s_browser_mode
= {
547 struct special s_cookie
= {
553 struct special s_alias
= {
559 struct special s_mime
= {
565 struct special s_js
= {
571 struct special s_kb
= {
577 struct special s_cookie_wl
= {
583 struct special s_download_dir
= {
589 struct special s_work_dir
= {
598 #define XT_S_INVALID (0)
601 #define XT_S_FLOAT (3)
603 #define XT_SF_RESTART (1<<0)
604 #define XT_SF_RUNTIME (1<<1)
610 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
611 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
612 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
613 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
614 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
615 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
616 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
617 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
618 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
619 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
620 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
621 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
622 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
623 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
624 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
625 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
626 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
627 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
628 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
629 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
630 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
631 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
632 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
633 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
634 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
635 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
636 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
637 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
638 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
639 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
640 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
641 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
642 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
643 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
644 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
645 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
646 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
647 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
648 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
650 /* runtime settings */
651 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
652 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
653 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
654 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
655 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
658 int about(struct tab
*, struct karg
*);
659 int blank(struct tab
*, struct karg
*);
660 int cookie_show_wl(struct tab
*, struct karg
*);
661 int js_show_wl(struct tab
*, struct karg
*);
662 int help(struct tab
*, struct karg
*);
663 int set(struct tab
*, struct karg
*);
664 int stats(struct tab
*, struct karg
*);
665 int marco(struct tab
*, struct karg
*);
666 const char * marco_message(int *);
667 int xtp_page_cl(struct tab
*, struct karg
*);
668 int xtp_page_dl(struct tab
*, struct karg
*);
669 int xtp_page_fl(struct tab
*, struct karg
*);
670 int xtp_page_hl(struct tab
*, struct karg
*);
672 #define XT_URI_ABOUT ("about:")
673 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
674 #define XT_URI_ABOUT_ABOUT ("about")
675 #define XT_URI_ABOUT_BLANK ("blank")
676 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
677 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
678 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
679 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
680 #define XT_URI_ABOUT_FAVORITES ("favorites")
681 #define XT_URI_ABOUT_HELP ("help")
682 #define XT_URI_ABOUT_HISTORY ("history")
683 #define XT_URI_ABOUT_JSWL ("jswl")
684 #define XT_URI_ABOUT_SET ("set")
685 #define XT_URI_ABOUT_STATS ("stats")
686 #define XT_URI_ABOUT_MARCO ("marco")
690 int (*func
)(struct tab
*, struct karg
*);
692 { XT_URI_ABOUT_ABOUT
, about
},
693 { XT_URI_ABOUT_BLANK
, blank
},
694 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
695 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
696 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
697 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
698 { XT_URI_ABOUT_HELP
, help
},
699 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
700 { XT_URI_ABOUT_JSWL
, js_show_wl
},
701 { XT_URI_ABOUT_SET
, set
},
702 { XT_URI_ABOUT_STATS
, stats
},
703 { XT_URI_ABOUT_MARCO
, marco
},
707 extern char *__progname
;
710 GtkWidget
*main_window
;
711 GtkNotebook
*notebook
;
712 GtkWidget
*arrow
, *abtn
;
713 struct tab_list tabs
;
714 struct history_list hl
;
715 struct download_list downloads
;
716 struct domain_list c_wl
;
717 struct domain_list js_wl
;
718 struct undo_tailq undos
;
719 struct keybinding_list kbl
;
721 int updating_dl_tabs
= 0;
722 int updating_hl_tabs
= 0;
723 int updating_cl_tabs
= 0;
724 int updating_fl_tabs
= 0;
726 uint64_t blocked_cookies
= 0;
727 char named_session
[PATH_MAX
];
728 void update_favicon(struct tab
*);
729 int icon_size_map(int);
731 GtkListStore
*completion_model
;
732 void completion_add(struct tab
*);
733 void completion_add_uri(const gchar
*);
738 int saved_errno
, status
;
743 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
747 if (errno
!= ECHILD
) {
749 clog_warn("sigchild: waitpid:");
755 if (WIFEXITED(status
)) {
756 if (WEXITSTATUS(status
) != 0) {
758 clog_warnx("sigchild: child exit status: %d",
759 WEXITSTATUS(status));
764 clog_warnx("sigchild: child is terminated abnormally");
773 is_g_object_setting(GObject
*o
, char *str
)
775 guint n_props
= 0, i
;
776 GParamSpec
**proplist
;
778 if (! G_IS_OBJECT(o
))
781 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
784 for (i
=0; i
< n_props
; i
++) {
785 if (! strcmp(proplist
[i
]->name
, str
))
792 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
798 /* we set this to indicate we want to manually do navaction */
800 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
801 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
804 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, title
);
805 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
808 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
809 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
810 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
811 GTK_ENTRY_ICON_PRIMARY
, pb
);
812 gdk_pixbuf_unref(pb
);
817 set_status(struct tab
*t
, gchar
*s
, int status
)
825 case XT_STATUS_LOADING
:
826 type
= g_strdup_printf("Loading: %s", s
);
830 type
= g_strdup_printf("Link: %s", s
);
832 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
836 type
= g_strdup_printf("%s", s
);
838 t
->status
= g_strdup(type
);
842 t
->status
= g_strdup(s
);
844 case XT_STATUS_NOTHING
:
849 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
855 hide_oops(struct tab
*t
)
857 gtk_widget_hide(t
->oops
);
861 hide_cmd(struct tab
*t
)
863 gtk_widget_hide(t
->cmd
);
867 show_cmd(struct tab
*t
)
869 gtk_widget_hide(t
->oops
);
870 gtk_widget_show(t
->cmd
);
874 show_oops(struct tab
*t
, const char *fmt
, ...)
883 if (vasprintf(&msg
, fmt
, ap
) == -1)
884 errx(1, "show_oops failed");
887 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
888 gtk_widget_hide(t
->cmd
);
889 gtk_widget_show(t
->oops
);
892 /* XXX collapse with show_oops */
894 show_oops_s(const char *fmt
, ...)
898 struct tab
*ti
, *t
= NULL
;
903 TAILQ_FOREACH(ti
, &tabs
, entry
)
904 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
912 if (vasprintf(&msg
, fmt
, ap
) == -1)
913 errx(1, "show_oops_s failed");
916 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
917 gtk_widget_hide(t
->cmd
);
918 gtk_widget_show(t
->oops
);
922 get_as_string(struct settings
*s
)
933 warnx("get_as_string skip %s\n", s
->name
);
934 } else if (s
->type
== XT_S_INT
)
935 r
= g_strdup_printf("%d", *s
->ival
);
936 else if (s
->type
== XT_S_STR
)
937 r
= g_strdup(*s
->sval
);
938 else if (s
->type
== XT_S_FLOAT
)
939 r
= g_strdup_printf("%f", *s
->fval
);
941 r
= g_strdup_printf("INVALID TYPE");
947 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
952 for (i
= 0; i
< LENGTH(rs
); i
++) {
953 if (rs
[i
].s
&& rs
[i
].s
->walk
)
954 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
956 s
= get_as_string(&rs
[i
]);
957 cb(&rs
[i
], s
, cb_args
);
964 set_browser_mode(struct settings
*s
, char *val
)
966 if (!strcmp(val
, "whitelist")) {
967 browser_mode
= XT_BM_WHITELIST
;
968 allow_volatile_cookies
= 0;
969 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
971 enable_cookie_whitelist
= 1;
972 read_only_cookies
= 0;
973 save_rejected_cookies
= 0;
974 session_timeout
= 3600;
976 enable_js_whitelist
= 1;
977 } else if (!strcmp(val
, "normal")) {
978 browser_mode
= XT_BM_NORMAL
;
979 allow_volatile_cookies
= 0;
980 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
982 enable_cookie_whitelist
= 0;
983 read_only_cookies
= 0;
984 save_rejected_cookies
= 0;
985 session_timeout
= 3600;
987 enable_js_whitelist
= 0;
988 } else if (!strcmp(val
, "kiosk")) {
989 browser_mode
= XT_BM_KIOSK
;
990 allow_volatile_cookies
= 0;
991 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
993 enable_cookie_whitelist
= 0;
994 read_only_cookies
= 0;
995 save_rejected_cookies
= 0;
996 session_timeout
= 3600;
998 enable_js_whitelist
= 0;
1008 get_browser_mode(struct settings
*s
)
1012 if (browser_mode
== XT_BM_WHITELIST
)
1013 r
= g_strdup("whitelist");
1014 else if (browser_mode
== XT_BM_NORMAL
)
1015 r
= g_strdup("normal");
1016 else if (browser_mode
== XT_BM_KIOSK
)
1017 r
= g_strdup("kiosk");
1025 set_cookie_policy(struct settings
*s
, char *val
)
1027 if (!strcmp(val
, "no3rdparty"))
1028 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1029 else if (!strcmp(val
, "accept"))
1030 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1031 else if (!strcmp(val
, "reject"))
1032 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1040 get_cookie_policy(struct settings
*s
)
1044 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1045 r
= g_strdup("no3rdparty");
1046 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1047 r
= g_strdup("accept");
1048 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1049 r
= g_strdup("reject");
1057 get_download_dir(struct settings
*s
)
1059 if (download_dir
[0] == '\0')
1061 return (g_strdup(download_dir
));
1065 set_download_dir(struct settings
*s
, char *val
)
1068 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1069 pwd
->pw_dir
, &val
[1]);
1071 strlcpy(download_dir
, val
, sizeof download_dir
);
1078 * We use these to prevent people putting xxxt:// URLs on
1079 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1081 #define XT_XTP_SES_KEY_SZ 8
1082 #define XT_XTP_SES_KEY_HEX_FMT \
1083 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1084 char *dl_session_key
; /* downloads */
1085 char *hl_session_key
; /* history list */
1086 char *cl_session_key
; /* cookie list */
1087 char *fl_session_key
; /* favorites list */
1089 char work_dir
[PATH_MAX
];
1090 char certs_dir
[PATH_MAX
];
1091 char cache_dir
[PATH_MAX
];
1092 char sessions_dir
[PATH_MAX
];
1093 char cookie_file
[PATH_MAX
];
1094 SoupURI
*proxy_uri
= NULL
;
1095 SoupSession
*session
;
1096 SoupCookieJar
*s_cookiejar
;
1097 SoupCookieJar
*p_cookiejar
;
1098 char rc_fname
[PATH_MAX
];
1100 struct mime_type_list mtl
;
1101 struct alias_list aliases
;
1104 struct tab
*create_new_tab(char *, struct undo
*, int);
1105 void delete_tab(struct tab
*);
1106 void adjustfont_webkit(struct tab
*, int);
1107 int run_script(struct tab
*, char *);
1108 int download_rb_cmp(struct download
*, struct download
*);
1109 gboolean
cmd_execute(struct tab
*t
, char *str
);
1112 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1114 return (strcmp(h1
->uri
, h2
->uri
));
1116 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1119 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1121 return (strcmp(d1
->d
, d2
->d
));
1123 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1126 get_work_dir(struct settings
*s
)
1128 if (work_dir
[0] == '\0')
1130 return (g_strdup(work_dir
));
1134 set_work_dir(struct settings
*s
, char *val
)
1137 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1138 pwd
->pw_dir
, &val
[1]);
1140 strlcpy(work_dir
, val
, sizeof work_dir
);
1146 * generate a session key to secure xtp commands.
1147 * pass in a ptr to the key in question and it will
1148 * be modified in place.
1151 generate_xtp_session_key(char **key
)
1153 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1159 /* make a new one */
1160 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1161 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1162 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1163 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1165 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1169 * validate a xtp session key.
1173 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1175 if (strcmp(trusted
, untrusted
) != 0) {
1176 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1185 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1187 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1189 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1191 struct valid_url_types
{
1202 valid_url_type(char *url
)
1206 for (i
= 0; i
< LENGTH(vut
); i
++)
1207 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1214 print_cookie(char *msg
, SoupCookie
*c
)
1220 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1221 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1222 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1223 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1224 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1225 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1226 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1227 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1228 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1229 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1233 walk_alias(struct settings
*s
,
1234 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1239 if (s
== NULL
|| cb
== NULL
) {
1240 show_oops_s("walk_alias invalid parameters");
1244 TAILQ_FOREACH(a
, &aliases
, entry
) {
1245 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1246 cb(s
, str
, cb_args
);
1252 match_alias(char *url_in
)
1256 char *url_out
= NULL
, *search
, *enc_arg
;
1258 search
= g_strdup(url_in
);
1260 if (strsep(&arg
, " \t") == NULL
) {
1261 show_oops_s("match_alias: NULL URL");
1265 TAILQ_FOREACH(a
, &aliases
, entry
) {
1266 if (!strcmp(search
, a
->a_name
))
1271 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1274 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1275 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1278 url_out
= g_strdup(a
->a_uri
);
1286 guess_url_type(char *url_in
)
1289 char *url_out
= NULL
, *enc_search
= NULL
;
1291 url_out
= match_alias(url_in
);
1292 if (url_out
!= NULL
)
1297 * If there is no dot nor slash in the string and it isn't a
1298 * path to a local file and doesn't resolves to an IP, assume
1299 * that the user wants to search for the string.
1302 if (strchr(url_in
, '.') == NULL
&&
1303 strchr(url_in
, '/') == NULL
&&
1304 stat(url_in
, &sb
) != 0 &&
1305 gethostbyname(url_in
) == NULL
) {
1307 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1308 url_out
= g_strdup_printf(search_string
, enc_search
);
1314 /* XXX not sure about this heuristic */
1315 if (stat(url_in
, &sb
) == 0)
1316 url_out
= g_strdup_printf("file://%s", url_in
);
1318 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1320 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1326 load_uri(struct tab
*t
, gchar
*uri
)
1329 gchar
*newuri
= NULL
;
1335 /* Strip leading spaces. */
1336 while(*uri
&& isspace(*uri
))
1339 if (strlen(uri
) == 0) {
1344 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1345 for (i
= 0; i
< LENGTH(about_list
); i
++)
1346 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1347 bzero(&args
, sizeof args
);
1348 about_list
[i
].func(t
, &args
);
1351 show_oops(t
, "invalid about page");
1355 if (valid_url_type(uri
)) {
1356 newuri
= guess_url_type(uri
);
1360 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1361 webkit_web_view_load_uri(t
->wv
, uri
);
1368 get_uri(WebKitWebView
*wv
)
1370 WebKitWebFrame
*frame
;
1373 frame
= webkit_web_view_get_main_frame(wv
);
1374 uri
= webkit_web_frame_get_uri(frame
);
1376 if (uri
&& strlen(uri
) > 0)
1383 add_alias(struct settings
*s
, char *line
)
1386 struct alias
*a
= NULL
;
1388 if (s
== NULL
|| line
== NULL
) {
1389 show_oops_s("add_alias invalid parameters");
1394 a
= g_malloc(sizeof(*a
));
1396 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1397 show_oops_s("add_alias: incomplete alias definition");
1400 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1401 show_oops_s("add_alias: invalid alias definition");
1405 a
->a_name
= g_strdup(alias
);
1406 a
->a_uri
= g_strdup(l
);
1408 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1410 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1420 add_mime_type(struct settings
*s
, char *line
)
1424 struct mime_type
*m
= NULL
;
1425 int downloadfirst
= 0;
1427 /* XXX this could be smarter */
1429 if (line
== NULL
&& strlen(line
) == 0) {
1430 show_oops_s("add_mime_type invalid parameters");
1439 m
= g_malloc(sizeof(*m
));
1441 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1442 show_oops_s("add_mime_type: invalid mime_type");
1445 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1446 mime_type
[strlen(mime_type
) - 1] = '\0';
1451 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1452 show_oops_s("add_mime_type: invalid mime_type");
1456 m
->mt_type
= g_strdup(mime_type
);
1457 m
->mt_action
= g_strdup(l
);
1458 m
->mt_download
= downloadfirst
;
1460 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1461 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1463 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1473 find_mime_type(char *mime_type
)
1475 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1477 TAILQ_FOREACH(m
, &mtl
, entry
) {
1478 if (m
->mt_default
&&
1479 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1482 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1495 walk_mime_type(struct settings
*s
,
1496 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1498 struct mime_type
*m
;
1501 if (s
== NULL
|| cb
== NULL
)
1502 show_oops_s("walk_mime_type invalid parameters");
1504 TAILQ_FOREACH(m
, &mtl
, entry
) {
1505 str
= g_strdup_printf("%s%s --> %s",
1507 m
->mt_default
? "*" : "",
1509 cb(s
, str
, cb_args
);
1515 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1520 if (str
== NULL
|| wl
== NULL
)
1522 if (strlen(str
) < 2)
1525 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1527 /* treat *.moo.com the same as .moo.com */
1528 if (str
[0] == '*' && str
[1] == '.')
1530 else if (str
[0] == '.')
1535 d
= g_malloc(sizeof *d
);
1537 d
->d
= g_strdup_printf(".%s", str
);
1539 d
->d
= g_strdup(str
);
1542 if (RB_INSERT(domain_list
, wl
, d
))
1545 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1556 add_cookie_wl(struct settings
*s
, char *entry
)
1558 wl_add(entry
, &c_wl
, 1);
1563 walk_cookie_wl(struct settings
*s
,
1564 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1568 if (s
== NULL
|| cb
== NULL
) {
1569 show_oops_s("walk_cookie_wl invalid parameters");
1573 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1574 cb(s
, d
->d
, cb_args
);
1578 walk_js_wl(struct settings
*s
,
1579 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1583 if (s
== NULL
|| cb
== NULL
) {
1584 show_oops_s("walk_js_wl invalid parameters");
1588 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1589 cb(s
, d
->d
, cb_args
);
1593 add_js_wl(struct settings
*s
, char *entry
)
1595 wl_add(entry
, &js_wl
, 1 /* persistent */);
1600 wl_find(const gchar
*search
, struct domain_list
*wl
)
1603 struct domain
*d
= NULL
, dfind
;
1606 if (search
== NULL
|| wl
== NULL
)
1608 if (strlen(search
) < 2)
1611 if (search
[0] != '.')
1612 s
= g_strdup_printf(".%s", search
);
1614 s
= g_strdup(search
);
1616 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1619 d
= RB_FIND(domain_list
, wl
, &dfind
);
1633 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1639 if (s
== NULL
|| wl
== NULL
)
1642 if (!strncmp(s
, "http://", strlen("http://")))
1643 s
= &s
[strlen("http://")];
1644 else if (!strncmp(s
, "https://", strlen("https://")))
1645 s
= &s
[strlen("https://")];
1650 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1651 /* chop string at first slash */
1652 if (s
[i
] == '/' || s
[i
] == '\0') {
1655 r
= wl_find(ss
, wl
);
1664 get_toplevel_domain(char *domain
)
1671 if (strlen(domain
) < 2)
1674 s
= &domain
[strlen(domain
) - 1];
1675 while (s
!= domain
) {
1691 settings_add(char *var
, char *val
)
1698 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1699 if (strcmp(var
, rs
[i
].name
))
1703 if (rs
[i
].s
->set(&rs
[i
], val
))
1704 errx(1, "invalid value for %s: %s", var
, val
);
1708 switch (rs
[i
].type
) {
1717 errx(1, "invalid sval for %s",
1731 errx(1, "invalid type for %s", var
);
1740 config_parse(char *filename
, int runtime
)
1743 char *line
, *cp
, *var
, *val
;
1744 size_t len
, lineno
= 0;
1746 char file
[PATH_MAX
];
1749 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1751 if (filename
== NULL
)
1754 if (runtime
&& runtime_settings
[0] != '\0') {
1755 snprintf(file
, sizeof file
, "%s/%s",
1756 work_dir
, runtime_settings
);
1757 if (stat(file
, &sb
)) {
1758 warnx("runtime file doesn't exist, creating it");
1759 if ((f
= fopen(file
, "w")) == NULL
)
1761 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1765 strlcpy(file
, filename
, sizeof file
);
1767 if ((config
= fopen(file
, "r")) == NULL
) {
1768 warn("config_parse: cannot open %s", filename
);
1773 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1774 if (feof(config
) || ferror(config
))
1778 cp
+= (long)strspn(cp
, WS
);
1779 if (cp
[0] == '\0') {
1785 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1786 errx(1, "invalid config file entry: %s", line
);
1788 cp
+= (long)strspn(cp
, WS
);
1790 if ((val
= strsep(&cp
, "\0")) == NULL
)
1793 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1794 handled
= settings_add(var
, val
);
1796 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1805 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1811 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1815 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1818 JSStringGetUTF8CString(jsref
, s
, l
);
1819 JSStringRelease(jsref
);
1825 disable_hints(struct tab
*t
)
1827 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1828 bzero(t
->hint_num
, sizeof t
->hint_num
);
1829 run_script(t
, "vimprobable_clear()");
1831 t
->hint_mode
= XT_HINT_NONE
;
1835 enable_hints(struct tab
*t
)
1837 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1838 run_script(t
, "vimprobable_show_hints()");
1840 t
->hint_mode
= XT_HINT_NONE
;
1843 #define XT_JS_OPEN ("open;")
1844 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1845 #define XT_JS_FIRE ("fire;")
1846 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1847 #define XT_JS_FOUND ("found;")
1848 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1851 run_script(struct tab
*t
, char *s
)
1853 JSGlobalContextRef ctx
;
1854 WebKitWebFrame
*frame
;
1856 JSValueRef val
, exception
;
1859 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1860 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1862 frame
= webkit_web_view_get_main_frame(t
->wv
);
1863 ctx
= webkit_web_frame_get_global_context(frame
);
1865 str
= JSStringCreateWithUTF8CString(s
);
1866 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1867 NULL
, 0, &exception
);
1868 JSStringRelease(str
);
1870 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1872 es
= js_ref_to_string(ctx
, exception
);
1873 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1877 es
= js_ref_to_string(ctx
, val
);
1878 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1880 /* handle return value right here */
1881 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1883 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1886 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1887 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1888 &es
[XT_JS_FIRE_LEN
]);
1893 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1894 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1905 hint(struct tab
*t
, struct karg
*args
)
1908 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1910 if (t
->hints_on
== 0)
1919 apply_style(struct tab
*t
)
1921 g_object_set(G_OBJECT(t
->settings
),
1922 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
1926 userstyle(struct tab
*t
, struct karg
*args
)
1928 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
1932 g_object_set(G_OBJECT(t
->settings
),
1933 "user-stylesheet-uri", NULL
, (char *)NULL
);
1942 * Doesn't work fully, due to the following bug:
1943 * https://bugs.webkit.org/show_bug.cgi?id=51747
1946 restore_global_history(void)
1948 char file
[PATH_MAX
];
1954 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1956 if ((f
= fopen(file
, "r")) == NULL
) {
1957 warnx("%s: fopen", __func__
);
1962 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1963 if (feof(f
) || ferror(f
))
1966 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1967 if (feof(f
) || ferror(f
)) {
1969 warnx("%s: broken history file\n", __func__
);
1973 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1974 webkit_web_history_item_new_with_data(uri
, title
);
1975 h
= g_malloc(sizeof(struct history
));
1976 h
->uri
= g_strdup(uri
);
1977 h
->title
= g_strdup(title
);
1978 RB_INSERT(history_list
, &hl
, h
);
1979 completion_add_uri(h
->uri
);
1981 warnx("%s: failed to restore history\n", __func__
);
1997 save_global_history_to_disk(struct tab
*t
)
1999 char file
[PATH_MAX
];
2003 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2005 if ((f
= fopen(file
, "w")) == NULL
) {
2006 show_oops(t
, "%s: global history file: %s",
2007 __func__
, strerror(errno
));
2011 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2012 if (h
->uri
&& h
->title
)
2013 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2022 quit(struct tab
*t
, struct karg
*args
)
2024 if (save_global_history
)
2025 save_global_history_to_disk(t
);
2033 open_tabs(struct tab
*t
, struct karg
*a
)
2035 char file
[PATH_MAX
];
2039 struct tab
*ti
, *tt
;
2044 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2045 if ((f
= fopen(file
, "r")) == NULL
)
2048 ti
= TAILQ_LAST(&tabs
, tab_list
);
2051 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2052 if (feof(f
) || ferror(f
))
2055 /* retrieve session name */
2056 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2057 strlcpy(named_session
,
2058 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2059 sizeof named_session
);
2063 if (uri
&& strlen(uri
))
2064 create_new_tab(uri
, NULL
, 1);
2070 /* close open tabs */
2071 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2073 tt
= TAILQ_FIRST(&tabs
);
2092 restore_saved_tabs(void)
2094 char file
[PATH_MAX
];
2095 int unlink_file
= 0;
2100 snprintf(file
, sizeof file
, "%s/%s",
2101 sessions_dir
, XT_RESTART_TABS_FILE
);
2102 if (stat(file
, &sb
) == -1)
2103 a
.s
= XT_SAVED_TABS_FILE
;
2106 a
.s
= XT_RESTART_TABS_FILE
;
2109 a
.i
= XT_SES_DONOTHING
;
2110 rv
= open_tabs(NULL
, &a
);
2119 save_tabs(struct tab
*t
, struct karg
*a
)
2121 char file
[PATH_MAX
];
2126 const gchar
**arr
= NULL
;
2131 snprintf(file
, sizeof file
, "%s/%s",
2132 sessions_dir
, named_session
);
2134 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2136 if ((f
= fopen(file
, "w")) == NULL
) {
2137 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2141 /* save session name */
2142 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2144 /* save tabs, in the order they are arranged in the notebook */
2145 TAILQ_FOREACH(ti
, &tabs
, entry
)
2148 arr
= g_malloc0(len
* sizeof(gchar
*));
2150 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2151 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2152 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2155 for (i
= 0; i
< len
; i
++)
2157 fprintf(f
, "%s\n", arr
[i
]);
2166 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2178 yank_uri(struct tab
*t
, struct karg
*args
)
2181 GtkClipboard
*clipboard
;
2183 if ((uri
= get_uri(t
->wv
)) == NULL
)
2186 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2187 gtk_clipboard_set_text(clipboard
, uri
, -1);
2198 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
2200 struct paste_args
*pap
;
2202 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
2205 pap
= (struct paste_args
*)data
;
2208 case XT_PASTE_CURRENT_TAB
:
2209 load_uri(pap
->t
, (gchar
*)text
);
2211 case XT_PASTE_NEW_TAB
:
2212 create_new_tab((gchar
*)text
, NULL
, 1);
2220 paste_uri(struct tab
*t
, struct karg
*args
)
2222 GtkClipboard
*clipboard
;
2223 struct paste_args
*pap
;
2225 pap
= g_malloc(sizeof(struct paste_args
));
2230 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2231 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
2237 find_domain(const gchar
*s
, int add_dot
)
2240 char *r
= NULL
, *ss
= NULL
;
2245 if (!strncmp(s
, "http://", strlen("http://")))
2246 s
= &s
[strlen("http://")];
2247 else if (!strncmp(s
, "https://", strlen("https://")))
2248 s
= &s
[strlen("https://")];
2254 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2255 /* chop string at first slash */
2256 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2259 r
= g_strdup_printf(".%s", ss
);
2270 toggle_cwl(struct tab
*t
, struct karg
*args
)
2274 char *dom
= NULL
, *dom_toggle
= NULL
;
2280 uri
= get_uri(t
->wv
);
2281 dom
= find_domain(uri
, 1);
2282 d
= wl_find(dom
, &c_wl
);
2289 if (args
->i
& XT_WL_TOGGLE
)
2291 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2293 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2296 if (args
->i
& XT_WL_TOPLEVEL
)
2297 dom_toggle
= get_toplevel_domain(dom
);
2302 /* enable cookies for domain */
2303 wl_add(dom_toggle
, &c_wl
, 0);
2305 /* disable cookies for domain */
2306 RB_REMOVE(domain_list
, &c_wl
, d
);
2308 webkit_web_view_reload(t
->wv
);
2315 toggle_js(struct tab
*t
, struct karg
*args
)
2320 char *dom
= NULL
, *dom_toggle
= NULL
;
2325 g_object_get(G_OBJECT(t
->settings
),
2326 "enable-scripts", &es
, (char *)NULL
);
2327 if (args
->i
& XT_WL_TOGGLE
)
2329 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2331 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2336 uri
= get_uri(t
->wv
);
2337 dom
= find_domain(uri
, 1);
2339 if (uri
== NULL
|| dom
== NULL
) {
2340 show_oops(t
, "Can't toggle domain in JavaScript white list");
2344 if (args
->i
& XT_WL_TOPLEVEL
)
2345 dom_toggle
= get_toplevel_domain(dom
);
2350 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2351 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2353 d
= wl_find(dom_toggle
, &js_wl
);
2355 RB_REMOVE(domain_list
, &js_wl
, d
);
2356 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2358 g_object_set(G_OBJECT(t
->settings
),
2359 "enable-scripts", es
, (char *)NULL
);
2360 g_object_set(G_OBJECT(t
->settings
),
2361 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2362 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2363 webkit_web_view_reload(t
->wv
);
2371 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2375 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2380 toggle_src(struct tab
*t
, struct karg
*args
)
2387 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2388 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2389 webkit_web_view_reload(t
->wv
);
2395 focus_webview(struct tab
*t
)
2400 /* only grab focus if we are visible */
2401 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2402 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2406 focus(struct tab
*t
, struct karg
*args
)
2408 if (t
== NULL
|| args
== NULL
)
2414 if (args
->i
== XT_FOCUS_URI
)
2415 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2416 else if (args
->i
== XT_FOCUS_SEARCH
)
2417 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2423 stats(struct tab
*t
, struct karg
*args
)
2425 char *stats
, *s
, line
[64 * 1024];
2426 uint64_t line_count
= 0;
2430 show_oops_s("stats invalid parameters");
2433 if (save_rejected_cookies
) {
2434 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2436 s
= fgets(line
, sizeof line
, r_cookie_f
);
2437 if (s
== NULL
|| feof(r_cookie_f
) ||
2443 snprintf(line
, sizeof line
,
2444 "<br>Cookies blocked(*) total: %llu", line_count
);
2446 show_oops(t
, "Can't open blocked cookies file: %s",
2450 stats
= g_strdup_printf(XT_DOCTYPE
2453 "<title>Statistics</title>"
2455 "<h1>Statistics</h1>"
2457 "Cookies blocked(*) this session: %llu"
2459 "<p><small><b>*</b> results vary based on settings"
2465 load_webkit_string(t
, stats
, XT_URI_ABOUT_STATS
);
2472 marco(struct tab
*t
, struct karg
*args
)
2474 char *message
, line
[64 * 1024];
2478 show_oops_s("marco invalid parameters");
2481 snprintf(line
, sizeof line
, "<br>%s", marco_message(&len
));
2483 message
= g_strdup_printf(XT_DOCTYPE
2486 "<title>Marco Sez...</title>"
2495 load_webkit_string(t
, message
, XT_URI_ABOUT_MARCO
);
2502 blank(struct tab
*t
, struct karg
*args
)
2505 show_oops_s("about invalid parameters");
2507 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2512 about(struct tab
*t
, struct karg
*args
)
2517 show_oops_s("about invalid parameters");
2519 about
= g_strdup_printf(XT_DOCTYPE
2522 "<title>About</title>"
2526 "<b>Version: %s</b><p>"
2529 "<li>Marco Peereboom <marco@peereboom.us></li>"
2530 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2531 "<li>Edd Barrett <vext01@gmail.com> </li>"
2532 "<li>Todd T. Fries <todd@fries.net> </li>"
2534 "Copyrights and licenses can be found on the XXXterm "
2535 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2541 load_webkit_string(t
, about
, XT_URI_ABOUT_ABOUT
);
2548 help(struct tab
*t
, struct karg
*args
)
2553 show_oops_s("help invalid parameters");
2558 "<title>XXXterm</title>"
2559 "<meta http-equiv=\"REFRESH\" content=\"0;"
2560 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2563 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2564 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2565 "cgi-bin/man-cgi?xxxterm</a>"
2570 load_webkit_string(t
, help
, XT_URI_ABOUT_HELP
);
2576 * update all favorite tabs apart from one. Pass NULL if
2577 * you want to update all.
2580 update_favorite_tabs(struct tab
*apart_from
)
2583 if (!updating_fl_tabs
) {
2584 updating_fl_tabs
= 1; /* stop infinite recursion */
2585 TAILQ_FOREACH(t
, &tabs
, entry
)
2586 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2587 && (t
!= apart_from
))
2588 xtp_page_fl(t
, NULL
);
2589 updating_fl_tabs
= 0;
2593 /* show a list of favorites (bookmarks) */
2595 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2597 char file
[PATH_MAX
];
2599 char *uri
= NULL
, *title
= NULL
;
2600 size_t len
, lineno
= 0;
2602 char *header
, *body
, *tmp
, *html
= NULL
;
2604 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2607 warn("%s: bad param", __func__
);
2609 /* mark tab as favorite list */
2610 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2612 /* new session key */
2613 if (!updating_fl_tabs
)
2614 generate_xtp_session_key(&fl_session_key
);
2616 /* open favorites */
2617 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2618 if ((f
= fopen(file
, "r")) == NULL
) {
2619 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2624 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2625 "<title>Favorites</title>\n"
2628 "<h1>Favorites</h1>\n",
2632 body
= g_strdup_printf("<div align='center'><table><tr>"
2633 "<th style='width: 4%%'>#</th><th>Link</th>"
2634 "<th style='width: 15%%'>Remove</th></tr>\n");
2637 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2638 if (feof(f
) || ferror(f
))
2646 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2647 if (feof(f
) || ferror(f
)) {
2648 show_oops(t
, "favorites file corrupt");
2654 body
= g_strdup_printf("%s<tr>"
2656 "<td><a href='%s'>%s</a></td>"
2657 "<td style='text-align: center'>"
2658 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2660 body
, i
, uri
, title
,
2661 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2673 /* if none, say so */
2676 body
= g_strdup_printf("%s<tr>"
2677 "<td colspan='3' style='text-align: center'>"
2678 "No favorites - To add one use the 'favadd' command."
2679 "</td></tr>", body
);
2690 html
= g_strdup_printf("%s%s</table></div></html>",
2692 load_webkit_string(t
, html
, XT_URI_ABOUT_FAVORITES
);
2695 update_favorite_tabs(t
);
2708 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2709 size_t cert_count
, char *title
)
2711 gnutls_datum_t cinfo
;
2712 char *tmp
, *header
, *body
, *footer
;
2715 header
= g_strdup_printf("<html><head><title>%s</title></head><body>", title
);
2716 footer
= g_strdup("</body></html>");
2717 body
= g_strdup("");
2719 for (i
= 0; i
< cert_count
; i
++) {
2720 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2725 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2726 body
, i
, cinfo
.data
);
2727 gnutls_free(cinfo
.data
);
2731 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2735 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2740 ca_cmd(struct tab
*t
, struct karg
*args
)
2743 int rv
= 1, certs
= 0, certs_read
;
2746 gnutls_x509_crt_t
*c
= NULL
;
2747 char *certs_buf
= NULL
, *s
;
2749 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2750 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2754 if (fstat(fileno(f
), &sb
) == -1) {
2755 show_oops(t
, "Can't stat CA file: %s", strerror(errno
));
2759 certs_buf
= g_malloc(sb
.st_size
+ 1);
2760 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2761 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2764 certs_buf
[sb
.st_size
] = '\0';
2767 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2769 s
+= strlen("BEGIN CERTIFICATE");
2772 bzero(&dt
, sizeof dt
);
2773 dt
.data
= certs_buf
;
2774 dt
.size
= sb
.st_size
;
2775 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2776 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2777 GNUTLS_X509_FMT_PEM
, 0);
2778 if (certs_read
<= 0) {
2779 show_oops(t
, "No cert(s) available");
2782 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2795 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2798 struct addrinfo hints
, *res
= NULL
, *ai
;
2802 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2805 su
= soup_uri_new(uri
);
2808 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2811 snprintf(port
, sizeof port
, "%d", su
->port
);
2812 bzero(&hints
, sizeof(struct addrinfo
));
2813 hints
.ai_flags
= AI_CANONNAME
;
2814 hints
.ai_family
= AF_UNSPEC
;
2815 hints
.ai_socktype
= SOCK_STREAM
;
2817 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2820 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2821 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2824 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2827 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2831 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2836 strlcpy(domain
, su
->host
, domain_sz
);
2847 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2850 gnutls_deinit(gsession
);
2852 gnutls_certificate_free_credentials(xcred
);
2858 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2859 gnutls_certificate_credentials_t
*xc
)
2861 gnutls_certificate_credentials_t xcred
;
2862 gnutls_session_t gsession
;
2865 if (gs
== NULL
|| xc
== NULL
)
2871 gnutls_certificate_allocate_credentials(&xcred
);
2872 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2873 GNUTLS_X509_FMT_PEM
);
2874 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2875 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2876 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2877 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2878 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2879 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2881 gnutls_error_is_fatal(rv
),
2882 gnutls_strerror_name(rv
));
2883 stop_tls(gsession
, xcred
);
2887 gnutls_credentials_type_t cred
;
2888 cred
= gnutls_auth_get_type(gsession
);
2889 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2890 stop_tls(gsession
, xcred
);
2902 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2906 const gnutls_datum_t
*cl
;
2907 gnutls_x509_crt_t
*all_certs
;
2910 if (certs
== NULL
|| cert_count
== NULL
)
2912 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2914 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2918 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2919 for (i
= 0; i
< len
; i
++) {
2920 gnutls_x509_crt_init(&all_certs
[i
]);
2921 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2922 GNUTLS_X509_FMT_PEM
< 0)) {
2936 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2940 for (i
= 0; i
< cert_count
; i
++)
2941 gnutls_x509_crt_deinit(certs
[i
]);
2946 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2947 size_t cert_count
, char *domain
)
2950 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2955 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2958 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2959 if ((f
= fopen(file
, "w")) == NULL
) {
2960 show_oops(t
, "Can't create cert file %s %s",
2961 file
, strerror(errno
));
2965 for (i
= 0; i
< cert_count
; i
++) {
2966 cert_buf_sz
= sizeof cert_buf
;
2967 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2968 cert_buf
, &cert_buf_sz
)) {
2969 show_oops(t
, "gnutls_x509_crt_export failed");
2972 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2973 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2978 /* not the best spot but oh well */
2979 gdk_color_parse("lightblue", &color
);
2980 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2981 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2982 gdk_color_parse(XT_COLOR_BLACK
, &color
);
2983 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2989 load_compare_cert(struct tab
*t
, struct karg
*args
)
2992 char domain
[8182], file
[PATH_MAX
];
2993 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2994 int s
= -1, rv
= 1, i
;
2998 gnutls_session_t gsession
;
2999 gnutls_x509_crt_t
*certs
;
3000 gnutls_certificate_credentials_t xcred
;
3005 if ((uri
= get_uri(t
->wv
)) == NULL
)
3008 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3012 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3013 show_oops(t
, "Start TLS failed");
3018 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3019 show_oops(t
, "Can't get connection certificates");
3023 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3024 if ((f
= fopen(file
, "r")) == NULL
)
3027 for (i
= 0; i
< cert_count
; i
++) {
3028 cert_buf_sz
= sizeof cert_buf
;
3029 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3030 cert_buf
, &cert_buf_sz
)) {
3033 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3034 rv
= -1; /* critical */
3037 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3038 rv
= -1; /* critical */
3047 free_connection_certs(certs
, cert_count
);
3049 /* we close the socket first for speed */
3052 stop_tls(gsession
, xcred
);
3058 cert_cmd(struct tab
*t
, struct karg
*args
)
3064 gnutls_session_t gsession
;
3065 gnutls_x509_crt_t
*certs
;
3066 gnutls_certificate_credentials_t xcred
;
3071 if ((uri
= get_uri(t
->wv
)) == NULL
) {
3072 show_oops(t
, "Invalid URI");
3076 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3077 show_oops(t
, "Invalid certidicate URI: %s", uri
);
3082 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3083 show_oops(t
, "Start TLS failed");
3088 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3089 show_oops(t
, "get_connection_certs failed");
3093 if (args
->i
& XT_SHOW
)
3094 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3095 else if (args
->i
& XT_SAVE
)
3096 save_certs(t
, certs
, cert_count
, domain
);
3098 free_connection_certs(certs
, cert_count
);
3100 /* we close the socket first for speed */
3103 stop_tls(gsession
, xcred
);
3109 remove_cookie(int index
)
3115 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3117 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3119 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3123 print_cookie("remove cookie", c
);
3124 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3129 soup_cookies_free(cf
);
3135 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3138 char *tmp
, *header
, *body
, *footer
;
3140 /* we set this to indicate we want to manually do navaction */
3141 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3143 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
3145 footer
= g_strdup("</body></html>");
3146 body
= g_strdup("");
3149 if (args
->i
& XT_WL_PERSISTENT
) {
3151 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3153 RB_FOREACH(d
, domain_list
, wl
) {
3157 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3163 if (args
->i
& XT_WL_SESSION
) {
3165 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3167 RB_FOREACH(d
, domain_list
, wl
) {
3171 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3176 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3181 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3183 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3189 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3191 char file
[PATH_MAX
];
3193 char *line
= NULL
, *lt
= NULL
;
3196 char *dom
= NULL
, *dom_save
= NULL
;
3202 if (t
== NULL
|| args
== NULL
)
3205 if (runtime_settings
[0] == '\0')
3208 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3209 if ((f
= fopen(file
, "r+")) == NULL
)
3212 uri
= get_uri(t
->wv
);
3213 dom
= find_domain(uri
, 1);
3214 if (uri
== NULL
|| dom
== NULL
) {
3215 show_oops(t
, "Can't add domain to %s white list",
3216 js
? "JavaScript" : "cookie");
3220 if (args
->i
& XT_WL_TOPLEVEL
) {
3222 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3223 show_oops(t
, "invalid domain: %s", dom
);
3226 } else if (args
->i
& XT_WL_FQDN
) {
3232 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3235 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3238 if (!strcmp(line
, lt
))
3244 fprintf(f
, "%s\n", lt
);
3249 d
= wl_find(dom_save
, &js_wl
);
3251 settings_add("js_wl", dom_save
);
3252 d
= wl_find(dom_save
, &js_wl
);
3256 d
= wl_find(dom_save
, &c_wl
);
3258 settings_add("cookie_wl", dom_save
);
3259 d
= wl_find(dom_save
, &c_wl
);
3263 /* find and add to persistent jar */
3264 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3265 for (;cf
; cf
= cf
->next
) {
3267 if (!strcmp(dom_save
, ci
->domain
) ||
3268 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3269 c
= soup_cookie_copy(ci
);
3270 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3273 soup_cookies_free(cf
);
3291 js_show_wl(struct tab
*t
, struct karg
*args
)
3293 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3294 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3300 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3302 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3303 wl_show(t
, args
, "Cookie White List", &c_wl
);
3309 cookie_cmd(struct tab
*t
, struct karg
*args
)
3311 if (args
->i
& XT_SHOW
)
3312 wl_show(t
, args
, "Cookie White List", &c_wl
);
3313 else if (args
->i
& XT_WL_TOGGLE
)
3314 toggle_cwl(t
, args
);
3315 else if (args
->i
& XT_SAVE
)
3316 wl_save(t
, args
, 0);
3317 else if (args
->i
& XT_DELETE
)
3318 show_oops(t
, "'cookie delete' currently unimplemented");
3324 js_cmd(struct tab
*t
, struct karg
*args
)
3326 if (args
->i
& XT_SHOW
)
3327 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3328 else if (args
->i
& XT_SAVE
)
3329 wl_save(t
, args
, 1);
3330 else if (args
->i
& XT_WL_TOGGLE
)
3332 else if (args
->i
& XT_DELETE
)
3333 show_oops(t
, "'js delete' currently unimplemented");
3339 add_favorite(struct tab
*t
, struct karg
*args
)
3341 char file
[PATH_MAX
];
3344 size_t urilen
, linelen
;
3345 const gchar
*uri
, *title
;
3350 /* don't allow adding of xtp pages to favorites */
3351 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3352 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3356 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3357 if ((f
= fopen(file
, "r+")) == NULL
) {
3358 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3362 title
= webkit_web_view_get_title(t
->wv
);
3363 uri
= get_uri(t
->wv
);
3368 if (title
== NULL
|| uri
== NULL
) {
3369 show_oops(t
, "can't add page to favorites");
3373 urilen
= strlen(uri
);
3376 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3377 if (feof(f
) || ferror(f
))
3380 if (linelen
== urilen
&& !strcmp(line
, uri
))
3387 fprintf(f
, "\n%s\n%s", title
, uri
);
3393 update_favorite_tabs(NULL
);
3399 navaction(struct tab
*t
, struct karg
*args
)
3401 WebKitWebHistoryItem
*item
;
3403 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3404 t
->tab_id
, args
->i
);
3407 if (args
->i
== XT_NAV_BACK
)
3408 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3410 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3412 return (XT_CB_PASSTHROUGH
);
3413 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3415 return (XT_CB_PASSTHROUGH
);
3420 webkit_web_view_go_back(t
->wv
);
3422 case XT_NAV_FORWARD
:
3423 webkit_web_view_go_forward(t
->wv
);
3426 webkit_web_view_reload(t
->wv
);
3428 case XT_NAV_RELOAD_CACHE
:
3429 webkit_web_view_reload_bypass_cache(t
->wv
);
3432 return (XT_CB_PASSTHROUGH
);
3436 move(struct tab
*t
, struct karg
*args
)
3438 GtkAdjustment
*adjust
;
3439 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3444 case XT_MOVE_BOTTOM
:
3446 case XT_MOVE_PAGEDOWN
:
3447 case XT_MOVE_PAGEUP
:
3448 case XT_MOVE_HALFDOWN
:
3449 case XT_MOVE_HALFUP
:
3450 adjust
= t
->adjust_v
;
3453 adjust
= t
->adjust_h
;
3457 pos
= gtk_adjustment_get_value(adjust
);
3458 ps
= gtk_adjustment_get_page_size(adjust
);
3459 upper
= gtk_adjustment_get_upper(adjust
);
3460 lower
= gtk_adjustment_get_lower(adjust
);
3461 si
= gtk_adjustment_get_step_increment(adjust
);
3462 pi
= gtk_adjustment_get_page_increment(adjust
);
3465 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3466 "max %f si %f pi %f\n",
3467 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3468 pos
, ps
, upper
, lower
, max
, si
, pi
);
3474 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3479 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3481 case XT_MOVE_BOTTOM
:
3482 case XT_MOVE_FARRIGHT
:
3483 gtk_adjustment_set_value(adjust
, max
);
3486 case XT_MOVE_FARLEFT
:
3487 gtk_adjustment_set_value(adjust
, lower
);
3489 case XT_MOVE_PAGEDOWN
:
3491 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3493 case XT_MOVE_PAGEUP
:
3495 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3497 case XT_MOVE_HALFDOWN
:
3499 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3501 case XT_MOVE_HALFUP
:
3503 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3506 return (XT_CB_PASSTHROUGH
);
3509 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3511 return (XT_CB_HANDLED
);
3515 url_set_visibility(void)
3519 TAILQ_FOREACH(t
, &tabs
, entry
) {
3520 if (show_url
== 0) {
3521 gtk_widget_hide(t
->toolbar
);
3524 gtk_widget_show(t
->toolbar
);
3529 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3532 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3534 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3538 statusbar_set_visibility(void)
3542 TAILQ_FOREACH(t
, &tabs
, entry
) {
3543 if (show_statusbar
== 0) {
3544 gtk_widget_hide(t
->statusbar
);
3547 gtk_widget_show(t
->statusbar
);
3552 url_set(struct tab
*t
, int enable_url_entry
)
3557 show_url
= enable_url_entry
;
3559 if (enable_url_entry
) {
3560 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3561 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3562 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3564 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3565 GTK_ENTRY_ICON_PRIMARY
);
3567 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3568 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3569 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3570 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3576 fullscreen(struct tab
*t
, struct karg
*args
)
3578 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3581 return (XT_CB_PASSTHROUGH
);
3583 if (show_url
== 0) {
3591 url_set_visibility();
3592 notebook_tab_set_visibility(notebook
);
3594 return (XT_CB_HANDLED
);
3598 statusaction(struct tab
*t
, struct karg
*args
)
3600 int rv
= XT_CB_HANDLED
;
3602 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3605 return (XT_CB_PASSTHROUGH
);
3608 case XT_STATUSBAR_SHOW
:
3609 if (show_statusbar
== 0) {
3611 statusbar_set_visibility();
3614 case XT_STATUSBAR_HIDE
:
3615 if (show_statusbar
== 1) {
3617 statusbar_set_visibility();
3625 urlaction(struct tab
*t
, struct karg
*args
)
3627 int rv
= XT_CB_HANDLED
;
3629 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3632 return (XT_CB_PASSTHROUGH
);
3636 if (show_url
== 0) {
3638 url_set_visibility();
3642 if (show_url
== 1) {
3644 url_set_visibility();
3652 tabaction(struct tab
*t
, struct karg
*args
)
3654 int rv
= XT_CB_HANDLED
;
3655 char *url
= args
->s
;
3658 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3661 return (XT_CB_PASSTHROUGH
);
3665 if (strlen(url
) > 0)
3666 create_new_tab(url
, NULL
, 1);
3668 create_new_tab(NULL
, NULL
, 1);
3673 case XT_TAB_DELQUIT
:
3674 if (gtk_notebook_get_n_pages(notebook
) > 1)
3680 if (strlen(url
) > 0)
3683 rv
= XT_CB_PASSTHROUGH
;
3689 if (show_tabs
== 0) {
3691 notebook_tab_set_visibility(notebook
);
3695 if (show_tabs
== 1) {
3697 notebook_tab_set_visibility(notebook
);
3700 case XT_TAB_UNDO_CLOSE
:
3701 if (undo_count
== 0) {
3702 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3706 u
= TAILQ_FIRST(&undos
);
3707 create_new_tab(u
->uri
, u
, 1);
3709 TAILQ_REMOVE(&undos
, u
, entry
);
3711 /* u->history is freed in create_new_tab() */
3716 rv
= XT_CB_PASSTHROUGH
;
3730 resizetab(struct tab
*t
, struct karg
*args
)
3732 if (t
== NULL
|| args
== NULL
) {
3733 show_oops_s("resizetab invalid parameters");
3734 return (XT_CB_PASSTHROUGH
);
3737 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3738 t
->tab_id
, args
->i
);
3740 adjustfont_webkit(t
, args
->i
);
3742 return (XT_CB_HANDLED
);
3746 movetab(struct tab
*t
, struct karg
*args
)
3751 if (t
== NULL
|| args
== NULL
) {
3752 show_oops_s("movetab invalid parameters");
3753 return (XT_CB_PASSTHROUGH
);
3756 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3757 t
->tab_id
, args
->i
);
3759 if (args
->i
== XT_TAB_INVALID
)
3760 return (XT_CB_PASSTHROUGH
);
3762 if (args
->i
< XT_TAB_INVALID
) {
3763 /* next or previous tab */
3764 if (TAILQ_EMPTY(&tabs
))
3765 return (XT_CB_PASSTHROUGH
);
3769 /* if at the last page, loop around to the first */
3770 if (gtk_notebook_get_current_page(notebook
) ==
3771 gtk_notebook_get_n_pages(notebook
) - 1)
3772 gtk_notebook_set_current_page(notebook
, 0);
3774 gtk_notebook_next_page(notebook
);
3777 /* if at the first page, loop around to the last */
3778 if (gtk_notebook_current_page(notebook
) == 0)
3779 gtk_notebook_set_current_page(notebook
,
3780 gtk_notebook_get_n_pages(notebook
) - 1);
3782 gtk_notebook_prev_page(notebook
);
3785 gtk_notebook_set_current_page(notebook
, 0);
3788 gtk_notebook_set_current_page(notebook
, -1);
3791 return (XT_CB_PASSTHROUGH
);
3794 return (XT_CB_HANDLED
);
3799 if (t
->tab_id
== x
) {
3800 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3801 return (XT_CB_HANDLED
);
3804 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3805 if (tt
->tab_id
== x
) {
3806 gtk_notebook_set_current_page(notebook
, x
);
3807 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3813 return (XT_CB_HANDLED
);
3817 command(struct tab
*t
, struct karg
*args
)
3819 char *s
= NULL
, *ss
= NULL
;
3823 if (t
== NULL
|| args
== NULL
) {
3824 show_oops_s("command invalid parameters");
3825 return (XT_CB_PASSTHROUGH
);
3844 case XT_CMD_OPEN_CURRENT
:
3847 case XT_CMD_TABNEW_CURRENT
:
3848 if (!s
) /* FALL THROUGH? */
3850 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3851 ss
= g_strdup_printf("%s%s", s
, uri
);
3856 show_oops(t
, "command: invalid opcode %d", args
->i
);
3857 return (XT_CB_PASSTHROUGH
);
3860 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3862 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3863 gdk_color_parse(XT_COLOR_WHITE
, &color
);
3864 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3866 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3867 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3872 return (XT_CB_HANDLED
);
3876 * Return a new string with a download row (in html)
3877 * appended. Old string is freed.
3880 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3883 WebKitDownloadStatus stat
;
3884 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3886 char cur_sz
[FMT_SCALED_STRSIZE
];
3887 char tot_sz
[FMT_SCALED_STRSIZE
];
3890 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3892 /* All actions wil take this form:
3893 * xxxt://class/seskey
3895 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3896 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3898 stat
= webkit_download_get_status(dl
->download
);
3901 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3902 status_html
= g_strdup_printf("Finished");
3903 cmd_html
= g_strdup_printf(
3904 "<a href='%s%d/%d'>Remove</a>",
3905 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3907 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3908 /* gather size info */
3909 progress
= 100 * webkit_download_get_progress(dl
->download
);
3912 webkit_download_get_current_size(dl
->download
), cur_sz
);
3914 webkit_download_get_total_size(dl
->download
), tot_sz
);
3916 status_html
= g_strdup_printf(
3917 "<div style='width: 100%%' align='center'>"
3918 "<div class='progress-outer'>"
3919 "<div class='progress-inner' style='width: %.2f%%'>"
3920 "</div></div></div>"
3921 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3922 progress
, cur_sz
, tot_sz
, progress
);
3924 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3925 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3929 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3930 status_html
= g_strdup_printf("Cancelled");
3931 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3932 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3934 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3935 status_html
= g_strdup_printf("Error!");
3936 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3937 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3939 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3940 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3941 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3942 status_html
= g_strdup_printf("Starting");
3945 show_oops(t
, "%s: unknown download status", __func__
);
3948 new_html
= g_strdup_printf(
3949 "%s\n<tr><td>%s</td><td>%s</td>"
3950 "<td style='text-align:center'>%s</td></tr>\n",
3951 html
, basename(webkit_download_get_destination_uri(dl
->download
)),
3952 status_html
, cmd_html
);
3956 g_free(status_html
);
3967 * update all download tabs apart from one. Pass NULL if
3968 * you want to update all.
3971 update_download_tabs(struct tab
*apart_from
)
3974 if (!updating_dl_tabs
) {
3975 updating_dl_tabs
= 1; /* stop infinite recursion */
3976 TAILQ_FOREACH(t
, &tabs
, entry
)
3977 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3978 && (t
!= apart_from
))
3979 xtp_page_dl(t
, NULL
);
3980 updating_dl_tabs
= 0;
3985 * update all cookie tabs apart from one. Pass NULL if
3986 * you want to update all.
3989 update_cookie_tabs(struct tab
*apart_from
)
3992 if (!updating_cl_tabs
) {
3993 updating_cl_tabs
= 1; /* stop infinite recursion */
3994 TAILQ_FOREACH(t
, &tabs
, entry
)
3995 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3996 && (t
!= apart_from
))
3997 xtp_page_cl(t
, NULL
);
3998 updating_cl_tabs
= 0;
4003 * update all history tabs apart from one. Pass NULL if
4004 * you want to update all.
4007 update_history_tabs(struct tab
*apart_from
)
4011 if (!updating_hl_tabs
) {
4012 updating_hl_tabs
= 1; /* stop infinite recursion */
4013 TAILQ_FOREACH(t
, &tabs
, entry
)
4014 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4015 && (t
!= apart_from
))
4016 xtp_page_hl(t
, NULL
);
4017 updating_hl_tabs
= 0;
4021 /* cookie management XTP page */
4023 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4025 char *header
, *body
, *footer
, *page
, *tmp
;
4026 int i
= 1; /* all ids start 1 */
4027 GSList
*sc
, *pc
, *pc_start
;
4029 char *type
, *table_headers
;
4030 char *last_domain
= strdup("");
4032 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4035 show_oops_s("%s invalid parameters", __func__
);
4038 /* mark this tab as cookie jar */
4039 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
4041 /* Generate a new session key */
4042 if (!updating_cl_tabs
)
4043 generate_xtp_session_key(&cl_session_key
);
4046 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4047 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
4048 "</head><body><h1>Cookie Jar</h1>\n");
4051 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
4058 "<th>HTTP<br />only</th>"
4059 "<th>Rm</th></tr>\n");
4061 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4062 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4066 for (; sc
; sc
= sc
->next
) {
4069 if (strcmp(last_domain
, c
->domain
) != 0) {
4072 last_domain
= strdup(c
->domain
);
4076 body
= g_strdup_printf("%s</table></div>"
4078 body
, c
->domain
, table_headers
);
4082 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4083 c
->domain
, table_headers
);
4088 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4089 if (soup_cookie_equal(pc
->data
, c
)) {
4090 type
= "Session + Persistent";
4095 body
= g_strdup_printf(
4097 "<td style='width: text-align: center'>%s</td>"
4098 "<td style='width: 1px'>%s</td>"
4099 "<td style='width=70%%;overflow: visible'>"
4100 " <textarea rows='4'>%s</textarea>"
4104 "<td style='width: 1px; text-align: center'>%d</td>"
4105 "<td style='width: 1px; text-align: center'>%d</td>"
4106 "<td style='width: 1px; text-align: center'>"
4107 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4114 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4129 soup_cookies_free(sc
);
4130 soup_cookies_free(pc
);
4132 /* small message if there are none */
4134 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4135 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4139 footer
= g_strdup_printf("</table></div></body></html>");
4141 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4146 g_free(table_headers
);
4147 g_free(last_domain
);
4149 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4150 update_cookie_tabs(t
);
4158 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4160 char *header
, *body
, *footer
, *page
, *tmp
;
4162 int i
= 1; /* all ids start 1 */
4164 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4167 show_oops_s("%s invalid parameters", __func__
);
4171 /* mark this tab as history manager */
4172 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4174 /* Generate a new session key */
4175 if (!updating_hl_tabs
)
4176 generate_xtp_session_key(&hl_session_key
);
4179 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4180 "<title>History</title>\n"
4183 "<h1>History</h1>\n",
4187 body
= g_strdup_printf("<div align='center'><table><tr>"
4188 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4190 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4192 body
= g_strdup_printf(
4194 "<td><a href='%s'>%s</a></td>"
4196 "<td style='text-align: center'>"
4197 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4198 body
, h
->uri
, h
->uri
, h
->title
,
4199 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4200 XT_XTP_HL_REMOVE
, i
);
4206 /* small message if there are none */
4209 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4210 "colspan='3'>No History</td></tr>\n", body
);
4215 footer
= g_strdup_printf("</table></div></body></html>");
4217 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4220 * update all history manager tabs as the xtp session
4221 * key has now changed. No need to update the current tab.
4222 * Already did that above.
4224 update_history_tabs(t
);
4230 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4237 * Generate a web page detailing the status of any downloads
4240 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4242 struct download
*dl
;
4243 char *header
, *body
, *footer
, *page
, *tmp
;
4247 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4250 show_oops_s("%s invalid parameters", __func__
);
4253 /* mark as a download manager tab */
4254 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4257 * Generate a new session key for next page instance.
4258 * This only happens for the top level call to xtp_page_dl()
4259 * in which case updating_dl_tabs is 0.
4261 if (!updating_dl_tabs
)
4262 generate_xtp_session_key(&dl_session_key
);
4264 /* header - with refresh so as to update */
4265 if (refresh_interval
>= 1)
4266 ref
= g_strdup_printf(
4267 "<meta http-equiv='refresh' content='%u"
4268 ";url=%s%d/%s/%d' />\n",
4278 header
= g_strdup_printf(
4280 "<title>Downloads</title>\n%s%s</head>\n",
4281 XT_DOCTYPE XT_HTML_TAG
,
4285 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4286 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4287 "</p><table><tr><th style='width: 60%%'>"
4288 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4289 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4291 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4292 body
= xtp_page_dl_row(t
, body
, dl
);
4296 /* message if no downloads in list */
4299 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4300 " style='text-align: center'>"
4301 "No downloads</td></tr>\n", body
);
4306 footer
= g_strdup_printf("</table></div></body></html>");
4308 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4312 * update all download manager tabs as the xtp session
4313 * key has now changed. No need to update the current tab.
4314 * Already did that above.
4316 update_download_tabs(t
);
4323 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4330 search(struct tab
*t
, struct karg
*args
)
4334 if (t
== NULL
|| args
== NULL
) {
4335 show_oops_s("search invalid parameters");
4338 if (t
->search_text
== NULL
) {
4339 if (global_search
== NULL
)
4340 return (XT_CB_PASSTHROUGH
);
4342 t
->search_text
= g_strdup(global_search
);
4343 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4344 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4348 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4349 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4352 case XT_SEARCH_NEXT
:
4353 d
= t
->search_forward
;
4355 case XT_SEARCH_PREV
:
4356 d
= !t
->search_forward
;
4359 return (XT_CB_PASSTHROUGH
);
4362 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4364 return (XT_CB_HANDLED
);
4367 struct settings_args
{
4373 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4376 struct settings_args
*sa
= cb_args
;
4381 if (s
->flags
& XT_SF_RUNTIME
)
4387 *sa
->body
= g_strdup_printf(
4389 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4390 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4402 set(struct tab
*t
, struct karg
*args
)
4404 char *header
, *body
, *footer
, *page
, *tmp
;
4406 struct settings_args sa
;
4408 bzero(&sa
, sizeof sa
);
4412 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4413 "\n<head><title>Settings</title>\n"
4414 "</head><body><h1>Settings</h1>\n");
4417 body
= g_strdup_printf("<div align='center'><table><tr>"
4418 "<th align='left'>Setting</th>"
4419 "<th align='left'>Value</th></tr>\n");
4421 settings_walk(print_setting
, &sa
);
4424 /* small message if there are none */
4427 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4428 "colspan='2'>No settings</td></tr>\n", body
);
4433 footer
= g_strdup_printf("</table></div></body></html>");
4435 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4441 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4443 return (XT_CB_PASSTHROUGH
);
4447 session_save(struct tab
*t
, char *filename
)
4452 if (strlen(filename
) == 0)
4455 if (filename
[0] == '.' || filename
[0] == '/')
4459 if (save_tabs(t
, &a
))
4461 strlcpy(named_session
, filename
, sizeof named_session
);
4469 session_open(struct tab
*t
, char *filename
)
4474 if (strlen(filename
) == 0)
4477 if (filename
[0] == '.' || filename
[0] == '/')
4481 a
.i
= XT_SES_CLOSETABS
;
4482 if (open_tabs(t
, &a
))
4485 strlcpy(named_session
, filename
, sizeof named_session
);
4493 session_delete(struct tab
*t
, char *filename
)
4495 char file
[PATH_MAX
];
4498 if (strlen(filename
) == 0)
4501 if (filename
[0] == '.' || filename
[0] == '/')
4504 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4508 if (!strcmp(filename
, named_session
))
4509 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4510 sizeof named_session
);
4518 session_cmd(struct tab
*t
, struct karg
*args
)
4520 char *filename
= args
->s
;
4525 if (args
->i
& XT_SHOW
)
4526 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4527 XT_SAVED_TABS_FILE
: named_session
);
4528 else if (args
->i
& XT_SAVE
) {
4529 if (session_save(t
, filename
)) {
4530 show_oops(t
, "Can't save session: %s",
4531 filename
? filename
: "INVALID");
4534 } else if (args
->i
& XT_OPEN
) {
4535 if (session_open(t
, filename
)) {
4536 show_oops(t
, "Can't open session: %s",
4537 filename
? filename
: "INVALID");
4540 } else if (args
->i
& XT_DELETE
) {
4541 if (session_delete(t
, filename
)) {
4542 show_oops(t
, "Can't delete session: %s",
4543 filename
? filename
: "INVALID");
4548 return (XT_CB_PASSTHROUGH
);
4552 * Make a hardcopy of the page
4555 print_page(struct tab
*t
, struct karg
*args
)
4557 WebKitWebFrame
*frame
;
4559 GtkPrintOperation
*op
;
4560 GtkPrintOperationAction action
;
4561 GtkPrintOperationResult print_res
;
4562 GError
*g_err
= NULL
;
4563 int marg_l
, marg_r
, marg_t
, marg_b
;
4565 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4567 ps
= gtk_page_setup_new();
4568 op
= gtk_print_operation_new();
4569 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4570 frame
= webkit_web_view_get_main_frame(t
->wv
);
4572 /* the default margins are too small, so we will bump them */
4573 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4574 XT_PRINT_EXTRA_MARGIN
;
4575 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4576 XT_PRINT_EXTRA_MARGIN
;
4577 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4578 XT_PRINT_EXTRA_MARGIN
;
4579 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4580 XT_PRINT_EXTRA_MARGIN
;
4583 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4584 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4585 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4586 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4588 gtk_print_operation_set_default_page_setup(op
, ps
);
4590 /* this appears to free 'op' and 'ps' */
4591 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4593 /* check it worked */
4594 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4595 show_oops_s("can't print: %s", g_err
->message
);
4596 g_error_free (g_err
);
4604 go_home(struct tab
*t
, struct karg
*args
)
4611 restart(struct tab
*t
, struct karg
*args
)
4615 a
.s
= XT_RESTART_TABS_FILE
;
4617 execvp(start_argv
[0], start_argv
);
4623 #define CTRL GDK_CONTROL_MASK
4624 #define MOD1 GDK_MOD1_MASK
4625 #define SHFT GDK_SHIFT_MASK
4627 /* inherent to GTK not all keys will be caught at all times */
4628 /* XXX sort key bindings */
4629 struct key_binding
{
4634 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4636 { "cookiejar", MOD1
, 0, GDK_j
},
4637 { "downloadmgr", MOD1
, 0, GDK_d
},
4638 { "history", MOD1
, 0, GDK_h
},
4639 { "print", CTRL
, 0, GDK_p
},
4640 { "search", 0, 0, GDK_slash
},
4641 { "searchb", 0, 0, GDK_question
},
4642 { "command", 0, 0, GDK_colon
},
4643 { "quit", CTRL
, 0, GDK_q
},
4644 { "restart", MOD1
, 0, GDK_q
},
4645 { "js toggle", CTRL
, 0, GDK_j
},
4646 { "cookie toggle", MOD1
, 0, GDK_c
},
4647 { "togglesrc", CTRL
, 0, GDK_s
},
4648 { "yankuri", 0, 0, GDK_y
},
4649 { "pasteuricur", 0, 0, GDK_p
},
4650 { "pasteurinew", 0, 0, GDK_P
},
4653 { "searchnext", 0, 0, GDK_n
},
4654 { "searchprevious", 0, 0, GDK_N
},
4657 { "focusaddress", 0, 0, GDK_F6
},
4658 { "focussearch", 0, 0, GDK_F7
},
4661 { "hinting", 0, 0, GDK_f
},
4663 /* custom stylesheet */
4664 { "userstyle", 0, 0, GDK_i
},
4667 { "goback", 0, 0, GDK_BackSpace
},
4668 { "goback", MOD1
, 0, GDK_Left
},
4669 { "goforward", SHFT
, 0, GDK_BackSpace
},
4670 { "goforward", MOD1
, 0, GDK_Right
},
4671 { "reload", 0, 0, GDK_F5
},
4672 { "reload", CTRL
, 0, GDK_r
},
4673 { "reloadforce", CTRL
, 0, GDK_R
},
4674 { "reload", CTRL
, 0, GDK_l
},
4675 { "favorites", MOD1
, 1, GDK_f
},
4677 /* vertical movement */
4678 { "scrolldown", 0, 0, GDK_j
},
4679 { "scrolldown", 0, 0, GDK_Down
},
4680 { "scrollup", 0, 0, GDK_Up
},
4681 { "scrollup", 0, 0, GDK_k
},
4682 { "scrollbottom", 0, 0, GDK_G
},
4683 { "scrollbottom", 0, 0, GDK_End
},
4684 { "scrolltop", 0, 0, GDK_Home
},
4685 { "scrolltop", 0, 0, GDK_g
},
4686 { "scrollpagedown", 0, 0, GDK_space
},
4687 { "scrollpagedown", CTRL
, 0, GDK_f
},
4688 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4689 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4690 { "scrollpageup", 0, 0, GDK_Page_Up
},
4691 { "scrollpageup", CTRL
, 0, GDK_b
},
4692 { "scrollhalfup", CTRL
, 0, GDK_u
},
4693 /* horizontal movement */
4694 { "scrollright", 0, 0, GDK_l
},
4695 { "scrollright", 0, 0, GDK_Right
},
4696 { "scrollleft", 0, 0, GDK_Left
},
4697 { "scrollleft", 0, 0, GDK_h
},
4698 { "scrollfarright", 0, 0, GDK_dollar
},
4699 { "scrollfarleft", 0, 0, GDK_0
},
4702 { "tabnew", CTRL
, 0, GDK_t
},
4703 { "tabclose", CTRL
, 1, GDK_w
},
4704 { "tabundoclose", 0, 0, GDK_U
},
4705 { "tabgoto1", CTRL
, 0, GDK_1
},
4706 { "tabgoto2", CTRL
, 0, GDK_2
},
4707 { "tabgoto3", CTRL
, 0, GDK_3
},
4708 { "tabgoto4", CTRL
, 0, GDK_4
},
4709 { "tabgoto5", CTRL
, 0, GDK_5
},
4710 { "tabgoto6", CTRL
, 0, GDK_6
},
4711 { "tabgoto7", CTRL
, 0, GDK_7
},
4712 { "tabgoto8", CTRL
, 0, GDK_8
},
4713 { "tabgoto9", CTRL
, 0, GDK_9
},
4714 { "tabgoto10", CTRL
, 0, GDK_0
},
4715 { "tabfirst", CTRL
, 0, GDK_less
},
4716 { "tablast", CTRL
, 0, GDK_greater
},
4717 { "tabprevious", CTRL
, 0, GDK_Left
},
4718 { "tabnext", CTRL
, 0, GDK_Right
},
4719 { "focusout", CTRL
, 0, GDK_minus
},
4720 { "focusin", CTRL
, 0, GDK_plus
},
4721 { "focusin", CTRL
, 0, GDK_equal
},
4723 /* command aliases (handy when -S flag is used) */
4724 { "promptopen", 0, 0, GDK_F9
},
4725 { "promptopencurrent", 0, 0, GDK_F10
},
4726 { "prompttabnew", 0, 0, GDK_F11
},
4727 { "prompttabnewcurrent",0, 0, GDK_F12
},
4729 TAILQ_HEAD(keybinding_list
, key_binding
);
4732 walk_kb(struct settings
*s
,
4733 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4735 struct key_binding
*k
;
4738 if (s
== NULL
|| cb
== NULL
) {
4739 show_oops_s("walk_kb invalid parameters");
4743 TAILQ_FOREACH(k
, &kbl
, entry
) {
4749 if (gdk_keyval_name(k
->key
) == NULL
)
4752 strlcat(str
, k
->cmd
, sizeof str
);
4753 strlcat(str
, ",", sizeof str
);
4755 if (k
->mask
& GDK_SHIFT_MASK
)
4756 strlcat(str
, "S-", sizeof str
);
4757 if (k
->mask
& GDK_CONTROL_MASK
)
4758 strlcat(str
, "C-", sizeof str
);
4759 if (k
->mask
& GDK_MOD1_MASK
)
4760 strlcat(str
, "M1-", sizeof str
);
4761 if (k
->mask
& GDK_MOD2_MASK
)
4762 strlcat(str
, "M2-", sizeof str
);
4763 if (k
->mask
& GDK_MOD3_MASK
)
4764 strlcat(str
, "M3-", sizeof str
);
4765 if (k
->mask
& GDK_MOD4_MASK
)
4766 strlcat(str
, "M4-", sizeof str
);
4767 if (k
->mask
& GDK_MOD5_MASK
)
4768 strlcat(str
, "M5-", sizeof str
);
4770 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4771 cb(s
, str
, cb_args
);
4775 init_keybindings(void)
4778 struct key_binding
*k
;
4780 for (i
= 0; i
< LENGTH(keys
); i
++) {
4781 k
= g_malloc0(sizeof *k
);
4782 k
->cmd
= keys
[i
].cmd
;
4783 k
->mask
= keys
[i
].mask
;
4784 k
->use_in_entry
= keys
[i
].use_in_entry
;
4785 k
->key
= keys
[i
].key
;
4786 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4788 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4789 k
->cmd
? k
->cmd
: "unnamed key");
4794 keybinding_clearall(void)
4796 struct key_binding
*k
, *next
;
4798 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4799 next
= TAILQ_NEXT(k
, entry
);
4803 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4804 k
->cmd
? k
->cmd
: "unnamed key");
4805 TAILQ_REMOVE(&kbl
, k
, entry
);
4811 keybinding_add(char *kb
, char *value
, struct key_binding
*orig
)
4813 struct key_binding
*k
;
4814 guint keyval
, mask
= 0;
4817 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s %s\n", kb
, value
, orig
->cmd
);
4821 if (strcmp(kb
, orig
->cmd
))
4824 /* find modifier keys */
4825 if (strstr(value
, "S-"))
4826 mask
|= GDK_SHIFT_MASK
;
4827 if (strstr(value
, "C-"))
4828 mask
|= GDK_CONTROL_MASK
;
4829 if (strstr(value
, "M1-"))
4830 mask
|= GDK_MOD1_MASK
;
4831 if (strstr(value
, "M2-"))
4832 mask
|= GDK_MOD2_MASK
;
4833 if (strstr(value
, "M3-"))
4834 mask
|= GDK_MOD3_MASK
;
4835 if (strstr(value
, "M4-"))
4836 mask
|= GDK_MOD4_MASK
;
4837 if (strstr(value
, "M5-"))
4838 mask
|= GDK_MOD5_MASK
;
4841 for (i
= strlen(value
) - 1; i
> 0; i
--)
4842 if (value
[i
] == '-')
4843 value
= &value
[i
+ 1];
4845 /* validate keyname */
4846 keyval
= gdk_keyval_from_name(value
);
4847 if (keyval
== GDK_VoidSymbol
) {
4848 warnx("invalid keybinding name %s", value
);
4851 /* must run this test too, gtk+ doesn't handle 10 for example */
4852 if (gdk_keyval_name(keyval
) == NULL
) {
4853 warnx("invalid keybinding name %s", value
);
4857 /* make sure it isn't a dupe */
4858 TAILQ_FOREACH(k
, &kbl
, entry
)
4859 if (k
->key
== keyval
&& k
->mask
== mask
) {
4860 warnx("duplicate keybinding for %s", value
);
4865 k
= g_malloc0(sizeof *k
);
4868 k
->use_in_entry
= orig
->use_in_entry
;
4871 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4876 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4877 k
->name
, gdk_keyval_name(keyval
));
4879 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4885 add_kb(struct settings
*s
, char *entry
)
4890 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4892 /* clearall is special */
4893 if (!strcmp(entry
, "clearall")) {
4894 keybinding_clearall();
4898 kb
= strstr(entry
, ",");
4904 /* make sure it is a valid keybinding */
4905 for (i
= 0; i
< LENGTH(keys
); i
++)
4906 if (keys
[i
].cmd
&& !strcmp(entry
, keys
[i
].cmd
)) {
4907 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s 0x%x %d 0x%x\n",
4910 keys
[i
].use_in_entry
,
4913 return (keybinding_add(entry
, value
, &keys
[i
]));
4922 int (*func
)(struct tab
*, struct karg
*);
4924 bool userarg
; /* allow free text arg */
4926 { "command", 0, command
, {.i
= ':'}, FALSE
},
4927 { "search", 0, command
, {.i
= '/'}, FALSE
},
4928 { "searchb", 0, command
, {.i
= '?'}, FALSE
},
4929 { "togglesrc", 0, toggle_src
, {0}, FALSE
},
4931 /* yanking and pasting */
4932 { "yankuri", 0, yank_uri
, {0}, FALSE
},
4933 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
4934 { "pasteuricur", 0, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
}, FALSE
},
4935 { "pasteurinew", 0, paste_uri
, {.i
= XT_PASTE_NEW_TAB
}, FALSE
},
4938 { "searchnext", 0, search
, {.i
= XT_SEARCH_NEXT
}, FALSE
},
4939 { "searchprevious", 0, search
, {.i
= XT_SEARCH_PREV
}, FALSE
},
4940 { "searchprev", 0, search
, {.i
= XT_SEARCH_PREV
}, FALSE
},
4943 { "focusaddress", 0, focus
, {.i
= XT_FOCUS_URI
}, FALSE
},
4944 { "focussearch", 0, focus
, {.i
= XT_FOCUS_SEARCH
}, FALSE
},
4947 { "hinting", 0, hint
, {.i
= 0}, FALSE
},
4949 /* custom stylesheet */
4950 { "userstyle", 0, userstyle
, {.i
= 0 }, FALSE
},
4953 { "goback", 0, navaction
, {.i
= XT_NAV_BACK
}, FALSE
},
4954 { "goforward", 0, navaction
, {.i
= XT_NAV_FORWARD
}, FALSE
},
4955 { "reload", 0, navaction
, {.i
= XT_NAV_RELOAD
}, FALSE
},
4956 { "reloadforce", 0, navaction
, {.i
= XT_NAV_RELOAD_CACHE
}, FALSE
},
4958 /* vertical movement */
4959 { "scrolldown", 0, move
, {.i
= XT_MOVE_DOWN
}, FALSE
},
4960 { "scrollup", 0, move
, {.i
= XT_MOVE_UP
}, FALSE
},
4961 { "scrollbottom", 0, move
, {.i
= XT_MOVE_BOTTOM
}, FALSE
},
4962 { "scrolltop", 0, move
, {.i
= XT_MOVE_TOP
}, FALSE
},
4963 { "1", 0, move
, {.i
= XT_MOVE_TOP
}, FALSE
},
4964 { "scrollhalfdown", 0, move
, {.i
= XT_MOVE_HALFDOWN
},FALSE
},
4965 { "scrollhalfup", 0, move
, {.i
= XT_MOVE_HALFUP
}, FALSE
},
4966 { "scrollpagedown", 0, move
, {.i
= XT_MOVE_PAGEDOWN
},FALSE
},
4967 { "scrollpageup", 0, move
, {.i
= XT_MOVE_PAGEUP
}, FALSE
},
4968 /* horizontal movement */
4969 { "scrollright", 0, move
, {.i
= XT_MOVE_RIGHT
}, FALSE
},
4970 { "scrollleft", 0, move
, {.i
= XT_MOVE_LEFT
}, FALSE
},
4971 { "scrollfarright", 0, move
, {.i
= XT_MOVE_FARRIGHT
},FALSE
},
4972 { "scrollfarleft", 0, move
, {.i
= XT_MOVE_FARLEFT
}, FALSE
},
4975 { "favorites", 0, xtp_page_fl
, {0}, FALSE
},
4976 { "fav", 0, xtp_page_fl
, {0}, FALSE
},
4977 { "favadd", 0, add_favorite
, {0}, FALSE
},
4979 { "quit", 0, quit
, {0}, FALSE
},
4980 { "q!", 0, quit
, {0}, FALSE
},
4981 { "qa", 0, quit
, {0}, FALSE
},
4982 { "qa!", 0, quit
, {0}, FALSE
},
4983 { "w", 0, save_tabs
, {0}, FALSE
},
4984 { "wq", 0, save_tabs_and_quit
, {0}, FALSE
},
4985 { "wq!", 0, save_tabs_and_quit
, {0}, FALSE
},
4986 { "help", 0, help
, {0}, FALSE
},
4987 { "about", 0, about
, {0}, FALSE
},
4988 { "stats", 0, stats
, {0}, FALSE
},
4989 { "version", 0, about
, {0}, FALSE
},
4990 { "cookiejar", 0, xtp_page_cl
, {0}, FALSE
},
4993 { "js", 0, js_cmd
, {.i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
}, FALSE
},
4994 { "save", 1, js_cmd
, {.i
= XT_SAVE
| XT_WL_FQDN
}, FALSE
},
4995 { "domain", 2, js_cmd
, {.i
= XT_SAVE
| XT_WL_TOPLEVEL
}, FALSE
},
4996 { "fqdn", 2, js_cmd
, {.i
= XT_SAVE
| XT_WL_FQDN
}, FALSE
},
4997 { "show", 1, js_cmd
, {.i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
}, FALSE
},
4998 { "all", 2, js_cmd
, {.i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
}, FALSE
},
4999 { "persistent", 2, js_cmd
, {.i
= XT_SHOW
| XT_WL_PERSISTENT
}, FALSE
},
5000 { "session", 2, js_cmd
, {.i
= XT_SHOW
| XT_WL_SESSION
}, FALSE
},
5001 { "toggle", 1, js_cmd
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
}, FALSE
},
5002 { "domain", 2, js_cmd
, {.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
}, FALSE
},
5003 { "fqdn", 2, js_cmd
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
}, FALSE
},
5005 /* cookie command */
5006 { "cookie", 0, cookie_cmd
, {0}, FALSE
},
5007 { "save", 1, cookie_cmd
, {.i
= XT_SAVE
| XT_WL_FQDN
}, FALSE
},
5008 { "domain", 2, cookie_cmd
, {.i
= XT_SAVE
| XT_WL_TOPLEVEL
}, FALSE
},
5009 { "fqdn", 2, cookie_cmd
, {.i
= XT_SAVE
| XT_WL_FQDN
}, FALSE
},
5010 { "show", 1, cookie_cmd
, {.i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
}, FALSE
},
5011 { "all", 2, cookie_cmd
, {.i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
}, FALSE
},
5012 { "persistent", 2, cookie_cmd
, {.i
= XT_SHOW
| XT_WL_PERSISTENT
}, FALSE
},
5013 { "session", 2, cookie_cmd
, {.i
= XT_SHOW
| XT_WL_SESSION
}, FALSE
},
5014 { "toggle", 1, cookie_cmd
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
}, FALSE
},
5015 { "domain", 2, cookie_cmd
, {.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
}, FALSE
},
5016 { "fqdn", 2, cookie_cmd
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
}, FALSE
},
5019 { "cert", 0, cert_cmd
, {.i
= XT_SHOW
}, FALSE
},
5020 { "save", 1, cert_cmd
, {.i
= XT_SAVE
}, FALSE
},
5021 { "show", 1, cert_cmd
, {.i
= XT_SHOW
}, FALSE
},
5023 { "ca", 0, ca_cmd
, {0}, FALSE
},
5024 { "downloadmgr", 0, xtp_page_dl
, {0}, FALSE
},
5025 { "dl", 0, xtp_page_dl
, {0}, FALSE
},
5026 { "h", 0, xtp_page_hl
, {0}, FALSE
},
5027 { "hist", 0, xtp_page_hl
, {0}, FALSE
},
5028 { "history", 0, xtp_page_hl
, {0}, FALSE
},
5029 { "home", 0, go_home
, {0}, FALSE
},
5030 { "restart", 0, restart
, {0}, FALSE
},
5031 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
}, FALSE
},
5032 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
}, FALSE
},
5033 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
}, FALSE
},
5034 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
}, FALSE
},
5035 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
}, FALSE
},
5036 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
}, FALSE
},
5037 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
}, FALSE
},
5038 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
}, FALSE
},
5040 { "print", 0, print_page
, {0}, FALSE
},
5043 { "o", 0, tabaction
, {.i
= XT_TAB_OPEN
}, TRUE
},
5044 { "op", 0, tabaction
, {.i
= XT_TAB_OPEN
}, TRUE
},
5045 { "open", 0, tabaction
, {.i
= XT_TAB_OPEN
}, TRUE
},
5046 { "tabnew", 0, tabaction
, {.i
= XT_TAB_NEW
}, TRUE
},
5047 { "tabedit", 0, tabaction
, {.i
= XT_TAB_NEW
}, TRUE
},
5048 { "tabe", 0, tabaction
, {.i
= XT_TAB_NEW
}, TRUE
},
5049 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
}, FALSE
},
5050 { "tabundoclose", 0, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
5051 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
}, FALSE
},
5052 { "tabshow", 0, tabaction
, {.i
= XT_TAB_SHOW
}, FALSE
},
5053 { "tabs", 0, tabaction
, {.i
= XT_TAB_SHOW
}, FALSE
},
5054 { "tabhide", 0, tabaction
, {.i
= XT_TAB_HIDE
}, FALSE
},
5055 { "tabh", 0, tabaction
, {.i
= XT_TAB_HIDE
}, FALSE
},
5056 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
}, FALSE
},
5057 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
}, FALSE
},
5058 /* XXX add count to these commands */
5059 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5060 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5061 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5062 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
}, FALSE
},
5063 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
}, FALSE
},
5064 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
}, FALSE
},
5065 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
}, FALSE
},
5066 { "tabprev", 0, movetab
, {.i
= XT_TAB_PREV
}, FALSE
},
5067 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
}, FALSE
},
5068 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
}, FALSE
},
5069 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
}, FALSE
},
5070 { "tabgoto1", 0, movetab
, {.i
= 1}, FALSE
},
5071 { "tabgoto2", 0, movetab
, {.i
= 2}, FALSE
},
5072 { "tabgoto3", 0, movetab
, {.i
= 3}, FALSE
},
5073 { "tabgoto4", 0, movetab
, {.i
= 4}, FALSE
},
5074 { "tabgoto5", 0, movetab
, {.i
= 5}, FALSE
},
5075 { "tabgoto6", 0, movetab
, {.i
= 6}, FALSE
},
5076 { "tabgoto7", 0, movetab
, {.i
= 7}, FALSE
},
5077 { "tabgoto8", 0, movetab
, {.i
= 8}, FALSE
},
5078 { "tabgoto9", 0, movetab
, {.i
= 9}, FALSE
},
5079 { "tabgoto10", 0, movetab
, {.i
= 10}, FALSE
},
5080 { "focusout", 0, resizetab
, {.i
= -1}, FALSE
},
5081 { "focusin", 0, resizetab
, {.i
= 1}, FALSE
},
5082 { "focusin", 0, resizetab
, {.i
= 1}, FALSE
},
5084 /* command aliases (handy when -S flag is used) */
5085 { "promptopen", 0, command
, {.i
= XT_CMD_OPEN
}, FALSE
},
5086 { "promptopencurrent", 0, command
, {.i
= XT_CMD_OPEN_CURRENT
}, FALSE
},
5087 { "prompttabnew", 0, command
, {.i
= XT_CMD_TABNEW
}, FALSE
},
5088 { "prompttabnewcurrent",0, command
, {.i
= XT_CMD_TABNEW_CURRENT
}, FALSE
},
5091 { "set", 0, set
, {0}, FALSE
},
5092 { "fullscreen", 0, fullscreen
, {0}, FALSE
},
5093 { "f", 0, fullscreen
, {0}, FALSE
},
5096 { "session", 0, session_cmd
, {.i
= XT_SHOW
}, FALSE
},
5097 { "delete", 1, session_cmd
, {.i
= XT_DELETE
}, TRUE
},
5098 { "open", 1, session_cmd
, {.i
= XT_OPEN
}, TRUE
},
5099 { "save", 1, session_cmd
, {.i
= XT_SAVE
}, TRUE
},
5100 { "show", 1, session_cmd
, {.i
= XT_SHOW
}, FALSE
},
5107 } cmd_status
= {-1, 0};
5110 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5116 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5122 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5124 a
.i
= XT_NAV_FORWARD
;
5134 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5136 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5138 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5145 * cancel, remove, etc. downloads
5148 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5150 struct download find
, *d
= NULL
;
5152 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5154 /* some commands require a valid download id */
5155 if (cmd
!= XT_XTP_DL_LIST
) {
5156 /* lookup download in question */
5158 d
= RB_FIND(download_list
, &downloads
, &find
);
5161 show_oops(t
, "%s: no such download", __func__
);
5166 /* decide what to do */
5168 case XT_XTP_DL_CANCEL
:
5169 webkit_download_cancel(d
->download
);
5171 case XT_XTP_DL_REMOVE
:
5172 webkit_download_cancel(d
->download
); /* just incase */
5173 g_object_unref(d
->download
);
5174 RB_REMOVE(download_list
, &downloads
, d
);
5176 case XT_XTP_DL_LIST
:
5180 show_oops(t
, "%s: unknown command", __func__
);
5183 xtp_page_dl(t
, NULL
);
5187 * Actions on history, only does one thing for now, but
5188 * we provide the function for future actions
5191 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5193 struct history
*h
, *next
;
5197 case XT_XTP_HL_REMOVE
:
5198 /* walk backwards, as listed in reverse */
5199 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5200 next
= RB_PREV(history_list
, &hl
, h
);
5202 RB_REMOVE(history_list
, &hl
, h
);
5203 g_free((gpointer
) h
->title
);
5204 g_free((gpointer
) h
->uri
);
5211 case XT_XTP_HL_LIST
:
5212 /* Nothing - just xtp_page_hl() below */
5215 show_oops(t
, "%s: unknown command", __func__
);
5219 xtp_page_hl(t
, NULL
);
5222 /* remove a favorite */
5224 remove_favorite(struct tab
*t
, int index
)
5226 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5227 char *new_favs
, *tmp
;
5232 /* open favorites */
5233 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5235 if ((f
= fopen(file
, "r")) == NULL
) {
5236 show_oops(t
, "%s: can't open favorites: %s",
5237 __func__
, strerror(errno
));
5241 /* build a string which will become the new favroites file */
5242 new_favs
= g_strdup_printf("%s", "");
5245 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5246 if (feof(f
) || ferror(f
))
5248 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5255 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5256 if (feof(f
) || ferror(f
)) {
5257 show_oops(t
, "%s: can't parse favorites %s",
5258 __func__
, strerror(errno
));
5263 /* as long as this isn't the one we are deleting add to file */
5266 new_favs
= g_strdup_printf("%s%s\n%s\n",
5267 new_favs
, title
, uri
);
5279 /* write back new favorites file */
5280 if ((f
= fopen(file
, "w")) == NULL
) {
5281 show_oops(t
, "%s: can't open favorites: %s",
5282 __func__
, strerror(errno
));
5286 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5299 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5302 case XT_XTP_FL_LIST
:
5303 /* nothing, just the below call to xtp_page_fl() */
5305 case XT_XTP_FL_REMOVE
:
5306 remove_favorite(t
, arg
);
5309 show_oops(t
, "%s: invalid favorites command", __func__
);
5313 xtp_page_fl(t
, NULL
);
5317 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5320 case XT_XTP_CL_LIST
:
5321 /* nothing, just xtp_page_cl() */
5323 case XT_XTP_CL_REMOVE
:
5327 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5331 xtp_page_cl(t
, NULL
);
5334 /* link an XTP class to it's session key and handler function */
5335 struct xtp_despatch
{
5338 void (*handle_func
)(struct tab
*, uint8_t, int);
5341 struct xtp_despatch xtp_despatches
[] = {
5342 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5343 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5344 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5345 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5346 { NULL
, NULL
, NULL
}
5350 * is the url xtp protocol? (xxxt://)
5351 * if so, parse and despatch correct bahvior
5354 parse_xtp_url(struct tab
*t
, const char *url
)
5356 char *dup
= NULL
, *p
, *last
;
5357 uint8_t n_tokens
= 0;
5358 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5359 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5363 * tokens array meaning:
5365 * tokens[1] = session key
5366 * tokens[2] = action
5367 * tokens[3] = optional argument
5370 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5372 /*xtp tab meaning is normal unless proven special */
5373 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5375 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5378 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5380 /* split out the url */
5381 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5382 (p
= strtok_r(NULL
, "/", &last
))) {
5384 tokens
[n_tokens
++] = p
;
5387 /* should be atleast three fields 'class/seskey/command/arg' */
5391 dsp
= xtp_despatches
;
5392 req_class
= atoi(tokens
[0]);
5393 while (dsp
->xtp_class
!= NULL
) {
5394 if (dsp
->xtp_class
== req_class
) {
5401 /* did we find one atall? */
5402 if (dsp_match
== NULL
) {
5403 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5407 /* check session key and call despatch function */
5408 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5409 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5422 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5424 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5426 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5429 show_oops_s("activate_uri_entry_cb invalid parameters");
5434 show_oops(t
, "activate_uri_entry_cb no uri");
5438 uri
+= strspn(uri
, "\t ");
5440 /* if xxxt:// treat specially */
5441 if (!parse_xtp_url(t
, uri
)) {
5442 load_uri(t
, (gchar
*)uri
);
5448 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5450 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5451 char *newuri
= NULL
;
5454 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5457 show_oops_s("activate_search_entry_cb invalid parameters");
5461 if (search_string
== NULL
) {
5462 show_oops(t
, "no search_string");
5466 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5467 newuri
= g_strdup_printf(search_string
, enc_search
);
5470 webkit_web_view_load_uri(t
->wv
, newuri
);
5478 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5480 struct domain
*d
= NULL
;
5483 if (uri
== NULL
|| t
== NULL
)
5486 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5491 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5492 es
? "enable" : "disable", uri
);
5494 g_object_set(G_OBJECT(t
->settings
),
5495 "enable-scripts", es
, (char *)NULL
);
5496 g_object_set(G_OBJECT(t
->settings
),
5497 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5498 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5500 button_set_stockid(t
->js_toggle
,
5501 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5505 show_ca_status(struct tab
*t
, const char *uri
)
5507 WebKitWebFrame
*frame
;
5508 WebKitWebDataSource
*source
;
5509 WebKitNetworkRequest
*request
;
5510 SoupMessage
*message
;
5512 gchar
*col_str
= XT_COLOR_WHITE
;
5515 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5516 ssl_strict_certs
, ssl_ca_file
, uri
);
5520 if (ssl_ca_file
== NULL
) {
5521 if (g_str_has_prefix(uri
, "http://"))
5523 if (g_str_has_prefix(uri
, "https://")) {
5524 col_str
= XT_COLOR_RED
;
5529 if (g_str_has_prefix(uri
, "http://") ||
5530 !g_str_has_prefix(uri
, "https://"))
5533 frame
= webkit_web_view_get_main_frame(t
->wv
);
5534 source
= webkit_web_frame_get_data_source(frame
);
5535 request
= webkit_web_data_source_get_request(source
);
5536 message
= webkit_network_request_get_message(request
);
5538 if (message
&& (soup_message_get_flags(message
) &
5539 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5540 col_str
= XT_COLOR_GREEN
;
5543 r
= load_compare_cert(t
, NULL
);
5545 col_str
= XT_COLOR_BLUE
;
5547 col_str
= XT_COLOR_YELLOW
;
5549 col_str
= XT_COLOR_RED
;
5554 gdk_color_parse(col_str
, &color
);
5555 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5557 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5558 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5560 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5561 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5564 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5566 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5567 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5574 free_favicon(struct tab
*t
)
5576 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5577 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5579 if (t
->icon_request
)
5580 g_object_unref(t
->icon_request
);
5582 g_object_unref(t
->icon_pixbuf
);
5583 if (t
->icon_dest_uri
)
5584 g_free(t
->icon_dest_uri
);
5586 t
->icon_pixbuf
= NULL
;
5587 t
->icon_request
= NULL
;
5588 t
->icon_dest_uri
= NULL
;
5592 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5594 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5595 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5597 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5598 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5600 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5601 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5605 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5607 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5608 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5610 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5611 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5613 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5614 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5618 is_valid_icon(char *file
)
5621 const char *mime_type
;
5625 gf
= g_file_new_for_path(file
);
5626 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5628 mime_type
= g_file_info_get_content_type(fi
);
5629 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5630 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5631 g_strcmp0(mime_type
, "image/png") == 0 ||
5632 g_strcmp0(mime_type
, "image/gif") == 0 ||
5633 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5641 set_favicon_from_file(struct tab
*t
, char *file
)
5644 GdkPixbuf
*pixbuf
, *scaled
;
5647 if (t
== NULL
|| file
== NULL
)
5649 if (t
->icon_pixbuf
) {
5650 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5654 if (g_str_has_prefix(file
, "file://"))
5655 file
+= strlen("file://");
5656 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5658 if (!stat(file
, &sb
)) {
5659 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5660 /* corrupt icon so trash it */
5661 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5664 /* no need to set icon to default here */
5669 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5670 if (pixbuf
== NULL
) {
5671 xt_icon_from_name(t
, "text-html");
5675 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5677 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5678 __func__
, t
->tab_id
, width
, height
);
5680 if (width
> 16 || height
> 16) {
5681 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5682 GDK_INTERP_BILINEAR
);
5683 g_object_unref(pixbuf
);
5687 if (scaled
== NULL
) {
5688 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5689 GDK_INTERP_BILINEAR
);
5693 t
->icon_pixbuf
= scaled
;
5694 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5698 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5701 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5702 struct tab
*tt
= NULL
, *t
= NULL
;
5705 * find the webview instead of passing in the tab as it could have been
5706 * deleted from underneath us.
5708 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5717 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5718 __func__
, t
->tab_id
, status
);
5721 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5723 t
->icon_download
= NULL
;
5726 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5729 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5732 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5734 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5735 __func__
, t
->tab_id
);
5736 t
->icon_download
= NULL
;
5739 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5742 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5743 __func__
, t
->icon_dest_uri
);
5744 set_favicon_from_file(t
, t
->icon_dest_uri
);
5745 /* these will be freed post callback */
5746 t
->icon_request
= NULL
;
5747 t
->icon_download
= NULL
;
5755 abort_favicon_download(struct tab
*t
)
5757 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5759 if (t
->icon_download
) {
5760 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5761 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5762 webkit_download_cancel(t
->icon_download
);
5763 t
->icon_download
= NULL
;
5767 xt_icon_from_name(t
, "text-html");
5771 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5773 gchar
*name_hash
, file
[PATH_MAX
];
5776 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5778 if (uri
== NULL
|| t
== NULL
)
5781 if (t
->icon_request
) {
5782 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5786 /* check to see if we got the icon in cache */
5787 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5788 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5791 if (!stat(file
, &sb
)) {
5792 if (sb
.st_size
> 0) {
5793 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5795 set_favicon_from_file(t
, file
);
5799 /* corrupt icon so trash it */
5800 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5805 /* create download for icon */
5806 t
->icon_request
= webkit_network_request_new(uri
);
5807 if (t
->icon_request
== NULL
) {
5808 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5813 t
->icon_download
= webkit_download_new(t
->icon_request
);
5814 if (t
->icon_download
== NULL
) {
5815 fprintf(stderr
, "%s: icon_download", __func__
);
5819 /* we have to free icon_dest_uri later */
5820 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5821 webkit_download_set_destination_uri(t
->icon_download
,
5824 if (webkit_download_get_status(t
->icon_download
) ==
5825 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5826 fprintf(stderr
, "%s: download failed to start", __func__
);
5827 g_object_unref(t
->icon_request
);
5828 g_free(t
->icon_dest_uri
);
5829 t
->icon_request
= NULL
;
5830 t
->icon_dest_uri
= NULL
;
5834 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5835 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5837 webkit_download_start(t
->icon_download
);
5841 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5843 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5844 struct history
*h
, find
;
5845 const gchar
*s_loading
;
5848 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
5849 webkit_web_view_get_load_status(wview
), get_uri(wview
) ? get_uri(wview
) : "NOTHING");
5852 show_oops_s("notify_load_status_cb invalid paramters");
5856 switch (webkit_web_view_get_load_status(wview
)) {
5857 case WEBKIT_LOAD_PROVISIONAL
:
5859 abort_favicon_download(t
);
5860 #if GTK_CHECK_VERSION(2, 20, 0)
5861 gtk_widget_show(t
->spinner
);
5862 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5864 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5866 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5872 case WEBKIT_LOAD_COMMITTED
:
5874 if ((uri
= get_uri(wview
)) != NULL
) {
5875 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5876 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5882 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5885 /* check if js white listing is enabled */
5886 if (enable_js_whitelist
) {
5887 uri
= get_uri(wview
);
5888 check_and_set_js(uri
, t
);
5894 show_ca_status(t
, uri
);
5896 /* we know enough to autosave the session */
5897 if (session_autosave
) {
5903 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5907 case WEBKIT_LOAD_FINISHED
:
5909 uri
= get_uri(wview
);
5913 if (!strncmp(uri
, "http://", strlen("http://")) ||
5914 !strncmp(uri
, "https://", strlen("https://")) ||
5915 !strncmp(uri
, "file://", strlen("file://"))) {
5917 h
= RB_FIND(history_list
, &hl
, &find
);
5919 title
= webkit_web_view_get_title(wview
);
5920 set
= title
? title
: uri
;
5921 h
= g_malloc(sizeof *h
);
5922 h
->uri
= g_strdup(uri
);
5923 h
->title
= g_strdup(set
);
5924 RB_INSERT(history_list
, &hl
, h
);
5925 completion_add_uri(h
->uri
);
5926 update_history_tabs(NULL
);
5930 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5931 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5932 case WEBKIT_LOAD_FAILED
:
5935 #if GTK_CHECK_VERSION(2, 20, 0)
5936 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5937 gtk_widget_hide(t
->spinner
);
5939 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5940 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5941 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5943 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5948 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5950 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5951 webkit_web_view_can_go_back(wview
));
5953 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5954 webkit_web_view_can_go_forward(wview
));
5956 /* take focus if we are visible */
5961 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5963 const gchar
*set
= NULL
, *title
= NULL
;
5965 title
= webkit_web_view_get_title(wview
);
5966 set
= title
? title
: get_uri(wview
);
5967 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5968 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5972 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5974 run_script(t
, JS_HINTING
);
5978 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5980 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5981 progress
== 100 ? 0 : (double)progress
/ 100);
5982 if (show_url
== 0) {
5983 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5984 progress
== 100 ? 0 : (double)progress
/ 100);
5989 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5990 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5991 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5994 WebKitWebNavigationReason reason
;
5995 struct domain
*d
= NULL
;
5998 show_oops_s("webview_npd_cb invalid parameters");
6002 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6004 webkit_network_request_get_uri(request
));
6006 uri
= (char *)webkit_network_request_get_uri(request
);
6008 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
6010 create_new_tab(uri
, NULL
, ctrl_click_focus
);
6011 webkit_web_policy_decision_ignore(pd
);
6012 return (TRUE
); /* we made the decission */
6016 * This is a little hairy but it comes down to this:
6017 * when we run in whitelist mode we have to assist the browser in
6018 * opening the URL that it would have opened in a new tab.
6020 reason
= webkit_web_navigation_action_get_reason(na
);
6021 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6022 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6023 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6026 webkit_web_policy_decision_use(pd
);
6027 return (TRUE
); /* we made the decission */
6034 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6037 struct domain
*d
= NULL
;
6039 WebKitWebView
*webview
= NULL
;
6041 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6042 webkit_web_view_get_uri(wv
));
6045 /* open in current tab */
6047 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6048 uri
= webkit_web_view_get_uri(wv
);
6049 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6052 tt
= create_new_tab(NULL
, NULL
, 1);
6054 } else if (enable_scripts
== 1) {
6055 tt
= create_new_tab(NULL
, NULL
, 1);
6063 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6066 struct domain
*d
= NULL
;
6068 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6070 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6071 uri
= webkit_web_view_get_uri(wv
);
6072 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6076 } else if (enable_scripts
== 1)
6083 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6085 /* we can not eat the event without throwing gtk off so defer it */
6087 /* catch middle click */
6088 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6093 /* catch ctrl click */
6094 if (e
->type
== GDK_BUTTON_RELEASE
&&
6095 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6100 return (XT_CB_PASSTHROUGH
);
6104 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6106 struct mime_type
*m
;
6108 m
= find_mime_type(mime_type
);
6116 show_oops(t
, "can't fork mime handler");
6125 execlp(m
->mt_action
, m
->mt_action
,
6126 webkit_network_request_get_uri(request
), (void *)NULL
);
6135 get_mime_type(char *file
)
6137 const char *mime_type
;
6141 if (g_str_has_prefix(file
, "file://"))
6142 file
+= strlen("file://");
6144 gf
= g_file_new_for_path(file
);
6145 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6147 mime_type
= g_file_info_get_content_type(fi
);
6155 run_download_mimehandler(char *mime_type
, char *file
)
6157 struct mime_type
*m
;
6159 m
= find_mime_type(mime_type
);
6165 show_oops_s("can't fork download mime handler");
6174 if (g_str_has_prefix(file
, "file://"))
6175 file
+= strlen("file://");
6176 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6185 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6188 WebKitDownloadStatus status
;
6189 const gchar
*file
= NULL
, *mime
= NULL
;
6191 if (download
== NULL
)
6193 status
= webkit_download_get_status(download
);
6194 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6197 file
= webkit_download_get_destination_uri(download
);
6200 mime
= get_mime_type((char *)file
);
6204 run_download_mimehandler((char *)mime
, (char *)file
);
6208 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6209 WebKitNetworkRequest
*request
, char *mime_type
,
6210 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6213 show_oops_s("webview_mimetype_cb invalid parameters");
6217 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6218 t
->tab_id
, mime_type
);
6220 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6221 webkit_web_policy_decision_ignore(decision
);
6226 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6227 webkit_web_policy_decision_download(decision
);
6235 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6238 const gchar
*filename
;
6240 struct download
*download_entry
;
6243 if (wk_download
== NULL
|| t
== NULL
) {
6244 show_oops_s("%s invalid parameters", __func__
);
6248 filename
= webkit_download_get_suggested_filename(wk_download
);
6249 if (filename
== NULL
)
6250 return (FALSE
); /* abort download */
6252 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
6254 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6255 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6257 webkit_download_set_destination_uri(wk_download
, uri
);
6259 if (webkit_download_get_status(wk_download
) ==
6260 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6261 show_oops(t
, "%s: download failed to start", __func__
);
6263 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6265 /* connect "download first" mime handler */
6266 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6267 G_CALLBACK(download_status_changed_cb
), NULL
);
6269 download_entry
= g_malloc(sizeof(struct download
));
6270 download_entry
->download
= wk_download
;
6271 download_entry
->tab
= t
;
6272 download_entry
->id
= next_download_id
++;
6273 RB_INSERT(download_list
, &downloads
, download_entry
);
6274 /* get from history */
6275 g_object_ref(wk_download
);
6276 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6277 show_oops(t
, "Download of '%s' started...",
6278 basename(webkit_download_get_destination_uri(wk_download
)));
6284 /* sync other download manager tabs */
6285 update_download_tabs(NULL
);
6288 * NOTE: never redirect/render the current tab before this
6289 * function returns. This will cause the download to never start.
6291 return (ret
); /* start download */
6295 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6297 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6300 show_oops_s("webview_hover_cb");
6305 set_status(t
, uri
, XT_STATUS_LINK
);
6308 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6313 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6315 struct key_binding
*k
;
6317 TAILQ_FOREACH(k
, &kbl
, entry
)
6318 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6320 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6321 return (cmd_execute(t
, k
->cmd
));
6322 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6323 return (cmd_execute(t
, k
->cmd
));
6327 return (XT_CB_PASSTHROUGH
);
6331 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6333 char s
[2], buf
[128];
6334 const char *errstr
= NULL
;
6337 /* don't use w directly; use t->whatever instead */
6340 show_oops_s("wv_keypress_after_cb");
6341 return (XT_CB_PASSTHROUGH
);
6344 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6345 e
->keyval
, e
->state
, t
);
6349 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6351 return (XT_CB_HANDLED
);
6355 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6356 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6358 /* we have a string */
6360 /* we have a number */
6361 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6369 /* XXX unfuck this */
6370 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6371 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6372 /* last input was numerical */
6374 l
= strlen(t
->hint_num
);
6381 t
->hint_num
[l
] = '\0';
6385 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6386 /* last input was alphanumerical */
6388 l
= strlen(t
->hint_buf
);
6395 t
->hint_buf
[l
] = '\0';
6405 /* numerical input */
6406 if (CLEAN(e
->state
) == 0 &&
6407 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6408 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6409 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6410 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6413 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6415 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6418 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6420 t
->hint_mode
= XT_HINT_NUMERICAL
;
6424 /* empty the counter buffer */
6425 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6426 return (XT_CB_HANDLED
);
6429 /* alphanumerical input */
6431 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6432 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6433 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6434 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6435 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6436 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6437 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6440 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6443 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6445 t
->hint_mode
= XT_HINT_ALPHANUM
;
6448 /* empty the counter buffer */
6449 bzero(t
->hint_num
, sizeof t
->hint_num
);
6450 return (XT_CB_HANDLED
);
6453 return (XT_CB_HANDLED
);
6456 return (handle_keypress(t
, e
, 0));
6460 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6464 return (XT_CB_PASSTHROUGH
);
6468 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6470 const gchar
*c
= gtk_entry_get_text(w
);
6474 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6475 e
->keyval
, e
->state
, t
);
6478 show_oops_s("cmd_keyrelease_cb invalid parameters");
6479 return (XT_CB_PASSTHROUGH
);
6482 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6483 e
->keyval
, e
->state
, t
);
6487 if (strlen(c
) == 1) {
6488 webkit_web_view_unmark_text_matches(t
->wv
);
6494 else if (c
[0] == '?')
6500 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6502 /* not found, mark red */
6503 gdk_color_parse(XT_COLOR_RED
, &color
);
6504 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6505 /* unmark and remove selection */
6506 webkit_web_view_unmark_text_matches(t
->wv
);
6507 /* my kingdom for a way to unselect text in webview */
6509 /* found, highlight all */
6510 webkit_web_view_unmark_text_matches(t
->wv
);
6511 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6512 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6513 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6514 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6517 return (XT_CB_PASSTHROUGH
);
6521 match_uri(const gchar
*uri
, const gchar
*key
) {
6524 gboolean match
= FALSE
;
6528 if (!strncmp(key
, uri
, len
))
6531 voffset
= strstr(uri
, "/") + 2;
6532 if (!strncmp(key
, voffset
, len
))
6534 else if (g_str_has_prefix(voffset
, "www.")) {
6535 voffset
= voffset
+ strlen("www.");
6536 if (!strncmp(key
, voffset
, len
))
6545 cmd_getlist(int id
, char *key
)
6550 if (id
>= 0 && (cmds
[id
].userarg
&& (cmds
[id
].arg
.i
== XT_TAB_OPEN
|| cmds
[id
].arg
.i
== XT_TAB_NEW
))) {
6551 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6552 if (match_uri(h
->uri
, key
)) {
6553 cmd_status
.list
[c
] = (char *)h
->uri
;
6562 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6564 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6565 if(cmds
[i
].level
< dep
)
6567 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6568 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6576 cmd_getnext(int dir
)
6578 cmd_status
.index
+= dir
;
6580 if (cmd_status
.index
<0)
6581 cmd_status
.index
= cmd_status
.len
-1;
6582 else if (cmd_status
.index
>= cmd_status
.len
)
6583 cmd_status
.index
= 0;
6585 return cmd_status
.list
[cmd_status
.index
];
6589 cmd_tokenize(char *s
, char *tokens
[])
6593 size_t len
= strlen(s
);
6594 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6596 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6606 cmd_complete(struct tab
*t
, char *str
, int dir
)
6608 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6609 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
6610 char *tok
, *match
, *s
= strdup(str
);
6612 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6614 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", str
);
6616 levels
= cmd_tokenize(s
, tokens
);
6618 for (i
= 0; i
< levels
- 1; i
++) {
6620 for (j
= c
; j
< LENGTH(cmds
); j
++)
6621 if (cmds
[j
].level
== dep
&& !strcmp(tok
, cmds
[j
].cmd
)) {
6622 strlcat(res
, tok
, sizeof res
);
6623 strlcat(res
, " ", sizeof res
);
6632 if (cmd_status
.index
== -1)
6633 cmd_getlist(parent
, tokens
[i
]);
6635 if (cmd_status
.len
> 0) {
6636 match
= cmd_getnext(dir
);
6637 strlcat(res
, match
, sizeof res
);
6638 gtk_entry_set_text(w
, res
);
6639 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6644 cmd_valid(char *str
)
6646 char *tok
, *last
, *s
= g_strdup(str
);
6647 int i
, c
= 0, dep
= 0;
6649 for (tok
= strtok_r(s
, " ", &last
); tok
;
6650 tok
= strtok_r(NULL
, " ", &last
)) {
6651 for (i
= c
; i
< LENGTH(cmds
); i
++) {
6652 if (cmds
[i
].level
< dep
) {
6653 return (XT_CB_PASSTHROUGH
);
6655 if (cmds
[i
].level
== dep
&& !strcmp(tok
, cmds
[i
].cmd
)) {
6656 if (cmds
[i
].userarg
) {
6657 return (XT_CB_HANDLED
);
6664 if (i
== LENGTH(cmds
)) {
6665 return (XT_CB_PASSTHROUGH
);
6668 return (XT_CB_HANDLED
);
6672 cmd_execute(struct tab
*t
, char *str
)
6674 struct cmd
*cmd
= NULL
;
6675 char *tok
, *last
, *s
= g_strdup(str
);
6676 int j
, c
= 0, dep
= 0;
6678 for (tok
= strtok_r(s
, " ", &last
); tok
;
6679 tok
= strtok_r(NULL
, " ", &last
)) {
6680 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6681 if (cmds
[j
].level
< dep
) {
6682 show_oops(t
, "Invalid command: %s", str
);
6683 return (XT_CB_PASSTHROUGH
);
6685 if (cmds
[j
].level
== dep
&& !strcmp(tok
, cmds
[j
].cmd
)) {
6688 cmd
->arg
.s
= last
? g_strdup(last
) : g_strdup("");
6696 if (j
== LENGTH(cmds
)) {
6697 show_oops(t
, "Invalid command: %s", str
);
6698 return (XT_CB_PASSTHROUGH
);
6701 cmd
->arg
.s
= g_strdup(tok
);
6703 /* arg->s contains last token */
6704 cmd
->func(t
, &cmd
->arg
);
6708 return (XT_CB_HANDLED
);
6712 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6715 show_oops_s("entry_key_cb invalid parameters");
6716 return (XT_CB_PASSTHROUGH
);
6719 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6720 e
->keyval
, e
->state
, t
);
6724 if (e
->keyval
== GDK_Escape
) {
6725 /* don't use focus_webview(t) because we want to type :cmds */
6726 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6729 return (handle_keypress(t
, e
, 1));
6733 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6735 int rv
= XT_CB_HANDLED
;
6736 const gchar
*c
= gtk_entry_get_text(w
);
6739 show_oops_s("cmd_keypress_cb parameters");
6740 return (XT_CB_PASSTHROUGH
);
6743 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6744 e
->keyval
, e
->state
, t
);
6748 e
->keyval
= GDK_Escape
;
6749 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6750 e
->keyval
= GDK_Escape
;
6752 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
6753 cmd_status
.index
= -1;
6755 switch (e
->keyval
) {
6758 cmd_complete(t
, (char *)&c
[1], 1);
6760 case GDK_ISO_Left_Tab
:
6762 cmd_complete(t
, (char *)&c
[1], -1);
6766 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6774 if (c
[0] == '/' || c
[0] == '?')
6775 webkit_web_view_unmark_text_matches(t
->wv
);
6779 rv
= XT_CB_PASSTHROUGH
;
6785 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6788 show_oops_s("cmd_focusout_cb invalid parameters");
6789 return (XT_CB_PASSTHROUGH
);
6791 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6796 if (show_url
== 0 || t
->focus_wv
)
6799 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6801 return (XT_CB_PASSTHROUGH
);
6805 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6808 const gchar
*c
= gtk_entry_get_text(entry
);
6811 show_oops_s("cmd_activate_cb invalid parameters");
6815 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6820 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6826 if (c
[0] == '/' || c
[0] == '?') {
6827 if (t
->search_text
) {
6828 g_free(t
->search_text
);
6829 t
->search_text
= NULL
;
6832 t
->search_text
= g_strdup(s
);
6834 g_free(global_search
);
6835 global_search
= g_strdup(s
);
6836 t
->search_forward
= c
[0] == '/';
6848 backward_cb(GtkWidget
*w
, struct tab
*t
)
6853 show_oops_s("backward_cb invalid parameters");
6857 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6864 forward_cb(GtkWidget
*w
, struct tab
*t
)
6869 show_oops_s("forward_cb invalid parameters");
6873 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6875 a
.i
= XT_NAV_FORWARD
;
6880 home_cb(GtkWidget
*w
, struct tab
*t
)
6883 show_oops_s("home_cb invalid parameters");
6887 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
6893 stop_cb(GtkWidget
*w
, struct tab
*t
)
6895 WebKitWebFrame
*frame
;
6898 show_oops_s("stop_cb invalid parameters");
6902 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6904 frame
= webkit_web_view_get_main_frame(t
->wv
);
6905 if (frame
== NULL
) {
6906 show_oops(t
, "stop_cb: no frame");
6910 webkit_web_frame_stop_loading(frame
);
6911 abort_favicon_download(t
);
6915 setup_webkit(struct tab
*t
)
6917 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
6918 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
6919 FALSE
, (char *)NULL
);
6921 warnx("webkit does not have \"enable-dns-prefetching\" property");
6922 g_object_set(G_OBJECT(t
->settings
),
6923 "user-agent", t
->user_agent
, (char *)NULL
);
6924 g_object_set(G_OBJECT(t
->settings
),
6925 "enable-scripts", enable_scripts
, (char *)NULL
);
6926 g_object_set(G_OBJECT(t
->settings
),
6927 "enable-plugins", enable_plugins
, (char *)NULL
);
6928 g_object_set(G_OBJECT(t
->settings
),
6929 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
6930 g_object_set(G_OBJECT(t
->wv
),
6931 "full-content-zoom", TRUE
, (char *)NULL
);
6932 adjustfont_webkit(t
, XT_FONT_SET
);
6934 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6938 create_browser(struct tab
*t
)
6944 show_oops_s("create_browser invalid parameters");
6948 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
6949 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
6950 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
6951 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
6953 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
6954 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
6955 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
6957 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
6958 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
6961 t
->settings
= webkit_web_settings_new();
6963 if (user_agent
== NULL
) {
6964 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
6966 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
6969 t
->user_agent
= g_strdup(user_agent
);
6971 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
6983 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6984 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6985 gtk_widget_set_name(w
, "xxxterm");
6986 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
6987 g_signal_connect(G_OBJECT(w
), "delete_event",
6988 G_CALLBACK (gtk_main_quit
), NULL
);
6994 create_kiosk_toolbar(struct tab
*t
)
6996 GtkWidget
*toolbar
= NULL
, *b
;
6998 b
= gtk_hbox_new(FALSE
, 0);
7000 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7002 /* backward button */
7003 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7004 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7005 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7006 G_CALLBACK(backward_cb
), t
);
7007 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7009 /* forward button */
7010 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7011 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7012 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7013 G_CALLBACK(forward_cb
), t
);
7014 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7017 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7018 gtk_widget_set_sensitive(t
->gohome
, true);
7019 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7020 G_CALLBACK(home_cb
), t
);
7021 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7023 /* create widgets but don't use them */
7024 t
->uri_entry
= gtk_entry_new();
7025 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7026 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7027 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7033 create_toolbar(struct tab
*t
)
7035 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7037 b
= gtk_hbox_new(FALSE
, 0);
7039 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7042 /* backward button */
7043 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7044 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7045 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7046 G_CALLBACK(backward_cb
), t
);
7047 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7049 /* forward button */
7050 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7051 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7052 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7053 G_CALLBACK(forward_cb
), t
);
7054 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7058 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7059 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7060 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7061 G_CALLBACK(stop_cb
), t
);
7062 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7066 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7067 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7068 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7069 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7070 G_CALLBACK(js_toggle_cb
), t
);
7071 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7074 t
->uri_entry
= gtk_entry_new();
7075 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7076 G_CALLBACK(activate_uri_entry_cb
), t
);
7077 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7078 G_CALLBACK(entry_key_cb
), t
);
7080 eb1
= gtk_hbox_new(FALSE
, 0);
7081 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7082 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7083 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7086 if (fancy_bar
&& search_string
) {
7088 t
->search_entry
= gtk_entry_new();
7089 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7090 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7091 G_CALLBACK(activate_search_entry_cb
), t
);
7092 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7093 G_CALLBACK(entry_key_cb
), t
);
7094 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7095 eb2
= gtk_hbox_new(FALSE
, 0);
7096 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7097 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7099 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7109 TAILQ_FOREACH(t
, &tabs
, entry
)
7110 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7114 undo_close_tab_save(struct tab
*t
)
7118 struct undo
*u1
, *u2
;
7120 WebKitWebHistoryItem
*item
;
7122 if ((uri
= get_uri(t
->wv
)) == NULL
)
7125 u1
= g_malloc0(sizeof(struct undo
));
7126 u1
->uri
= g_strdup(uri
);
7128 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7130 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7131 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7134 /* forward history */
7135 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7139 u1
->history
= g_list_prepend(u1
->history
,
7140 webkit_web_history_item_copy(item
));
7141 items
= g_list_next(items
);
7146 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7147 u1
->history
= g_list_prepend(u1
->history
,
7148 webkit_web_history_item_copy(item
));
7152 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7156 u1
->history
= g_list_prepend(u1
->history
,
7157 webkit_web_history_item_copy(item
));
7158 items
= g_list_next(items
);
7161 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7163 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7164 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7165 TAILQ_REMOVE(&undos
, u2
, entry
);
7167 g_list_free(u2
->history
);
7176 delete_tab(struct tab
*t
)
7180 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7185 TAILQ_REMOVE(&tabs
, t
, entry
);
7187 /* halt all webkit activity */
7188 abort_favicon_download(t
);
7189 webkit_web_view_stop_loading(t
->wv
);
7190 undo_close_tab_save(t
);
7192 if (browser_mode
== XT_BM_KIOSK
) {
7193 gtk_widget_destroy(t
->uri_entry
);
7194 gtk_widget_destroy(t
->stop
);
7195 gtk_widget_destroy(t
->js_toggle
);
7199 gtk_widget_destroy(t
->vbox
);
7200 g_free(t
->user_agent
);
7201 g_free(t
->stylesheet
);
7205 if (TAILQ_EMPTY(&tabs
)) {
7206 if (browser_mode
== XT_BM_KIOSK
)
7207 create_new_tab(home
, NULL
, 1);
7209 create_new_tab(NULL
, NULL
, 1);
7212 /* recreate session */
7213 if (session_autosave
) {
7220 adjustfont_webkit(struct tab
*t
, int adjust
)
7225 show_oops_s("adjustfont_webkit invalid parameters");
7229 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7230 if (adjust
== XT_FONT_SET
) {
7231 t
->font_size
= default_font_size
;
7232 zoom
= default_zoom_level
;
7233 t
->font_size
+= adjust
;
7234 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7235 t
->font_size
, (char *)NULL
);
7236 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7237 &t
->font_size
, (char *)NULL
);
7239 t
->font_size
+= adjust
;
7240 zoom
+= adjust
/25.0;
7245 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7246 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7250 append_tab(struct tab
*t
)
7255 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7256 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7260 create_new_tab(char *title
, struct undo
*u
, int focus
)
7263 int load
= 1, id
, notfound
;
7265 WebKitWebHistoryItem
*item
;
7269 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7271 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7272 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7276 t
= g_malloc0(sizeof *t
);
7278 if (title
== NULL
) {
7279 title
= "(untitled)";
7283 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7285 /* label + button for tab */
7286 b
= gtk_hbox_new(FALSE
, 0);
7289 #if GTK_CHECK_VERSION(2, 20, 0)
7290 t
->spinner
= gtk_spinner_new ();
7292 t
->label
= gtk_label_new(title
);
7293 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7294 gtk_widget_set_size_request(t
->label
, 100, 0);
7295 gtk_widget_set_size_request(b
, 130, 0);
7297 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7298 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7299 #if GTK_CHECK_VERSION(2, 20, 0)
7300 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7304 if (browser_mode
== XT_BM_KIOSK
)
7305 t
->toolbar
= create_kiosk_toolbar(t
);
7307 t
->toolbar
= create_toolbar(t
);
7309 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7312 t
->browser_win
= create_browser(t
);
7313 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7315 /* oops message for user feedback */
7316 t
->oops
= gtk_entry_new();
7317 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7318 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7319 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7320 gdk_color_parse(XT_COLOR_RED
, &color
);
7321 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7322 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7325 t
->cmd
= gtk_entry_new();
7326 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7327 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7328 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7331 t
->statusbar
= gtk_entry_new();
7332 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
7333 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
7334 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
7335 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7336 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7337 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7338 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7339 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
7341 /* xtp meaning is normal by default */
7342 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7344 /* set empty favicon */
7345 xt_icon_from_name(t
, "text-html");
7347 /* and show it all */
7348 gtk_widget_show_all(b
);
7349 gtk_widget_show_all(t
->vbox
);
7351 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7355 id
= gtk_notebook_get_current_page(notebook
);
7356 TAILQ_FOREACH(tt
, &tabs
, entry
) {
7357 if (tt
->tab_id
== id
) {
7359 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
7360 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
7370 #if GTK_CHECK_VERSION(2, 20, 0)
7371 /* turn spinner off if we are a new tab without uri */
7373 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7374 gtk_widget_hide(t
->spinner
);
7377 /* make notebook tabs reorderable */
7378 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7380 g_object_connect(G_OBJECT(t
->cmd
),
7381 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7382 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7383 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7384 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7387 /* reuse wv_button_cb to hide oops */
7388 g_object_connect(G_OBJECT(t
->oops
),
7389 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7392 g_object_connect(G_OBJECT(t
->wv
),
7393 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7394 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7395 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7396 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7397 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7398 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7399 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7400 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7401 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7402 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7403 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7404 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7405 #if WEBKIT_CHECK_VERSION(1, 1, 18)
7406 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7408 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7410 g_signal_connect(t
->wv
,
7411 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7412 g_signal_connect(t
->wv
,
7413 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7415 /* hijack the unused keys as if we were the browser */
7416 g_object_connect(G_OBJECT(t
->toolbar
),
7417 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7420 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7421 G_CALLBACK(tab_close_cb
), t
);
7426 url_set_visibility();
7427 statusbar_set_visibility();
7430 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7431 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7436 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7440 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7445 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7446 /* restore the tab's history */
7447 if (u
&& u
->history
) {
7451 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7452 items
= g_list_next(items
);
7455 item
= g_list_nth_data(u
->history
, u
->back
);
7457 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7460 g_list_free(u
->history
);
7462 webkit_web_back_forward_list_clear(t
->bfl
);
7468 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
7474 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7476 TAILQ_FOREACH(t
, &tabs
, entry
) {
7477 if (t
->tab_id
== pn
) {
7478 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7481 uri
= webkit_web_view_get_title(t
->wv
);
7484 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7496 menuitem_response(struct tab
*t
)
7498 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7502 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7504 GtkWidget
*menu
, *menu_items
;
7505 GdkEventButton
*bevent
;
7509 if (event
->type
== GDK_BUTTON_PRESS
) {
7510 bevent
= (GdkEventButton
*) event
;
7511 menu
= gtk_menu_new();
7513 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7514 if ((uri
= get_uri(ti
->wv
)) == NULL
)
7515 /* XXX make sure there is something to print */
7516 /* XXX add gui pages in here to look purdy */
7518 menu_items
= gtk_menu_item_new_with_label(uri
);
7519 gtk_menu_append(GTK_MENU (menu
), menu_items
);
7520 gtk_widget_show(menu_items
);
7522 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
7523 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
7527 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7528 bevent
->button
, bevent
->time
);
7530 /* unref object so it'll free itself when popped down */
7531 g_object_ref_sink(menu
);
7532 g_object_unref(menu
);
7534 return (TRUE
/* eat event */);
7537 return (FALSE
/* propagate */);
7541 icon_size_map(int icon_size
)
7543 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7544 icon_size
> GTK_ICON_SIZE_DIALOG
)
7545 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7551 create_button(char *name
, char *stockid
, int size
)
7553 GtkWidget
*button
, *image
;
7557 rcstring
= g_strdup_printf(
7558 "style \"%s-style\"\n"
7560 " GtkWidget::focus-padding = 0\n"
7561 " GtkWidget::focus-line-width = 0\n"
7565 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
7566 gtk_rc_parse_string(rcstring
);
7568 button
= gtk_button_new();
7569 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7570 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
7572 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7573 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7574 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7575 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7576 gtk_widget_set_name(button
, name
);
7577 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7583 button_set_stockid(GtkWidget
*button
, char *stockid
)
7587 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7588 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7589 gtk_button_set_image(GTK_BUTTON(button
), image
);
7598 char file
[PATH_MAX
];
7601 vbox
= gtk_vbox_new(FALSE
, 0);
7602 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7603 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7604 gtk_notebook_set_tab_hborder(notebook
, 0);
7605 gtk_notebook_set_tab_vborder(notebook
, 0);
7606 gtk_notebook_set_scrollable(notebook
, TRUE
);
7607 notebook_tab_set_visibility(notebook
);
7608 gtk_notebook_set_show_border(notebook
, FALSE
);
7609 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7611 abtn
= gtk_button_new();
7612 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7613 gtk_widget_set_size_request(arrow
, -1, -1);
7614 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7615 gtk_widget_set_size_request(abtn
, -1, 20);
7616 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7618 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7619 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7620 gtk_widget_set_size_request(vbox
, -1, -1);
7622 g_object_connect(G_OBJECT(notebook
),
7623 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7625 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7626 G_CALLBACK(arrow_cb
), NULL
);
7628 main_window
= create_window();
7629 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7630 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7633 for (i
= 0; i
< LENGTH(icons
); i
++) {
7634 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7635 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7636 l
= g_list_append(l
, pb
);
7638 gtk_window_set_default_icon_list(l
);
7640 gtk_widget_show_all(abtn
);
7641 gtk_widget_show_all(main_window
);
7645 set_hook(void **hook
, char *name
)
7648 errx(1, "set_hook");
7650 if (*hook
== NULL
) {
7651 *hook
= dlsym(RTLD_NEXT
, name
);
7653 errx(1, "can't hook %s", name
);
7657 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7659 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7661 g_return_val_if_fail(cookie1
, FALSE
);
7662 g_return_val_if_fail(cookie2
, FALSE
);
7664 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7665 !strcmp (cookie1
->value
, cookie2
->value
) &&
7666 !strcmp (cookie1
->path
, cookie2
->path
) &&
7667 !strcmp (cookie1
->domain
, cookie2
->domain
));
7671 transfer_cookies(void)
7674 SoupCookie
*sc
, *pc
;
7676 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7678 for (;cf
; cf
= cf
->next
) {
7680 sc
= soup_cookie_copy(pc
);
7681 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7684 soup_cookies_free(cf
);
7688 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7693 print_cookie("soup_cookie_jar_delete_cookie", c
);
7695 if (cookies_enabled
== 0)
7698 if (jar
== NULL
|| c
== NULL
)
7701 /* find and remove from persistent jar */
7702 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7704 for (;cf
; cf
= cf
->next
) {
7706 if (soup_cookie_equal(ci
, c
)) {
7707 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7712 soup_cookies_free(cf
);
7714 /* delete from session jar */
7715 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7719 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7721 struct domain
*d
= NULL
;
7725 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7726 jar
, p_cookiejar
, s_cookiejar
);
7728 if (cookies_enabled
== 0)
7731 /* see if we are up and running */
7732 if (p_cookiejar
== NULL
) {
7733 _soup_cookie_jar_add_cookie(jar
, cookie
);
7736 /* disallow p_cookiejar adds, shouldn't happen */
7737 if (jar
== p_cookiejar
)
7741 if (jar
== NULL
|| cookie
== NULL
)
7744 if (enable_cookie_whitelist
&&
7745 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7747 DNPRINTF(XT_D_COOKIE
,
7748 "soup_cookie_jar_add_cookie: reject %s\n",
7750 if (save_rejected_cookies
) {
7751 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7752 show_oops_s("can't open reject cookie file");
7755 fseek(r_cookie_f
, 0, SEEK_END
);
7756 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7757 cookie
->http_only
? "#HttpOnly_" : "",
7759 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7761 cookie
->secure
? "TRUE" : "FALSE",
7763 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7770 if (!allow_volatile_cookies
)
7774 if (cookie
->expires
== NULL
&& session_timeout
) {
7775 soup_cookie_set_expires(cookie
,
7776 soup_date_new_from_now(session_timeout
));
7777 print_cookie("modified add cookie", cookie
);
7780 /* see if we are white listed for persistence */
7781 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7782 /* add to persistent jar */
7783 c
= soup_cookie_copy(cookie
);
7784 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7785 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7788 /* add to session jar */
7789 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7790 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7796 char file
[PATH_MAX
];
7798 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7799 "soup_cookie_jar_add_cookie");
7800 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7801 "soup_cookie_jar_delete_cookie");
7803 if (cookies_enabled
== 0)
7807 * the following code is intricate due to overriding several libsoup
7809 * do not alter order of these operations.
7812 /* rejected cookies */
7813 if (save_rejected_cookies
)
7814 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
7816 /* persistent cookies */
7817 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
7818 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7820 /* session cookies */
7821 s_cookiejar
= soup_cookie_jar_new();
7822 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7823 cookie_policy
, (void *)NULL
);
7826 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7830 setup_proxy(char *uri
)
7833 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7834 soup_uri_free(proxy_uri
);
7838 if (http_proxy
!= uri
) {
7845 http_proxy
= g_strdup(uri
);
7846 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7847 proxy_uri
= soup_uri_new(http_proxy
);
7848 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7853 send_cmd_to_socket(char *cmd
)
7856 struct sockaddr_un sa
;
7858 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7859 warnx("%s: socket", __func__
);
7863 sa
.sun_family
= AF_UNIX
;
7864 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7865 work_dir
, XT_SOCKET_FILE
);
7868 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7869 warnx("%s: connect", __func__
);
7873 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
7874 warnx("%s: send", __func__
);
7885 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7888 char str
[XT_MAX_URL_LENGTH
];
7889 socklen_t t
= sizeof(struct sockaddr_un
);
7890 struct sockaddr_un sa
;
7896 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7901 if (getpeereid(s
, &uid
, &gid
) == -1) {
7905 if (uid
!= getuid() || gid
!= getgid()) {
7906 warnx("unauthorized user");
7912 warnx("not a valid user");
7916 n
= recv(s
, str
, sizeof(str
), 0);
7920 tt
= TAILQ_LAST(&tabs
, tab_list
);
7921 cmd_execute(tt
, str
);
7928 struct sockaddr_un sa
;
7930 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7931 warn("is_running: socket");
7935 sa
.sun_family
= AF_UNIX
;
7936 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7937 work_dir
, XT_SOCKET_FILE
);
7940 /* connect to see if there is a listener */
7941 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7942 rv
= 0; /* not running */
7944 rv
= 1; /* already running */
7955 struct sockaddr_un sa
;
7957 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7958 warn("build_socket: socket");
7962 sa
.sun_family
= AF_UNIX
;
7963 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7964 work_dir
, XT_SOCKET_FILE
);
7967 /* connect to see if there is a listener */
7968 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7969 /* no listener so we will */
7970 unlink(sa
.sun_path
);
7972 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7973 warn("build_socket: bind");
7977 if (listen(s
, 1) == -1) {
7978 warn("build_socket: listen");
7991 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
7992 GtkTreeIter
*iter
, struct tab
*t
)
7996 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8003 completion_add_uri(const gchar
*uri
)
8007 /* add uri to list_store */
8008 gtk_list_store_append(completion_model
, &iter
);
8009 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8013 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8014 GtkTreeIter
*iter
, gpointer user_data
)
8017 gboolean match
= FALSE
;
8019 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8025 match
= match_uri(value
, key
);
8032 completion_add(struct tab
*t
)
8034 /* enable completion for tab */
8035 t
->completion
= gtk_entry_completion_new();
8036 gtk_entry_completion_set_text_column(t
->completion
, 0);
8037 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8038 gtk_entry_completion_set_model(t
->completion
,
8039 GTK_TREE_MODEL(completion_model
));
8040 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8042 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8043 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8044 G_CALLBACK(completion_select_cb
), t
);
8051 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8056 main(int argc
, char *argv
[])
8059 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8060 char conf
[PATH_MAX
] = { '\0' };
8061 char file
[PATH_MAX
];
8062 char *env_proxy
= NULL
;
8065 struct sigaction sact
;
8066 gchar
*priority
= g_strdup("NORMAL");
8070 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8072 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8081 errx(0 , "Version: %s", version
);
8084 strlcpy(conf
, optarg
, sizeof(conf
));
8087 strlcpy(named_session
, optarg
, sizeof(named_session
));
8108 RB_INIT(&downloads
);
8112 TAILQ_INIT(&aliases
);
8118 gnutls_global_init();
8120 /* generate session keys for xtp pages */
8121 generate_xtp_session_key(&dl_session_key
);
8122 generate_xtp_session_key(&hl_session_key
);
8123 generate_xtp_session_key(&cl_session_key
);
8124 generate_xtp_session_key(&fl_session_key
);
8127 gtk_init(&argc
, &argv
);
8128 if (!g_thread_supported())
8129 g_thread_init(NULL
);
8132 bzero(&sact
, sizeof(sact
));
8133 sigemptyset(&sact
.sa_mask
);
8134 sact
.sa_handler
= sigchild
;
8135 sact
.sa_flags
= SA_NOCLDSTOP
;
8136 sigaction(SIGCHLD
, &sact
, NULL
);
8138 /* set download dir */
8139 pwd
= getpwuid(getuid());
8141 errx(1, "invalid user %d", getuid());
8142 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8144 /* set default string settings */
8145 home
= g_strdup("http://www.peereboom.us");
8146 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8147 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8148 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
8150 /* read config file */
8151 if (strlen(conf
) == 0)
8152 snprintf(conf
, sizeof conf
, "%s/.%s",
8153 pwd
->pw_dir
, XT_CONF_FILE
);
8154 config_parse(conf
, 0);
8156 /* working directory */
8157 if (strlen(work_dir
) == 0)
8158 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8159 pwd
->pw_dir
, XT_DIR
);
8160 if (stat(work_dir
, &sb
)) {
8161 if (mkdir(work_dir
, S_IRWXU
) == -1)
8162 err(1, "mkdir work_dir");
8163 if (stat(work_dir
, &sb
))
8164 err(1, "stat work_dir");
8166 if (S_ISDIR(sb
.st_mode
) == 0)
8167 errx(1, "%s not a dir", work_dir
);
8168 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8169 warnx("fixing invalid permissions on %s", work_dir
);
8170 if (chmod(work_dir
, S_IRWXU
) == -1)
8174 /* icon cache dir */
8175 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8176 if (stat(cache_dir
, &sb
)) {
8177 if (mkdir(cache_dir
, S_IRWXU
) == -1)
8178 err(1, "mkdir cache_dir");
8179 if (stat(cache_dir
, &sb
))
8180 err(1, "stat cache_dir");
8182 if (S_ISDIR(sb
.st_mode
) == 0)
8183 errx(1, "%s not a dir", cache_dir
);
8184 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8185 warnx("fixing invalid permissions on %s", cache_dir
);
8186 if (chmod(cache_dir
, S_IRWXU
) == -1)
8191 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8192 if (stat(certs_dir
, &sb
)) {
8193 if (mkdir(certs_dir
, S_IRWXU
) == -1)
8194 err(1, "mkdir certs_dir");
8195 if (stat(certs_dir
, &sb
))
8196 err(1, "stat certs_dir");
8198 if (S_ISDIR(sb
.st_mode
) == 0)
8199 errx(1, "%s not a dir", certs_dir
);
8200 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8201 warnx("fixing invalid permissions on %s", certs_dir
);
8202 if (chmod(certs_dir
, S_IRWXU
) == -1)
8207 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8208 work_dir
, XT_SESSIONS_DIR
);
8209 if (stat(sessions_dir
, &sb
)) {
8210 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
8211 err(1, "mkdir sessions_dir");
8212 if (stat(sessions_dir
, &sb
))
8213 err(1, "stat sessions_dir");
8215 if (S_ISDIR(sb
.st_mode
) == 0)
8216 errx(1, "%s not a dir", sessions_dir
);
8217 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8218 warnx("fixing invalid permissions on %s", sessions_dir
);
8219 if (chmod(sessions_dir
, S_IRWXU
) == -1)
8222 /* runtime settings that can override config file */
8223 if (runtime_settings
[0] != '\0')
8224 config_parse(runtime_settings
, 1);
8227 if (!strcmp(download_dir
, pwd
->pw_dir
))
8228 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8229 if (stat(download_dir
, &sb
)) {
8230 if (mkdir(download_dir
, S_IRWXU
) == -1)
8231 err(1, "mkdir download_dir");
8232 if (stat(download_dir
, &sb
))
8233 err(1, "stat download_dir");
8235 if (S_ISDIR(sb
.st_mode
) == 0)
8236 errx(1, "%s not a dir", download_dir
);
8237 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8238 warnx("fixing invalid permissions on %s", download_dir
);
8239 if (chmod(download_dir
, S_IRWXU
) == -1)
8243 /* favorites file */
8244 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8245 if (stat(file
, &sb
)) {
8246 warnx("favorites file doesn't exist, creating it");
8247 if ((f
= fopen(file
, "w")) == NULL
)
8248 err(1, "favorites");
8253 session
= webkit_get_default_session();
8254 /* XXX ssl-priority property not quite available yet */
8255 if (is_g_object_setting(G_OBJECT(session
), "ssl-priority"))
8256 g_object_set(G_OBJECT(session
), "ssl-priority", priority
,
8259 warnx("session does not have \"ssl-priority\" property");
8264 if (stat(ssl_ca_file
, &sb
)) {
8265 warn("no CA file: %s", ssl_ca_file
);
8266 g_free(ssl_ca_file
);
8269 g_object_set(session
,
8270 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8271 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8276 env_proxy
= getenv("http_proxy");
8278 setup_proxy(env_proxy
);
8280 setup_proxy(http_proxy
);
8283 send_cmd_to_socket(argv
[0]);
8287 /* set some connection parameters */
8288 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8289 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8292 /* see if there is already an xxxterm running */
8293 if (single_instance
&& is_running()) {
8295 warnx("already running");
8301 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8302 send_cmd_to_socket(cmd
);
8312 /* uri completion */
8313 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8318 if (save_global_history
)
8319 restore_global_history();
8321 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8322 restore_saved_tabs();
8324 a
.s
= named_session
;
8325 a
.i
= XT_SES_DONOTHING
;
8326 open_tabs(NULL
, &a
);
8330 create_new_tab(argv
[0], NULL
, focus
);
8337 if (TAILQ_EMPTY(&tabs
))
8338 create_new_tab(home
, NULL
, 1);
8341 if ((s
= build_socket()) != -1)
8342 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
8346 gnutls_global_deinit();