about summary refs log tree commit diff stats
path: root/settings.c
diff options
context:
space:
mode:
authorMarco Peereboom <marco@conformal.com>2011-11-02 13:45:12 -0500
committerMarco Peereboom <marco@conformal.com>2011-11-02 13:45:12 -0500
commiteaf0a2b218ce43104538559bb9c772d511ce68ad (patch)
treefb42b14a51e5123cdc324bbfab2329ef4a53d4b5 /settings.c
parent1e2fb8e373c651d2cca8c1e00ec4a60f8b1752d8 (diff)
downloadxombrero-eaf0a2b218ce43104538559bb9c772d511ce68ad.tar.gz
More file splits
Add whitelist and settings files.
Diffstat (limited to 'settings.c')
-rw-r--r--settings.c1314
1 files changed, 1314 insertions, 0 deletions
diff --git a/settings.c b/settings.c
new file mode 100644
index 0000000..8ff3ad9
--- /dev/null
+++ b/settings.c
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
+ * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
+ * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
+ * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
+ * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
+ * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "xxxterm.h"
+
+/* globals */
+SoupURI			*proxy_uri = NULL;
+PangoFontDescription	*cmd_font;
+PangoFontDescription	*oops_font;
+PangoFontDescription	*statusbar_font;
+PangoFontDescription	*tabbar_font;
+
+/* settings that require restart */
+int		tabless = 0;	/* allow only 1 tab */
+int		enable_socket = 0;
+int		single_instance = 0; /* only allow one xxxterm to run */
+int		fancy_bar = 1;	/* fancy toolbar */
+int		browser_mode = XT_BM_NORMAL;
+int		enable_localstorage = 1;
+char		*statusbar_elems = NULL;
+
+/* runtime settings */
+int		show_tabs = 1;	/* show tabs on notebook */
+int		tab_style = XT_TABS_NORMAL; /* tab bar style */
+int		show_url = 1;	/* show url toolbar on notebook */
+int		show_statusbar = 0; /* vimperator style status bar */
+int		ctrl_click_focus = 0; /* ctrl click gets focus */
+int		cookies_enabled = 1; /* enable cookies */
+int		read_only_cookies = 0; /* enable to not write cookies */
+int		enable_scripts = 1;
+int		enable_plugins = 1;
+gfloat		default_zoom_level = 1.0;
+char		default_script[PATH_MAX];
+int		window_height = 768;
+int		window_width = 1024;
+int		icon_size = 2; /* 1 = smallest, 2+ = bigger */
+int		refresh_interval = 10; /* download refresh interval */
+int		enable_plugin_whitelist = 0;
+int		enable_cookie_whitelist = 0;
+int		enable_js_whitelist = 0;
+int		session_timeout = 3600; /* cookie session timeout */
+int		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
+char		*ssl_ca_file = NULL;
+char		*resource_dir = NULL;
+gboolean	ssl_strict_certs = FALSE;
+int		append_next = 1; /* append tab after current tab */
+char		*home = NULL;
+char		*search_string = NULL;
+char		*http_proxy = NULL;
+char		download_dir[PATH_MAX];
+char		runtime_settings[PATH_MAX]; /* override of settings */
+int		allow_volatile_cookies = 0;
+int		save_global_history = 0; /* save global history to disk */
+char		*user_agent = NULL;
+int		save_rejected_cookies = 0;
+int		session_autosave = 0;
+int		guess_search = 0;
+int		dns_prefetch = FALSE;
+gint		max_connections = 25;
+gint		max_host_connections = 5;
+gint		enable_spell_checking = 0;
+char		*spell_check_languages = NULL;
+int		xterm_workaround = 0;
+char		*url_regex = NULL;
+int		history_autosave = 0;
+char		search_file[PATH_MAX];
+char		command_file[PATH_MAX];
+char		*encoding = NULL;
+int		autofocus_onload = 0;
+
+char		*cmd_font_name = NULL;
+char		*oops_font_name = NULL;
+char		*statusbar_font_name = NULL;
+char		*tabbar_font_name = NULL;
+
+char		*get_download_dir(struct settings *);
+char		*get_default_script(struct settings *);
+char		*get_runtime_dir(struct settings *);
+char		*get_tab_style(struct settings *);
+char		*get_work_dir(struct settings *);
+
+int		add_cookie_wl(struct settings *, char *);
+int		add_js_wl(struct settings *, char *);
+int		add_pl_wl(struct settings *, char *);
+int		add_mime_type(struct settings *, char *);
+int		add_alias(struct settings *, char *);
+int		add_kb(struct settings *, char *);
+
+int		set_download_dir(struct settings *, char *);
+int		set_default_script(struct settings *, char *);
+int		set_runtime_dir(struct settings *, char *);
+int		set_tab_style(struct settings *, char *);
+int		set_work_dir(struct settings *, char *);
+
+void		walk_mime_type(struct settings *, void (*)(struct settings *,
+		    char *, void *), void *);
+void		walk_alias(struct settings *, void (*)(struct settings *,
+		    char *, void *), void *);
+void		walk_cookie_wl(struct settings *, void (*)(struct settings *,
+		    char *, void *), void *);
+void		walk_js_wl(struct settings *, void (*)(struct settings *,
+		    char *, void *), void *);
+void		walk_pl_wl(struct settings *, void (*)(struct settings *,
+		    char *, void *), void *);
+void		walk_kb(struct settings *, void (*)(struct settings *, char *,
+		    void *), void *);
+
+int
+set_http_proxy(char *proxy)
+{
+	SoupURI			*uri;
+
+	if (proxy == NULL)
+		return (1);
+
+	/* see if we need to clear it instead */
+	if (strlen(proxy) == 0) {
+		setup_proxy(NULL);
+		return (0);
+	}
+
+	uri = soup_uri_new(proxy);
+	if (uri == NULL || !SOUP_URI_VALID_FOR_HTTP(uri))
+		return (1);
+
+	setup_proxy(proxy);
+
+	soup_uri_free(uri);
+
+	return (0);
+}
+
+struct special {
+	int		(*set)(struct settings *, char *);
+	char		*(*get)(struct settings *);
+	void		(*walk)(struct settings *,
+			    void (*cb)(struct settings *, char *, void *),
+			    void *);
+};
+
+struct special		s_browser_mode = {
+	set_browser_mode,
+	get_browser_mode,
+	NULL
+};
+
+struct special		s_cookie = {
+	set_cookie_policy,
+	get_cookie_policy,
+	NULL
+};
+
+struct special		s_alias = {
+	add_alias,
+	NULL,
+	walk_alias
+};
+
+struct special		s_mime = {
+	add_mime_type,
+	NULL,
+	walk_mime_type
+};
+
+struct special		s_js = {
+	add_js_wl,
+	NULL,
+	walk_js_wl
+};
+
+struct special		s_pl = {
+	add_pl_wl,
+	NULL,
+	walk_pl_wl
+};
+
+struct special		s_kb = {
+	add_kb,
+	NULL,
+	walk_kb
+};
+
+struct special		s_cookie_wl = {
+	add_cookie_wl,
+	NULL,
+	walk_cookie_wl
+};
+
+struct special		s_default_script = {
+	set_default_script,
+	get_default_script,
+	NULL
+};
+
+struct special		s_download_dir = {
+	set_download_dir,
+	get_download_dir,
+	NULL
+};
+
+struct special		s_work_dir = {
+	set_work_dir,
+	get_work_dir,
+	NULL
+};
+
+struct special		s_tab_style = {
+	set_tab_style,
+	get_tab_style,
+	NULL
+};
+
+struct settings		rs[] = {
+	{ "allow_volatile_cookies",	XT_S_INT, 0,		&allow_volatile_cookies, NULL, NULL },
+	{ "append_next",		XT_S_INT, 0,		&append_next, NULL, NULL },
+	{ "autofocus_onload",		XT_S_INT, 0,		&autofocus_onload, NULL, NULL },
+	{ "browser_mode",		XT_S_INT, 0, NULL, NULL,&s_browser_mode },
+	{ "cookie_policy",		XT_S_INT, 0, NULL, NULL,&s_cookie },
+	{ "cookies_enabled",		XT_S_INT, 0,		&cookies_enabled, NULL, NULL },
+	{ "ctrl_click_focus",		XT_S_INT, 0,		&ctrl_click_focus, NULL, NULL },
+	{ "default_zoom_level",		XT_S_FLOAT, 0,		NULL, NULL, NULL, &default_zoom_level },
+	{ "default_script",		XT_S_STR, 0, NULL, NULL,&s_default_script },
+	{ "download_dir",		XT_S_STR, 0, NULL, NULL,&s_download_dir },
+	{ "enable_cookie_whitelist",	XT_S_INT, 0,		&enable_cookie_whitelist, NULL, NULL },
+	{ "enable_js_whitelist",	XT_S_INT, 0,		&enable_js_whitelist, NULL, NULL },
+	{ "enable_plugin_whitelist",	XT_S_INT, 0,		&enable_plugin_whitelist, NULL, NULL },
+	{ "enable_localstorage",	XT_S_INT, 0,		&enable_localstorage, NULL, NULL },
+	{ "enable_plugins",		XT_S_INT, 0,		&enable_plugins, NULL, NULL },
+	{ "enable_scripts",		XT_S_INT, 0,		&enable_scripts, NULL, NULL },
+	{ "enable_socket",		XT_S_INT, XT_SF_RESTART,&enable_socket, NULL, NULL },
+	{ "enable_spell_checking",	XT_S_INT, 0,		&enable_spell_checking, NULL, NULL },
+	{ "encoding",			XT_S_STR, 0, NULL,	&encoding, NULL },
+	{ "fancy_bar",			XT_S_INT, XT_SF_RESTART,&fancy_bar, NULL, NULL },
+	{ "guess_search",		XT_S_INT, 0,		&guess_search, NULL, NULL },
+	{ "history_autosave",		XT_S_INT, 0,		&history_autosave, NULL, NULL },
+	{ "home",			XT_S_STR, 0, NULL,	&home, NULL },
+	{ "http_proxy",			XT_S_STR, 0, NULL,	&http_proxy, NULL, NULL, set_http_proxy },
+	{ "icon_size",			XT_S_INT, 0,		&icon_size, NULL, NULL },
+	{ "max_connections",		XT_S_INT, XT_SF_RESTART,&max_connections, NULL, NULL },
+	{ "max_host_connections",	XT_S_INT, XT_SF_RESTART,&max_host_connections, NULL, NULL },
+	{ "read_only_cookies",		XT_S_INT, 0,		&read_only_cookies, NULL, NULL },
+	{ "refresh_interval",		XT_S_INT, 0,		&refresh_interval, NULL, NULL },
+	{ "resource_dir",		XT_S_STR, 0, NULL,	&resource_dir, NULL },
+	{ "search_string",		XT_S_STR, 0, NULL,	&search_string, NULL },
+	{ "save_global_history",	XT_S_INT, XT_SF_RESTART,&save_global_history, NULL, NULL },
+	{ "save_rejected_cookies",	XT_S_INT, XT_SF_RESTART,&save_rejected_cookies, NULL, NULL },
+	{ "session_timeout",		XT_S_INT, 0,		&session_timeout, NULL, NULL },
+	{ "session_autosave",		XT_S_INT, 0,		&session_autosave, NULL, NULL },
+	{ "single_instance",		XT_S_INT, XT_SF_RESTART,&single_instance, NULL, NULL },
+	{ "show_tabs",			XT_S_INT, 0,		&show_tabs, NULL, NULL },
+	{ "show_url",			XT_S_INT, 0,		&show_url, NULL, NULL },
+	{ "show_statusbar",		XT_S_INT, 0,		&show_statusbar, NULL, NULL },
+	{ "spell_check_languages",	XT_S_STR, 0, NULL, &spell_check_languages, NULL },
+	{ "ssl_ca_file",		XT_S_STR, 0, NULL,	&ssl_ca_file, NULL },
+	{ "ssl_strict_certs",		XT_S_INT, 0,		&ssl_strict_certs, NULL, NULL },
+	{ "statusbar_elems",		XT_S_STR, 0, NULL,	&statusbar_elems, NULL },
+	{ "tab_style",			XT_S_STR, 0, NULL, NULL,&s_tab_style },
+	{ "url_regex",			XT_S_STR, 0, NULL,	&url_regex, NULL },
+	{ "user_agent",			XT_S_STR, 0, NULL,	&user_agent, NULL },
+	{ "window_height",		XT_S_INT, 0,		&window_height, NULL, NULL },
+	{ "window_width",		XT_S_INT, 0,		&window_width, NULL, NULL },
+	{ "work_dir",			XT_S_STR, 0, NULL, NULL,&s_work_dir },
+	{ "xterm_workaround",		XT_S_INT, 0,		&xterm_workaround, NULL, NULL },
+
+	/* font settings */
+	{ "cmd_font",			XT_S_STR, 0, NULL, &cmd_font_name, NULL },
+	{ "oops_font",			XT_S_STR, 0, NULL, &oops_font_name, NULL },
+	{ "statusbar_font",		XT_S_STR, 0, NULL, &statusbar_font_name, NULL },
+	{ "tabbar_font",		XT_S_STR, 0, NULL, &tabbar_font_name, NULL },
+
+	/* runtime settings */
+	{ "alias",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_alias },
+	{ "cookie_wl",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_cookie_wl },
+	{ "js_wl",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_js },
+	{ "keybinding",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_kb },
+	{ "mime_type",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_mime },
+	{ "pl_wl",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_pl },
+};
+
+size_t
+get_settings_size(void)
+{
+	return (LENGTH(rs));
+}
+
+char *
+get_setting_name(int i)
+{
+	if (i > LENGTH(rs))
+		return (NULL);
+	return (rs[i].name);
+}
+
+char *
+get_as_string(struct settings *s)
+{
+	char			*r = NULL;
+
+	if (s == NULL)
+		return (NULL);
+
+	if (s->s) {
+		if (s->s->get)
+			r = s->s->get(s);
+		else
+			warnx("get_as_string skip %s\n", s->name);
+	} else if (s->type == XT_S_INT)
+		r = g_strdup_printf("%d", *s->ival);
+	else if (s->type == XT_S_STR)
+		r = g_strdup(*s->sval);
+	else if (s->type == XT_S_FLOAT)
+		r = g_strdup_printf("%f", *s->fval);
+	else
+		r = g_strdup_printf("INVALID TYPE");
+
+	return (r);
+}
+
+void
+settings_walk(void (*cb)(struct settings *, char *, void *), void *cb_args)
+{
+	int			i;
+	char			*s;
+
+	for (i = 0; i < LENGTH(rs); i++) {
+		if (rs[i].s && rs[i].s->walk)
+			rs[i].s->walk(&rs[i], cb, cb_args);
+		else {
+			s = get_as_string(&rs[i]);
+			cb(&rs[i], s, cb_args);
+			g_free(s);
+		}
+	}
+}
+
+int
+set_browser_mode(struct settings *s, char *val)
+{
+	if (!strcmp(val, "whitelist")) {
+		browser_mode = XT_BM_WHITELIST;
+		allow_volatile_cookies	= 0;
+		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
+		cookies_enabled = 1;
+		enable_cookie_whitelist = 1;
+		enable_plugin_whitelist = 1;
+		enable_plugins = 0;
+		read_only_cookies = 0;
+		save_rejected_cookies = 0;
+		session_timeout = 3600;
+		enable_scripts = 0;
+		enable_js_whitelist = 1;
+		enable_localstorage = 0;
+	} else if (!strcmp(val, "normal")) {
+		browser_mode = XT_BM_NORMAL;
+		allow_volatile_cookies = 0;
+		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
+		cookies_enabled = 1;
+		enable_cookie_whitelist = 0;
+		enable_plugin_whitelist = 0;
+		enable_plugins = 1;
+		read_only_cookies = 0;
+		save_rejected_cookies = 0;
+		session_timeout = 3600;
+		enable_scripts = 1;
+		enable_js_whitelist = 0;
+		enable_localstorage = 1;
+	} else if (!strcmp(val, "kiosk")) {
+		browser_mode = XT_BM_KIOSK;
+		allow_volatile_cookies = 0;
+		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
+		cookies_enabled = 1;
+		enable_cookie_whitelist = 0;
+		enable_plugin_whitelist = 0;
+		enable_plugins = 1;
+		read_only_cookies = 0;
+		save_rejected_cookies = 0;
+		session_timeout = 3600;
+		enable_scripts = 1;
+		enable_js_whitelist = 0;
+		enable_localstorage = 1;
+		show_tabs = 0;
+		tabless = 1;
+	} else
+		return (1);
+
+	return (0);
+}
+
+char *
+get_browser_mode(struct settings *s)
+{
+	char			*r = NULL;
+
+	if (browser_mode == XT_BM_WHITELIST)
+		r = g_strdup("whitelist");
+	else if (browser_mode == XT_BM_NORMAL)
+		r = g_strdup("normal");
+	else if (browser_mode == XT_BM_KIOSK)
+		r = g_strdup("kiosk");
+	else
+		return (NULL);
+
+	return (r);
+}
+
+int
+set_cookie_policy(struct settings *s, char *val)
+{
+	if (!strcmp(val, "no3rdparty"))
+		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
+	else if (!strcmp(val, "accept"))
+		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
+	else if (!strcmp(val, "reject"))
+		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_NEVER;
+	else
+		return (1);
+
+	return (0);
+}
+
+char *
+get_cookie_policy(struct settings *s)
+{
+	char			*r = NULL;
+
+	if (cookie_policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY)
+		r = g_strdup("no3rdparty");
+	else if (cookie_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS)
+		r = g_strdup("accept");
+	else if (cookie_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
+		r = g_strdup("reject");
+	else
+		return (NULL);
+
+	return (r);
+}
+
+char *
+get_default_script(struct settings *s)
+{
+	if (default_script[0] == '\0')
+		return (0);
+	return (g_strdup(default_script));
+}
+
+int
+set_default_script(struct settings *s, char *val)
+{
+	if (val[0] == '~')
+		snprintf(default_script, sizeof default_script, "%s/%s",
+		    pwd->pw_dir, &val[1]);
+	else
+		strlcpy(default_script, val, sizeof default_script);
+
+	return (0);
+}
+
+char *
+get_download_dir(struct settings *s)
+{
+	if (download_dir[0] == '\0')
+		return (0);
+	return (g_strdup(download_dir));
+}
+
+int
+set_download_dir(struct settings *s, char *val)
+{
+	if (val[0] == '~')
+		snprintf(download_dir, sizeof download_dir, "%s/%s",
+		    pwd->pw_dir, &val[1]);
+	else
+		strlcpy(download_dir, val, sizeof download_dir);
+
+	return (0);
+}
+
+int
+add_alias(struct settings *s, char *line)
+{
+	char			*l, *alias;
+	struct alias		*a = NULL;
+
+	if (s == NULL || line == NULL) {
+		show_oops(NULL, "add_alias invalid parameters");
+		return (1);
+	}
+
+	l = line;
+	a = g_malloc(sizeof(*a));
+
+	if ((alias = strsep(&l, " \t,")) == NULL || l == NULL) {
+		show_oops(NULL, "add_alias: incomplete alias definition");
+		goto bad;
+	}
+	if (strlen(alias) == 0 || strlen(l) == 0) {
+		show_oops(NULL, "add_alias: invalid alias definition");
+		goto bad;
+	}
+
+	a->a_name = g_strdup(alias);
+	a->a_uri = g_strdup(l);
+
+	DNPRINTF(XT_D_CONFIG, "add_alias: %s for %s\n", a->a_name, a->a_uri);
+
+	TAILQ_INSERT_TAIL(&aliases, a, entry);
+
+	return (0);
+bad:
+	if (a)
+		g_free(a);
+	return (1);
+}
+
+void
+walk_alias(struct settings *s,
+    void (*cb)(struct settings *, char *, void *), void *cb_args)
+{
+	struct alias		*a;
+	char			*str;
+
+	if (s == NULL || cb == NULL) {
+		show_oops(NULL, "walk_alias invalid parameters");
+		return;
+	}
+
+	TAILQ_FOREACH(a, &aliases, entry) {
+		str = g_strdup_printf("%s --> %s", a->a_name, a->a_uri);
+		cb(s, str, cb_args);
+		g_free(str);
+	}
+}
+
+int
+add_mime_type(struct settings *s, char *line)
+{
+	char			*mime_type;
+	char			*l;
+	struct mime_type	*m = NULL;
+	int			downloadfirst = 0;
+
+	/* XXX this could be smarter */
+
+	if (line == NULL || strlen(line) == 0) {
+		show_oops(NULL, "add_mime_type invalid parameters");
+		return (1);
+	}
+
+	l = line;
+	if (*l == '@') {
+		downloadfirst = 1;
+		l++;
+	}
+	m = g_malloc(sizeof(*m));
+
+	if ((mime_type = strsep(&l, " \t,")) == NULL || l == NULL) {
+		show_oops(NULL, "add_mime_type: invalid mime_type");
+		goto bad;
+	}
+	if (mime_type[strlen(mime_type) - 1] == '*') {
+		mime_type[strlen(mime_type) - 1] = '\0';
+		m->mt_default = 1;
+	} else
+		m->mt_default = 0;
+
+	if (strlen(mime_type) == 0 || strlen(l) == 0) {
+		show_oops(NULL, "add_mime_type: invalid mime_type");
+		goto bad;
+	}
+
+	m->mt_type = g_strdup(mime_type);
+	m->mt_action = g_strdup(l);
+	m->mt_download = downloadfirst;
+
+	DNPRINTF(XT_D_CONFIG, "add_mime_type: type %s action %s default %d\n",
+	    m->mt_type, m->mt_action, m->mt_default);
+
+	TAILQ_INSERT_TAIL(&mtl, m, entry);
+
+	return (0);
+bad:
+	if (m)
+		g_free(m);
+	return (1);
+}
+
+void
+walk_mime_type(struct settings *s,
+    void (*cb)(struct settings *, char *, void *), void *cb_args)
+{
+	struct mime_type	*m;
+	char			*str;
+
+	if (s == NULL || cb == NULL) {
+		show_oops(NULL, "walk_mime_type invalid parameters");
+		return;
+	}
+
+	TAILQ_FOREACH(m, &mtl, entry) {
+		str = g_strdup_printf("%s%s --> %s",
+		    m->mt_type,
+		    m->mt_default ? "*" : "",
+		    m->mt_action);
+		cb(s, str, cb_args);
+		g_free(str);
+	}
+}
+
+/* inherent to GTK not all keys will be caught at all times */
+/* XXX sort key bindings */
+struct key_binding	keys[] = {
+	{ "command_mode",	0,	0,	GDK_Escape	},
+	{ "insert_mode",	0,	0,	GDK_i		},
+	{ "cookiejar",		MOD1,	0,	GDK_j		},
+	{ "downloadmgr",	MOD1,	0,	GDK_d		},
+	{ "history",		MOD1,	0,	GDK_h		},
+	{ "print",		CTRL,	0,	GDK_p		},
+	{ "search",		0,	0,	GDK_slash	},
+	{ "searchb",		0,	0,	GDK_question	},
+	{ "statustoggle",	CTRL,	0,	GDK_n		},
+	{ "command",		0,	0,	GDK_colon	},
+	{ "qa",			CTRL,	0,	GDK_q		},
+	{ "restart",		MOD1,	0,	GDK_q		},
+	{ "js toggle",		CTRL,	0,	GDK_j		},
+	{ "cookie toggle",	MOD1,	0,	GDK_c		},
+	{ "togglesrc",		CTRL,	0,	GDK_s		},
+	{ "yankuri",		0,	0,	GDK_y		},
+	{ "pasteuricur",	0,	0,	GDK_p		},
+	{ "pasteurinew",	0,	0,	GDK_P		},
+	{ "toplevel toggle",	0,	0,	GDK_F4		},
+	{ "help",		0,	0,	GDK_F1		},
+	{ "run_script",		MOD1,	0,	GDK_r		},
+
+	/* search */
+	{ "searchnext",		0,	0,	GDK_n		},
+	{ "searchprevious",	0,	0,	GDK_N		},
+
+	/* focus */
+	{ "focusaddress",	0,	0,	GDK_F6		},
+	{ "focussearch",	0,	0,	GDK_F7		},
+
+	/* hinting */
+	{ "hinting",		0,	0,	GDK_f		},
+	{ "hinting",		0,	0,	GDK_period	},
+	{ "hinting_newtab",	SHFT,	0,	GDK_F		},
+	{ "hinting_newtab",	0,	0,	GDK_comma	},
+
+	/* custom stylesheet */
+	{ "userstyle",		0,	0,	GDK_s		},
+
+	/* navigation */
+	{ "goback",		0,	0,	GDK_BackSpace	},
+	{ "goback",		MOD1,	0,	GDK_Left	},
+	{ "goforward",		SHFT,	0,	GDK_BackSpace	},
+	{ "goforward",		MOD1,	0,	GDK_Right	},
+	{ "reload",		0,	0,	GDK_F5		},
+	{ "reload",		CTRL,	0,	GDK_r		},
+	{ "reload",		CTRL,	0,	GDK_l		},
+	{ "favorites",		MOD1,	1,	GDK_f		},
+
+	/* vertical movement */
+	{ "scrolldown",		0,	0,	GDK_j		},
+	{ "scrolldown",		0,	0,	GDK_Down	},
+	{ "scrollup",		0,	0,	GDK_Up		},
+	{ "scrollup",		0,	0,	GDK_k		},
+	{ "scrollbottom",	0,	0,	GDK_G		},
+	{ "scrollbottom",	0,	0,	GDK_End		},
+	{ "scrolltop",		0,	0,	GDK_Home	},
+	{ "scrollpagedown",	0,	0,	GDK_space	},
+	{ "scrollpagedown",	CTRL,	0,	GDK_f		},
+	{ "scrollhalfdown",	CTRL,	0,	GDK_d		},
+	{ "scrollpagedown",	0,	0,	GDK_Page_Down	},
+	{ "scrollpageup",	0,	0,	GDK_Page_Up	},
+	{ "scrollpageup",	CTRL,	0,	GDK_b		},
+	{ "scrollhalfup",	CTRL,	0,	GDK_u		},
+	/* horizontal movement */
+	{ "scrollright",	0,	0,	GDK_l		},
+	{ "scrollright",	0,	0,	GDK_Right	},
+	{ "scrollleft",		0,	0,	GDK_Left	},
+	{ "scrollleft",		0,	0,	GDK_h		},
+	{ "scrollfarright",	0,	0,	GDK_dollar	},
+	{ "scrollfarleft",	0,	0,	GDK_0		},
+
+	/* tabs */
+	{ "tabnew",		CTRL,	0,	GDK_t		},
+	{ "999tabnew",		CTRL,	0,	GDK_T		},
+	{ "tabclose",		CTRL,	1,	GDK_w		},
+	{ "tabundoclose",	0,	0,	GDK_U		},
+	{ "tabnext 1",		CTRL,	0,	GDK_1		},
+	{ "tabnext 2",		CTRL,	0,	GDK_2		},
+	{ "tabnext 3",		CTRL,	0,	GDK_3		},
+	{ "tabnext 4",		CTRL,	0,	GDK_4		},
+	{ "tabnext 5",		CTRL,	0,	GDK_5		},
+	{ "tabnext 6",		CTRL,	0,	GDK_6		},
+	{ "tabnext 7",		CTRL,	0,	GDK_7		},
+	{ "tabnext 8",		CTRL,	0,	GDK_8		},
+	{ "tabnext 9",		CTRL,	0,	GDK_9		},
+	{ "tabfirst",		CTRL,	0,	GDK_less	},
+	{ "tablast",		CTRL,	0,	GDK_greater	},
+	{ "tabprevious",	CTRL,	0,	GDK_Left	},
+	{ "tabnext",		CTRL,	0,	GDK_Right	},
+	{ "focusout",		CTRL,	0,	GDK_minus	},
+	{ "focusin",		CTRL,	0,	GDK_plus	},
+	{ "focusin",		CTRL,	0,	GDK_equal	},
+	{ "focusreset",		CTRL,	0,	GDK_0		},
+
+	/* command aliases (handy when -S flag is used) */
+	{ "promptopen",		0,	0,	GDK_F9		},
+	{ "promptopencurrent",	0,	0,	GDK_F10		},
+	{ "prompttabnew",	0,	0,	GDK_F11		},
+	{ "prompttabnewcurrent",0,	0,	GDK_F12		},
+};
+
+void
+walk_kb(struct settings *s,
+    void (*cb)(struct settings *, char *, void *), void *cb_args)
+{
+	struct key_binding	*k;
+	char			str[1024];
+
+	if (s == NULL || cb == NULL) {
+		show_oops(NULL, "walk_kb invalid parameters");
+		return;
+	}
+
+	TAILQ_FOREACH(k, &kbl, entry) {
+		if (k->cmd == NULL)
+			continue;
+		str[0] = '\0';
+
+		/* sanity */
+		if (gdk_keyval_name(k->key) == NULL)
+			continue;
+
+		strlcat(str, k->cmd, sizeof str);
+		strlcat(str, ",", sizeof str);
+
+		if (k->mask & GDK_SHIFT_MASK)
+			strlcat(str, "S-", sizeof str);
+		if (k->mask & GDK_CONTROL_MASK)
+			strlcat(str, "C-", sizeof str);
+		if (k->mask & GDK_MOD1_MASK)
+			strlcat(str, "M1-", sizeof str);
+		if (k->mask & GDK_MOD2_MASK)
+			strlcat(str, "M2-", sizeof str);
+		if (k->mask & GDK_MOD3_MASK)
+			strlcat(str, "M3-", sizeof str);
+		if (k->mask & GDK_MOD4_MASK)
+			strlcat(str, "M4-", sizeof str);
+		if (k->mask & GDK_MOD5_MASK)
+			strlcat(str, "M5-", sizeof str);
+
+		strlcat(str, gdk_keyval_name(k->key), sizeof str);
+		cb(s, str, cb_args);
+	}
+}
+
+void
+init_keybindings(void)
+{
+	int			i;
+	struct key_binding	*k;
+
+	for (i = 0; i < LENGTH(keys); i++) {
+		k = g_malloc0(sizeof *k);
+		k->cmd = keys[i].cmd;
+		k->mask = keys[i].mask;
+		k->use_in_entry = keys[i].use_in_entry;
+		k->key = keys[i].key;
+		TAILQ_INSERT_HEAD(&kbl, k, entry);
+
+		DNPRINTF(XT_D_KEYBINDING, "init_keybindings: added: %s\n",
+		    k->cmd ? k->cmd : "unnamed key");
+	}
+}
+
+void
+keybinding_clearall(void)
+{
+	struct key_binding	*k, *next;
+
+	for (k = TAILQ_FIRST(&kbl); k; k = next) {
+		next = TAILQ_NEXT(k, entry);
+		if (k->cmd == NULL)
+			continue;
+
+		DNPRINTF(XT_D_KEYBINDING, "keybinding_clearall: %s\n",
+		    k->cmd ? k->cmd : "unnamed key");
+		TAILQ_REMOVE(&kbl, k, entry);
+		g_free(k);
+	}
+}
+
+int
+keybinding_add(char *cmd, char *key, int use_in_entry)
+{
+	struct key_binding	*k;
+	guint			keyval, mask = 0;
+	int			i;
+
+	DNPRINTF(XT_D_KEYBINDING, "keybinding_add: %s %s\n", cmd, key);
+
+	/* Keys which are to be used in entry have been prefixed with an
+	 * exclamation mark. */
+	if (use_in_entry)
+		key++;
+
+	/* find modifier keys */
+	if (strstr(key, "S-"))
+		mask |= GDK_SHIFT_MASK;
+	if (strstr(key, "C-"))
+		mask |= GDK_CONTROL_MASK;
+	if (strstr(key, "M1-"))
+		mask |= GDK_MOD1_MASK;
+	if (strstr(key, "M2-"))
+		mask |= GDK_MOD2_MASK;
+	if (strstr(key, "M3-"))
+		mask |= GDK_MOD3_MASK;
+	if (strstr(key, "M4-"))
+		mask |= GDK_MOD4_MASK;
+	if (strstr(key, "M5-"))
+		mask |= GDK_MOD5_MASK;
+
+	/* find keyname */
+	for (i = strlen(key) - 1; i > 0; i--)
+		if (key[i] == '-')
+			key = &key[i + 1];
+
+	/* validate keyname */
+	keyval = gdk_keyval_from_name(key);
+	if (keyval == GDK_VoidSymbol) {
+		warnx("invalid keybinding name %s", key);
+		return (1);
+	}
+	/* must run this test too, gtk+ doesn't handle 10 for example */
+	if (gdk_keyval_name(keyval) == NULL) {
+		warnx("invalid keybinding name %s", key);
+		return (1);
+	}
+
+	/* Remove eventual dupes. */
+	TAILQ_FOREACH(k, &kbl, entry)
+		if (k->key == keyval && k->mask == mask) {
+			TAILQ_REMOVE(&kbl, k, entry);
+			g_free(k);
+			break;
+		}
+
+	/* add keyname */
+	k = g_malloc0(sizeof *k);
+	k->cmd = g_strdup(cmd);
+	k->mask = mask;
+	k->use_in_entry = use_in_entry;
+	k->key = keyval;
+
+	DNPRINTF(XT_D_KEYBINDING, "keybinding_add: %s 0x%x %d 0x%x\n",
+	    k->cmd,
+	    k->mask,
+	    k->use_in_entry,
+	    k->key);
+	DNPRINTF(XT_D_KEYBINDING, "keybinding_add: adding: %s %s\n",
+	    k->cmd, gdk_keyval_name(keyval));
+
+	TAILQ_INSERT_HEAD(&kbl, k, entry);
+
+	return (0);
+}
+
+int
+add_kb(struct settings *s, char *entry)
+{
+	char			*kb, *key;
+
+	DNPRINTF(XT_D_KEYBINDING, "add_kb: %s\n", entry);
+
+	/* clearall is special */
+	if (!strcmp(entry, "clearall")) {
+		keybinding_clearall();
+		return (0);
+	}
+
+	kb = strstr(entry, ",");
+	if (kb == NULL)
+		return (1);
+	*kb = '\0';
+	key = kb + 1;
+
+	return (keybinding_add(entry, key, key[0] == '!'));
+}
+
+void
+setup_proxy(char *uri)
+{
+	if (proxy_uri) {
+		g_object_set(session, "proxy_uri", NULL, (char *)NULL);
+		soup_uri_free(proxy_uri);
+		proxy_uri = NULL;
+	}
+	if (http_proxy) {
+		if (http_proxy != uri) {
+			g_free(http_proxy);
+			http_proxy = NULL;
+		}
+	}
+
+	if (uri) {
+		http_proxy = g_strdup(uri);
+		DNPRINTF(XT_D_CONFIG, "setup_proxy: %s\n", uri);
+		proxy_uri = soup_uri_new(http_proxy);
+		if (!(proxy_uri == NULL || !SOUP_URI_VALID_FOR_HTTP(proxy_uri)))
+			g_object_set(session, "proxy-uri", proxy_uri,
+			    (char *)NULL);
+	}
+}
+
+char *
+get_tab_style(struct settings *s)
+{
+	if (tab_style == XT_TABS_NORMAL)
+		return (g_strdup("normal"));
+	else
+		return (g_strdup("compact"));
+}
+
+int
+set_tab_style(struct settings *s, char *val)
+{
+	if (!strcmp(val, "normal"))
+		tab_style = XT_TABS_NORMAL;
+	else if (!strcmp(val, "compact"))
+		tab_style = XT_TABS_COMPACT;
+	else
+		return (1);
+
+	return (0);
+}
+
+char *
+get_work_dir(struct settings *s)
+{
+	if (work_dir[0] == '\0')
+		return (0);
+	return (g_strdup(work_dir));
+}
+
+int
+set_work_dir(struct settings *s, char *val)
+{
+	if (val[0] == '~')
+		snprintf(work_dir, sizeof work_dir, "%s/%s",
+		    pwd->pw_dir, &val[1]);
+	else
+		strlcpy(work_dir, val, sizeof work_dir);
+
+	return (0);
+}
+
+void
+walk_cookie_wl(struct settings *s,
+    void (*cb)(struct settings *, char *, void *), void *cb_args)
+{
+	struct domain		*d;
+
+	if (s == NULL || cb == NULL) {
+		show_oops(NULL, "walk_cookie_wl invalid parameters");
+		return;
+	}
+
+	RB_FOREACH_REVERSE(d, domain_list, &c_wl)
+		cb(s, d->d, cb_args);
+}
+
+void
+walk_js_wl(struct settings *s,
+    void (*cb)(struct settings *, char *, void *), void *cb_args)
+{
+	struct domain		*d;
+
+	if (s == NULL || cb == NULL) {
+		show_oops(NULL, "walk_js_wl invalid parameters");
+		return;
+	}
+
+	RB_FOREACH_REVERSE(d, domain_list, &js_wl)
+		cb(s, d->d, cb_args);
+}
+
+void
+walk_pl_wl(struct settings *s,
+    void (*cb)(struct settings *, char *, void *), void *cb_args)
+{
+	struct domain		*d;
+
+	if (s == NULL || cb == NULL) {
+		show_oops(NULL, "walk_pl_wl invalid parameters");
+		return;
+	}
+
+	RB_FOREACH_REVERSE(d, domain_list, &pl_wl)
+		cb(s, d->d, cb_args);
+}
+
+int
+settings_add(char *var, char *val)
+{
+	int i, rv, *p;
+	gfloat *f;
+	char **s;
+
+	/* get settings */
+	for (i = 0, rv = 0; i < LENGTH(rs); i++) {
+		if (strcmp(var, rs[i].name))
+			continue;
+
+		if (rs[i].s) {
+			if (rs[i].s->set(&rs[i], val))
+				errx(1, "invalid value for %s: %s", var, val);
+			rv = 1;
+			break;
+		} else
+			switch (rs[i].type) {
+			case XT_S_INT:
+				p = rs[i].ival;
+				*p = atoi(val);
+				rv = 1;
+				break;
+			case XT_S_STR:
+				s = rs[i].sval;
+				if (s == NULL)
+					errx(1, "invalid sval for %s",
+					    rs[i].name);
+				if (*s)
+					g_free(*s);
+				*s = g_strdup(val);
+				rv = 1;
+				break;
+			case XT_S_FLOAT:
+				f = rs[i].fval;
+				*f = atof(val);
+				rv = 1;
+				break;
+			case XT_S_INVALID:
+			default:
+				errx(1, "invalid type for %s", var);
+			}
+		break;
+	}
+	return (rv);
+}
+
+#define WS	"\n= \t"
+void
+config_parse(char *filename, int runtime)
+{
+	FILE			*config, *f;
+	char			*line, *cp, *var, *val;
+	size_t			len, lineno = 0;
+	int			handled;
+	char			file[PATH_MAX];
+	struct stat		sb;
+
+	DNPRINTF(XT_D_CONFIG, "config_parse: filename %s\n", filename);
+
+	if (filename == NULL)
+		return;
+
+	if (runtime && runtime_settings[0] != '\0') {
+		snprintf(file, sizeof file, "%s/%s",
+		    work_dir, runtime_settings);
+		if (stat(file, &sb)) {
+			warnx("runtime file doesn't exist, creating it");
+			if ((f = fopen(file, "w")) == NULL)
+				err(1, "runtime");
+			fprintf(f, "# AUTO GENERATED, DO NOT EDIT\n");
+			fclose(f);
+		}
+	} else
+		strlcpy(file, filename, sizeof file);
+
+	if ((config = fopen(file, "r")) == NULL) {
+		warn("config_parse: cannot open %s", filename);
+		return;
+	}
+
+	for (;;) {
+		if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
+			if (feof(config) || ferror(config))
+				break;
+
+		cp = line;
+		cp += (long)strspn(cp, WS);
+		if (cp[0] == '\0') {
+			/* empty line */
+			free(line);
+			continue;
+		}
+
+		if ((var = strsep(&cp, WS)) == NULL || cp == NULL)
+			startpage_add("invalid configuration file entry: %s",
+			    line);
+		else {
+			cp += (long)strspn(cp, WS);
+
+			if ((val = strsep(&cp, "\0")) == NULL)
+				break;
+
+			DNPRINTF(XT_D_CONFIG, "config_parse: %s=%s\n",
+			    var, val);
+			handled = settings_add(var, val);
+
+			if (handled == 0)
+				startpage_add("invalid configuration file entry"
+				    ": %s=%s", var, val);
+		}
+
+		free(line);
+	}
+
+	fclose(config);
+}
+
+struct settings_args {
+	char		**body;
+	int		i;
+};
+
+void
+print_setting(struct settings *s, char *val, void *cb_args)
+{
+	char			*tmp, *color;
+	struct settings_args	*sa = cb_args;
+
+	if (sa == NULL)
+		return;
+
+	if (s->flags & XT_SF_RUNTIME)
+		color = "#22cc22";
+	else
+		color = "#cccccc";
+
+	tmp = *sa->body;
+	*sa->body = g_strdup_printf(
+	    "%s\n<tr>"
+	    "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
+	    "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
+	    *sa->body,
+	    color,
+	    s->name,
+	    color,
+	    val
+	    );
+	g_free(tmp);
+	sa->i++;
+}
+
+int
+set_show(struct tab *t, struct karg *args)
+{
+	char			*body, *page, *tmp;
+	int			i = 1;
+	struct settings_args	sa;
+
+	bzero(&sa, sizeof sa);
+	sa.body = &body;
+
+	/* body */
+	body = g_strdup_printf("<div align='center'><table><tr>"
+	    "<th align='left'>Setting</th>"
+	    "<th align='left'>Value</th></tr>\n");
+
+	settings_walk(print_setting, &sa);
+	i = sa.i;
+
+	/* small message if there are none */
+	if (i == 1) {
+		tmp = body;
+		body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
+		    "colspan='2'>No settings</td></tr>\n", body);
+		g_free(tmp);
+	}
+
+	tmp = body;
+	body = g_strdup_printf("%s</table></div>", body);
+	g_free(tmp);
+
+	page = get_html_page("Settings", body, "", 0);
+
+	g_free(body);
+
+	load_webkit_string(t, page, XT_URI_ABOUT_SET);
+
+	g_free(page);
+
+	return (XT_CB_PASSTHROUGH);
+}
+
+int
+set(struct tab *t, struct karg *args)
+{
+	char			*p, *val;
+	int			i;
+
+	if (args == NULL || args->s == NULL)
+		return (set_show(t, args));
+
+	/* strip spaces */
+	p = g_strstrip(args->s);
+
+	if (strlen(p) == 0)
+		return (set_show(t, args));
+
+	/* we got some sort of string */
+	val = g_strrstr(p, "=");
+	if (val) {
+		*val++ = '\0';
+		val = g_strchomp(val);
+		p = g_strchomp(p);
+
+		for (i = 0; i < get_settings_size(); i++) {
+			if (strcmp(rs[i].name, p))
+				continue;
+
+			if (rs[i].activate) {
+				if (rs[i].activate(val))
+					show_oops(t, "%s invalid value %s",
+					    p, val);
+				else
+					show_oops(t, ":set %s = %s", p, val);
+				goto done;
+			} else {
+				show_oops(t, "not a runtime option: %s", p);
+				goto done;
+			}
+		}
+		show_oops(t, "unknown option: %s", p);
+	} else {
+		p = g_strchomp(p);
+
+		for (i = 0; i < get_settings_size(); i++) {
+			if (strcmp(rs[i].name, p))
+				continue;
+
+			/* XXX this could use some cleanup */
+			switch (rs[i].type) {
+			case XT_S_INT:
+				if (rs[i].ival)
+					show_oops(t, "%s = %d",
+					    rs[i].name, *rs[i].ival);
+				else if (rs[i].s && rs[i].s->get)
+					show_oops(t, "%s = %s",
+					    rs[i].name,
+					    rs[i].s->get(&rs[i]));
+				else if (rs[i].s && rs[i].s->get == NULL)
+					show_oops(t, "%s = ...", rs[i].name);
+				else
+					show_oops(t, "%s = ", rs[i].name);
+				break;
+			case XT_S_FLOAT:
+				if (rs[i].fval)
+					show_oops(t, "%s = %f",
+					    rs[i].name, *rs[i].fval);
+				else if (rs[i].s && rs[i].s->get)
+					show_oops(t, "%s = %s",
+					    rs[i].name,
+					    rs[i].s->get(&rs[i]));
+				else if (rs[i].s && rs[i].s->get == NULL)
+					show_oops(t, "%s = ...", rs[i].name);
+				else
+					show_oops(t, "%s = ", rs[i].name);
+				break;
+			case XT_S_STR:
+				if (rs[i].sval && *rs[i].sval)
+					show_oops(t, "%s = %s",
+					    rs[i].name, *rs[i].sval);
+				else if (rs[i].s && rs[i].s->get)
+					show_oops(t, "%s = %s",
+					    rs[i].name,
+					    rs[i].s->get(&rs[i]));
+				else if (rs[i].s && rs[i].s->get == NULL)
+					show_oops(t, "%s = ...", rs[i].name);
+				else
+					show_oops(t, "%s = ", rs[i].name);
+				break;
+			default:
+				show_oops(t, "unknown type for %s", rs[i].name);
+				goto done;
+			}
+
+			goto done;
+		}
+		show_oops(t, "unknown option: %s", p);
+	}
+done:
+	return (XT_CB_PASSTHROUGH);
+}
+