about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTodd T. Fries <todd@fries.net>2011-11-01 16:10:31 -0500
committerTodd T. Fries <todd@fries.net>2011-11-01 16:10:31 -0500
commitd831e178524c1f187a96cfdb3738e63e023d6258 (patch)
tree4827a6dce7b5a9372470eea73196db115d6ed340
parente562a275ba9277803a4a6fe89e71545268986a7b (diff)
downloadxombrero-d831e178524c1f187a96cfdb3738e63e023d6258.tar.gz
move some xtp functions over, RB_* prevented all
-rw-r--r--Makefile6
-rw-r--r--xtp.c689
-rw-r--r--xxxterm.c979
-rw-r--r--xxxterm.h99
4 files changed, 938 insertions, 835 deletions
diff --git a/Makefile b/Makefile
index 9ea2cc1..281ec62 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,11 @@ BINDIR=${PREFIX}/bin
 PROG=xxxterm
 MAN=xxxterm.1
 
-SRCS= whitelist.c inspector.c marco.c xxxterm.c
+SRCS+= xtp.c
+SRCS+= whitelist.c
+SRCS+= inspector.c
+SRCS+= marco.c
+SRCS+= xxxterm.c
 CFLAGS+= -O2
 DEBUG= -ggdb3
 LDADD= -lutil -lgcrypt
diff --git a/xtp.c b/xtp.c
new file mode 100644
index 0000000..20acbd1
--- /dev/null
+++ b/xtp.c
@@ -0,0 +1,689 @@
+/*
+ * Copyright (c) 2011 Conformal Systems LLC <info@conformal.com>
+ * Copyright (c) 2011 Marco Peereboom <marco@peereboom.us>
+ *
+ * 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"
+
+/*
+ * xxxterm "protocol" (xtp)
+ * We use this for managing stuff like downloads and favorites. They
+ * make magical HTML pages in memory which have xxxt:// links in order
+ * to communicate with xxxterm's internals. These links take the format:
+ * xxxt://class/session_key/action/arg
+ *
+ * Don't begin xtp class/actions as 0. atoi returns that on error.
+ *
+ * Typically we have not put addition of items in this framework, as
+ * adding items is either done via an ex-command or via a keybinding instead.
+ */
+
+int			updating_fl_tabs = 0;
+int			updating_hl_tabs = 0;
+
+char			*dl_session_key;	/* downloads */
+char			*hl_session_key;	/* history list */
+char			*cl_session_key;	/* cookie list */
+char			*fl_session_key;	/* favorites list */
+
+#define XT_XTP_STR		"xxxt://"
+
+/* XTP classes (xxxt://<class>) */
+#define XT_XTP_INVALID		0	/* invalid */
+#define XT_XTP_DL		1	/* downloads */
+#define XT_XTP_HL		2	/* history */
+#define XT_XTP_CL		3	/* cookies */
+#define XT_XTP_FL		4	/* favorites */
+
+/* XTP download actions */
+#define XT_XTP_DL_LIST		1
+#define XT_XTP_DL_CANCEL	2
+#define XT_XTP_DL_REMOVE	3
+
+/* XTP history actions */
+#define XT_XTP_HL_LIST		1
+#define XT_XTP_HL_REMOVE	2
+
+/* XTP cookie actions */
+#define XT_XTP_CL_LIST		1
+#define XT_XTP_CL_REMOVE	2
+
+/* XTP cookie actions */
+#define XT_XTP_FL_LIST		1
+#define XT_XTP_FL_REMOVE	2
+
+/* xtp tab meanings  - identifies which tabs have xtp pages in (corresponding to about_list indices) */
+#define XT_XTP_TAB_MEANING_NORMAL	-1 /* normal url */
+#define XT_XTP_TAB_MEANING_BL		1 /* about:blank in this tab */
+#define XT_XTP_TAB_MEANING_CL		4 /* cookie manager in this tab */
+#define XT_XTP_TAB_MEANING_DL		5 /* download manager in this tab */
+#define XT_XTP_TAB_MEANING_FL		6 /* favorite manager in this tab */
+#define XT_XTP_TAB_MEANING_HL		8 /* history manager in this tab */
+
+int	xtp_page_fl(struct tab *, struct karg *);
+void	update_cookie_tabs(struct tab *);
+
+/*
+ * generate a session key to secure xtp commands.
+ * pass in a ptr to the key in question and it will
+ * be modified in place.
+ */
+void
+generate_xtp_session_key(char **key)
+{
+	uint8_t			rand_bytes[XT_XTP_SES_KEY_SZ];
+
+	/* free old key */
+	if (*key)
+		g_free(*key);
+
+	/* make a new one */
+	arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
+	*key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
+	    rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
+	    rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
+
+	DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
+}
+
+/*
+ * validate a xtp session key.
+ * return (1) if OK
+ */
+int
+validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted)
+{
+	if (strcmp(trusted, untrusted) != 0) {
+		show_oops(t, "%s: xtp session key mismatch possible spoof",
+		    __func__);
+		return (0);
+	}
+
+	return (1);
+}
+
+/*
+ * update all favorite tabs apart from one. Pass NULL if
+ * you want to update all.
+ */
+void
+update_favorite_tabs(struct tab *apart_from)
+{
+	struct tab			*t;
+	if (!updating_fl_tabs) {
+		updating_fl_tabs = 1; /* stop infinite recursion */
+		TAILQ_FOREACH(t, &tabs, entry)
+			if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
+			    && (t != apart_from))
+				xtp_page_fl(t, NULL);
+		updating_fl_tabs = 0;
+	}
+}
+
+
+/* show a list of favorites (bookmarks) */
+int
+xtp_page_fl(struct tab *t, struct karg *args)
+{
+	char			file[PATH_MAX];
+	FILE			*f;
+	char			*uri = NULL, *title = NULL;
+	size_t			len, lineno = 0;
+	int			i, failed = 0;
+	char			*body, *tmp, *page = NULL;
+	const char		delim[3] = {'\\', '\\', '\0'};
+
+	DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
+
+	if (t == NULL)
+		warn("%s: bad param", __func__);
+
+	/* new session key */
+	if (!updating_fl_tabs)
+		generate_xtp_session_key(&fl_session_key);
+
+	/* open favorites */
+	snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
+	if ((f = fopen(file, "r")) == NULL) {
+		show_oops(t, "Can't open favorites file: %s", strerror(errno));
+		return (1);
+	}
+
+	/* body */
+	body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
+	    "<th style='width: 40px'>&#35;</th><th>Link</th>"
+	    "<th style='width: 40px'>Rm</th></tr>\n");
+
+	for (i = 1;;) {
+		if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
+			break;
+		if (strlen(title) == 0 || title[0] == '#') {
+			free(title);
+			title = NULL;
+			continue;
+		}
+
+		if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
+			if (feof(f) || ferror(f)) {
+				show_oops(t, "favorites file corrupt");
+				failed = 1;
+				break;
+			}
+
+		tmp = body;
+		body = g_strdup_printf("%s<tr>"
+		    "<td>%d</td>"
+		    "<td><a href='%s'>%s</a></td>"
+		    "<td style='text-align: center'>"
+		    "<a href='%s%d/%s/%d/%d'>X</a></td>"
+		    "</tr>\n",
+		    body, i, uri, title,
+		    XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
+
+		g_free(tmp);
+
+		free(uri);
+		uri = NULL;
+		free(title);
+		title = NULL;
+		i++;
+	}
+	fclose(f);
+
+	/* if none, say so */
+	if (i == 1) {
+		tmp = body;
+		body = g_strdup_printf("%s<tr>"
+		    "<td colspan='3' style='text-align: center'>"
+		    "No favorites - To add one use the 'favadd' command."
+		    "</td></tr>", body);
+		g_free(tmp);
+	}
+
+	tmp = body;
+	body = g_strdup_printf("%s</table>", body);
+	g_free(tmp);
+
+	if (uri)
+		free(uri);
+	if (title)
+		free(title);
+
+	/* render */
+	if (!failed) {
+		page = get_html_page("Favorites", body, "", 1);
+		load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
+		g_free(page);
+	}
+
+	update_favorite_tabs(t);
+
+	if (body)
+		g_free(body);
+
+	return (failed);
+}
+
+/*
+ * Return a new string with a download row (in html)
+ * appended. Old string is freed.
+ */
+char *
+xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
+{
+
+	WebKitDownloadStatus	stat;
+	char			*status_html = NULL, *cmd_html = NULL, *new_html;
+	gdouble			progress;
+	char			cur_sz[FMT_SCALED_STRSIZE];
+	char			tot_sz[FMT_SCALED_STRSIZE];
+	char			*xtp_prefix;
+
+	DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
+
+	/* All actions wil take this form:
+	 * xxxt://class/seskey
+	 */
+	xtp_prefix = g_strdup_printf("%s%d/%s/",
+	    XT_XTP_STR, XT_XTP_DL, dl_session_key);
+
+	stat = webkit_download_get_status(dl->download);
+
+	switch (stat) {
+	case WEBKIT_DOWNLOAD_STATUS_FINISHED:
+		status_html = g_strdup_printf("Finished");
+		cmd_html = g_strdup_printf(
+		    "<a href='%s%d/%d'>Remove</a>",
+		    xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
+		break;
+	case WEBKIT_DOWNLOAD_STATUS_STARTED:
+		/* gather size info */
+		progress = 100 * webkit_download_get_progress(dl->download);
+
+		fmt_scaled(
+		    webkit_download_get_current_size(dl->download), cur_sz);
+		fmt_scaled(
+		    webkit_download_get_total_size(dl->download), tot_sz);
+
+		status_html = g_strdup_printf(
+		    "<div style='width: 100%%' align='center'>"
+		    "<div class='progress-outer'>"
+		    "<div class='progress-inner' style='width: %.2f%%'>"
+		    "</div></div></div>"
+		    "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
+		    progress, cur_sz, tot_sz, progress);
+
+		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
+		    xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
+
+		break;
+		/* LLL */
+	case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
+		status_html = g_strdup_printf("Cancelled");
+		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
+		    xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
+		break;
+	case WEBKIT_DOWNLOAD_STATUS_ERROR:
+		status_html = g_strdup_printf("Error!");
+		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
+		    xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
+		break;
+	case WEBKIT_DOWNLOAD_STATUS_CREATED:
+		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
+		    xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
+		status_html = g_strdup_printf("Starting");
+		break;
+	default:
+		show_oops(t, "%s: unknown download status", __func__);
+	};
+
+	new_html = g_strdup_printf(
+	    "%s\n<tr><td>%s</td><td>%s</td>"
+	    "<td style='text-align:center'>%s</td></tr>\n",
+	    html, basename((char *)webkit_download_get_destination_uri(dl->download)),
+	    status_html, cmd_html);
+	g_free(html);
+
+	if (status_html)
+		g_free(status_html);
+
+	if (cmd_html)
+		g_free(cmd_html);
+
+	g_free(xtp_prefix);
+
+	return new_html;
+}
+
+/* cookie management XTP page */
+int
+xtp_page_cl(struct tab *t, struct karg *args)
+{
+	char			*body, *page, *tmp;
+	int			i = 1; /* all ids start 1 */
+	GSList			*sc, *pc, *pc_start;
+	SoupCookie		*c;
+	char			*type, *table_headers, *last_domain;
+
+	DNPRINTF(XT_D_CMD, "%s", __func__);
+
+	if (t == NULL) {
+		show_oops(NULL, "%s invalid parameters", __func__);
+		return (1);
+	}
+
+	/* Generate a new session key */
+	if (!updating_cl_tabs)
+		generate_xtp_session_key(&cl_session_key);
+
+	/* table headers */
+	table_headers = g_strdup_printf("<table><tr>"
+	    "<th>Type</th>"
+	    "<th>Name</th>"
+	    "<th style='width:200px'>Value</th>"
+	    "<th>Path</th>"
+	    "<th>Expires</th>"
+	    "<th>Secure</th>"
+	    "<th>HTTP<br />only</th>"
+	    "<th style='width:40px'>Rm</th></tr>\n");
+
+	sc = soup_cookie_jar_all_cookies(s_cookiejar);
+	pc = soup_cookie_jar_all_cookies(p_cookiejar);
+	pc_start = pc;
+
+	body = NULL;
+	last_domain = strdup("");
+	for (; sc; sc = sc->next) {
+		c = sc->data;
+
+		if (strcmp(last_domain, c->domain) != 0) {
+			/* new domain */
+			free(last_domain);
+			last_domain = strdup(c->domain);
+
+			if (body != NULL) {
+				tmp = body;
+				body = g_strdup_printf("%s</table>"
+				    "<h2>%s</h2>%s\n",
+				    body, c->domain, table_headers);
+				g_free(tmp);
+			} else {
+				/* first domain */
+				body = g_strdup_printf("<h2>%s</h2>%s\n",
+				    c->domain, table_headers);
+			}
+		}
+
+		type = "Session";
+		for (pc = pc_start; pc; pc = pc->next)
+			if (soup_cookie_equal(pc->data, c)) {
+				type = "Session + Persistent";
+				break;
+			}
+
+		tmp = body;
+		body = g_strdup_printf(
+		    "%s\n<tr>"
+		    "<td>%s</td>"
+		    "<td style='word-wrap:normal'>%s</td>"
+		    "<td>"
+		    "  <textarea rows='4'>%s</textarea>"
+		    "</td>"
+		    "<td>%s</td>"
+		    "<td>%s</td>"
+		    "<td>%d</td>"
+		    "<td>%d</td>"
+		    "<td style='text-align:center'>"
+		    "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
+		    body,
+		    type,
+		    c->name,
+		    c->value,
+		    c->path,
+		    c->expires ?
+		        soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
+		    c->secure,
+		    c->http_only,
+
+		    XT_XTP_STR,
+		    XT_XTP_CL,
+		    cl_session_key,
+		    XT_XTP_CL_REMOVE,
+		    i
+		    );
+
+		g_free(tmp);
+		i++;
+	}
+
+	soup_cookies_free(sc);
+	soup_cookies_free(pc);
+
+	/* small message if there are none */
+	if (i == 1) {
+		body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
+		    "colspan='8'>No Cookies</td></tr>\n", table_headers);
+	}
+	tmp = body;
+	body = g_strdup_printf("%s</table>", body);
+	g_free(tmp);
+
+	page = get_html_page("Cookie Jar", body, "", TRUE);
+	g_free(body);
+	g_free(table_headers);
+	g_free(last_domain);
+
+	load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
+	update_cookie_tabs(t);
+
+	g_free(page);
+
+	return (0);
+}
+
+/* remove a favorite */
+void
+remove_favorite(struct tab *t, int index)
+{
+	char			file[PATH_MAX], *title, *uri = NULL;
+	char			*new_favs, *tmp;
+	FILE			*f;
+	int			i;
+	size_t			len, lineno;
+
+	/* open favorites */
+	snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
+
+	if ((f = fopen(file, "r")) == NULL) {
+		show_oops(t, "%s: can't open favorites: %s",
+		    __func__, strerror(errno));
+		return;
+	}
+
+	/* build a string which will become the new favroites file */
+	new_favs = g_strdup("");
+
+	for (i = 1;;) {
+		if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
+			if (feof(f) || ferror(f))
+				break;
+		/* XXX THIS IS NOT THE RIGHT HEURISTIC */
+		if (len == 0) {
+			free(title);
+			title = NULL;
+			continue;
+		}
+
+		if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
+			if (feof(f) || ferror(f)) {
+				show_oops(t, "%s: can't parse favorites %s",
+				    __func__, strerror(errno));
+				goto clean;
+			}
+		}
+
+		/* as long as this isn't the one we are deleting add to file */
+		if (i != index) {
+			tmp = new_favs;
+			new_favs = g_strdup_printf("%s%s\n%s\n",
+			    new_favs, title, uri);
+			g_free(tmp);
+		}
+
+		free(uri);
+		uri = NULL;
+		free(title);
+		title = NULL;
+		i++;
+	}
+	fclose(f);
+
+	/* write back new favorites file */
+	if ((f = fopen(file, "w")) == NULL) {
+		show_oops(t, "%s: can't open favorites: %s",
+		    __func__, strerror(errno));
+		goto clean;
+	}
+
+	if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
+		show_oops(t, "%s: can't fwrite"); /* shut gcc up */
+	fclose(f);
+
+clean:
+	if (uri)
+		free(uri);
+	if (title)
+		free(title);
+
+	g_free(new_favs);
+}
+
+void
+xtp_handle_fl(struct tab *t, uint8_t cmd, int arg)
+{
+	switch (cmd) {
+	case XT_XTP_FL_LIST:
+		/* nothing, just the below call to xtp_page_fl() */
+		break;
+	case XT_XTP_FL_REMOVE:
+		remove_favorite(t, arg);
+		break;
+	default:
+		show_oops(t, "%s: invalid favorites command", __func__);
+		break;
+	};
+
+	xtp_page_fl(t, NULL);
+}
+
+void
+xtp_handle_cl(struct tab *t, uint8_t cmd, int arg)
+{
+	switch (cmd) {
+	case XT_XTP_CL_LIST:
+		/* nothing, just xtp_page_cl() */
+		break;
+	case XT_XTP_CL_REMOVE:
+		remove_cookie(arg);
+		break;
+	default:
+		show_oops(t, "%s: unknown cookie xtp command", __func__);
+		break;
+	};
+
+	xtp_page_cl(t, NULL);
+}
+
+/* link an XTP class to it's session key and handler function */
+struct xtp_despatch {
+	uint8_t			xtp_class;
+	char			**session_key;
+	void			(*handle_func)(struct tab *, uint8_t, int);
+};
+
+struct xtp_despatch		xtp_despatches[] = {
+	{ XT_XTP_DL, &dl_session_key, xtp_handle_dl },
+	{ XT_XTP_HL, &hl_session_key, xtp_handle_hl },
+	{ XT_XTP_FL, &fl_session_key, xtp_handle_fl },
+	{ XT_XTP_CL, &cl_session_key, xtp_handle_cl },
+	{ XT_XTP_INVALID, NULL, NULL }
+};
+
+/*
+ * is the url xtp protocol? (xxxt://)
+ * if so, parse and despatch correct bahvior
+ */
+int
+parse_xtp_url(struct tab *t, const char *url)
+{
+	char			*dup = NULL, *p, *last = NULL;
+	uint8_t			n_tokens = 0;
+	char			*tokens[4] = {NULL, NULL, NULL, ""};
+	struct xtp_despatch	*dsp, *dsp_match = NULL;
+	uint8_t			req_class;
+	int			ret = FALSE;
+
+	/*
+	 * tokens array meaning:
+	 *   tokens[0] = class
+	 *   tokens[1] = session key
+	 *   tokens[2] = action
+	 *   tokens[3] = optional argument
+	 */
+
+	DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url);
+
+	if (strncmp(url, XT_XTP_STR, strlen(XT_XTP_STR)))
+		goto clean;
+
+	dup = g_strdup(url + strlen(XT_XTP_STR));
+
+	/* split out the url */
+	for ((p = strtok_r(dup, "/", &last)); p;
+	    (p = strtok_r(NULL, "/", &last))) {
+		if (n_tokens < 4)
+			tokens[n_tokens++] = p;
+	}
+
+	/* should be atleast three fields 'class/seskey/command/arg' */
+	if (n_tokens < 3)
+		goto clean;
+
+	dsp = xtp_despatches;
+	req_class = atoi(tokens[0]);
+	while (dsp->xtp_class) {
+		if (dsp->xtp_class == req_class) {
+			dsp_match = dsp;
+			break;
+		}
+		dsp++;
+	}
+
+	/* did we find one atall? */
+	if (dsp_match == NULL) {
+		show_oops(t, "%s: no matching xtp despatch found", __func__);
+		goto clean;
+	}
+
+	/* check session key and call despatch function */
+	if (validate_xtp_session_key(t, *(dsp_match->session_key), tokens[1])) {
+		ret = TRUE; /* all is well, this was a valid xtp request */
+		dsp_match->handle_func(t, atoi(tokens[2]), atoi(tokens[3]));
+	}
+
+clean:
+	if (dup)
+		g_free(dup);
+
+	return (ret);
+}
+
+/*
+ * update all cookie tabs apart from one. Pass NULL if
+ * you want to update all.
+ */
+void
+update_cookie_tabs(struct tab *apart_from)
+{
+	struct tab			*t;
+	if (!updating_cl_tabs) {
+		updating_cl_tabs = 1; /* stop infinite recursion */
+		TAILQ_FOREACH(t, &tabs, entry)
+			if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
+			    && (t != apart_from))
+				xtp_page_cl(t, NULL);
+		updating_cl_tabs = 0;
+	}
+}
+
+/*
+ * update all history tabs apart from one. Pass NULL if
+ * you want to update all.
+ */
+void
+update_history_tabs(struct tab *apart_from)
+{
+	struct tab			*t;
+
+	if (!updating_hl_tabs) {
+		updating_hl_tabs = 1; /* stop infinite recursion */
+		TAILQ_FOREACH(t, &tabs, entry)
+			if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
+			    && (t != apart_from))
+				xtp_page_hl(t, NULL);
+		updating_hl_tabs = 0;
+	}
+}
+
diff --git a/xxxterm.c b/xxxterm.c
index 6394d22..9c12bff 100644
--- a/xxxterm.c
+++ b/xxxterm.c
@@ -57,11 +57,6 @@ char		*icons[] = {
 	"xxxtermicon128.png"
 };
 
-struct history {
-	RB_ENTRY(history)	entry;
-	const gchar		*uri;
-	const gchar		*title;
-};
 RB_HEAD(history_list, history);
 
 struct session {
@@ -70,12 +65,6 @@ struct session {
 };
 TAILQ_HEAD(session_list, session);
 
-struct download {
-	RB_ENTRY(download)	entry;
-	int			id;
-	WebKitDownload		*download;
-	struct tab		*tab;
-};
 RB_HEAD(download_list, download);
 
 
@@ -103,58 +92,6 @@ TAILQ_HEAD(command_list, command_entry);
 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
 int				next_download_id = 1;
 
-/* defines */
-#define XT_NAME			("XXXTerm")
-#define XT_DIR			(".xxxterm")
-#define XT_CACHE_DIR		("cache")
-#define XT_CERT_DIR		("certs/")
-#define XT_SESSIONS_DIR		("sessions/")
-#define XT_CONF_FILE		("xxxterm.conf")
-#define XT_FAVS_FILE		("favorites")
-#define XT_QMARKS_FILE		("quickmarks")
-#define XT_SAVED_TABS_FILE	("main_session")
-#define XT_RESTART_TABS_FILE	("restart_tabs")
-#define XT_SOCKET_FILE		("socket")
-#define XT_HISTORY_FILE		("history")
-#define XT_REJECT_FILE		("rejected.txt")
-#define XT_COOKIE_FILE		("cookies.txt")
-#define XT_SAVE_SESSION_ID	("SESSION_NAME=")
-#define XT_SEARCH_FILE		("search_history")
-#define XT_COMMAND_FILE		("command_history")
-#define XT_CB_HANDLED		(TRUE)
-#define XT_CB_PASSTHROUGH	(FALSE)
-#define XT_DOCTYPE		"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
-#define XT_HTML_TAG		"<html xmlns='http://www.w3.org/1999/xhtml'>\n"
-#define XT_DLMAN_REFRESH	"10"
-#define XT_PAGE_STYLE		"<style type='text/css'>\n"		\
-				"td{overflow: hidden;"			\
-				" padding: 2px 2px 2px 2px;"		\
-				" border: 1px solid black;"		\
-				" vertical-align:top;"			\
-				" word-wrap: break-word}\n"		\
-				"tr:hover{background: #ffff99}\n"	\
-				"th{background-color: #cccccc;"		\
-				" border: 1px solid black}\n"		\
-				"table{width: 100%%;"			\
-				" border: 1px black solid;"		\
-				" border-collapse:collapse}\n"		\
-				".progress-outer{"			\
-				"border: 1px solid black;"		\
-				" height: 8px;"				\
-				" width: 90%%}\n"			\
-				".progress-inner{float: left;"		\
-				" height: 8px;"				\
-				" background: green}\n"			\
-				".dlstatus{font-size: small;"		\
-				" text-align: center}\n"		\
-				"</style>\n"
-#define XT_MAX_URL_LENGTH	(4096) /* 1 page is atomic, don't make bigger */
-#define XT_MAX_UNDO_CLOSE_TAB	(32)
-#define XT_RESERVED_CHARS	"$&+,/:;=?@ \"<>#%%{}|^~[]`"
-#define XT_PRINT_EXTRA_MARGIN	10
-#define XT_URL_REGEX		("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
-#define XT_INVALID_MARK		(-1) /* XXX this is a double, maybe use something else, like a nan */
-
 /* colors */
 #define XT_COLOR_RED		"#cc0000"
 #define XT_COLOR_YELLOW		"#ffff66"
@@ -588,7 +525,6 @@ const char *		marco_message(int *);
 int			xtp_page_cl(struct tab *, struct karg *);
 int			xtp_page_dl(struct tab *, struct karg *);
 int			xtp_page_fl(struct tab *, struct karg *);
-int			xtp_page_hl(struct tab *, struct karg *);
 void			xt_icon_from_file(struct tab *, char *);
 const gchar		*get_uri(struct tab *);
 const gchar		*get_title(struct tab *, bool);
@@ -643,9 +579,7 @@ struct command_entry	*history_at;
 struct command_entry	*search_at;
 int			undo_count;
 int			updating_dl_tabs = 0;
-int			updating_hl_tabs = 0;
 int			updating_cl_tabs = 0;
-int			updating_fl_tabs = 0;
 int			cmd_history_count = 0;
 int			search_history_count = 0;
 char			*global_search;
@@ -1153,19 +1087,6 @@ set_download_dir(struct settings *s, char *val)
 	return (0);
 }
 
-/*
- * Session IDs.
- * We use these to prevent people putting xxxt:// URLs on
- * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
- */
-#define XT_XTP_SES_KEY_SZ	8
-#define XT_XTP_SES_KEY_HEX_FMT  \
-	"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
-char			*dl_session_key;	/* downloads */
-char			*hl_session_key;	/* history list */
-char			*cl_session_key;	/* cookie list */
-char			*fl_session_key;	/* favorites list */
-
 char			work_dir[PATH_MAX];
 char			certs_dir[PATH_MAX];
 char			cache_dir[PATH_MAX];
@@ -1236,46 +1157,6 @@ set_tab_style(struct settings *s, char *val)
 
 	return (0);
 }
-
-/*
- * generate a session key to secure xtp commands.
- * pass in a ptr to the key in question and it will
- * be modified in place.
- */
-void
-generate_xtp_session_key(char **key)
-{
-	uint8_t			rand_bytes[XT_XTP_SES_KEY_SZ];
-
-	/* free old key */
-	if (*key)
-		g_free(*key);
-
-	/* make a new one */
-	arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
-	*key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
-	    rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
-	    rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
-
-	DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
-}
-
-/*
- * validate a xtp session key.
- * return (1) if OK
- */
-int
-validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted)
-{
-	if (strcmp(trusted, untrusted) != 0) {
-		show_oops(t, "%s: xtp session key mismatch possible spoof",
-		    __func__);
-		return (0);
-	}
-
-	return (1);
-}
-
 int
 download_rb_cmp(struct download *e1, struct download *e2)
 {
@@ -2539,127 +2420,6 @@ startpage_add(const char *fmt, ...)
 	TAILQ_INSERT_TAIL(&spl, s, entry);
 }
 
-/*
- * update all favorite tabs apart from one. Pass NULL if
- * you want to update all.
- */
-void
-update_favorite_tabs(struct tab *apart_from)
-{
-	struct tab			*t;
-	if (!updating_fl_tabs) {
-		updating_fl_tabs = 1; /* stop infinite recursion */
-		TAILQ_FOREACH(t, &tabs, entry)
-			if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
-			    && (t != apart_from))
-				xtp_page_fl(t, NULL);
-		updating_fl_tabs = 0;
-	}
-}
-
-/* show a list of favorites (bookmarks) */
-int
-xtp_page_fl(struct tab *t, struct karg *args)
-{
-	char			file[PATH_MAX];
-	FILE			*f;
-	char			*uri = NULL, *title = NULL;
-	size_t			len, lineno = 0;
-	int			i, failed = 0;
-	char			*body, *tmp, *page = NULL;
-	const char		delim[3] = {'\\', '\\', '\0'};
-
-	DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
-
-	if (t == NULL)
-		warn("%s: bad param", __func__);
-
-	/* new session key */
-	if (!updating_fl_tabs)
-		generate_xtp_session_key(&fl_session_key);
-
-	/* open favorites */
-	snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
-	if ((f = fopen(file, "r")) == NULL) {
-		show_oops(t, "Can't open favorites file: %s", strerror(errno));
-		return (1);
-	}
-
-	/* body */
-	body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
-	    "<th style='width: 40px'>&#35;</th><th>Link</th>"
-	    "<th style='width: 40px'>Rm</th></tr>\n");
-
-	for (i = 1;;) {
-		if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
-			break;
-		if (strlen(title) == 0 || title[0] == '#') {
-			free(title);
-			title = NULL;
-			continue;
-		}
-
-		if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
-			if (feof(f) || ferror(f)) {
-				show_oops(t, "favorites file corrupt");
-				failed = 1;
-				break;
-			}
-
-		tmp = body;
-		body = g_strdup_printf("%s<tr>"
-		    "<td>%d</td>"
-		    "<td><a href='%s'>%s</a></td>"
-		    "<td style='text-align: center'>"
-		    "<a href='%s%d/%s/%d/%d'>X</a></td>"
-		    "</tr>\n",
-		    body, i, uri, title,
-		    XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
-
-		g_free(tmp);
-
-		free(uri);
-		uri = NULL;
-		free(title);
-		title = NULL;
-		i++;
-	}
-	fclose(f);
-
-	/* if none, say so */
-	if (i == 1) {
-		tmp = body;
-		body = g_strdup_printf("%s<tr>"
-		    "<td colspan='3' style='text-align: center'>"
-		    "No favorites - To add one use the 'favadd' command."
-		    "</td></tr>", body);
-		g_free(tmp);
-	}
-
-	tmp = body;
-	body = g_strdup_printf("%s</table>", body);
-	g_free(tmp);
-
-	if (uri)
-		free(uri);
-	if (title)
-		free(title);
-
-	/* render */
-	if (!failed) {
-		page = get_html_page("Favorites", body, "", 1);
-		load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
-		g_free(page);
-	}
-
-	update_favorite_tabs(t);
-
-	if (body)
-		g_free(body);
-
-	return (failed);
-}
-
 void
 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
     size_t cert_count, char *title)
@@ -3866,98 +3626,6 @@ command(struct tab *t, struct karg *args)
 
 	return (XT_CB_HANDLED);
 }
-
-/*
- * Return a new string with a download row (in html)
- * appended. Old string is freed.
- */
-char *
-xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
-{
-
-	WebKitDownloadStatus	stat;
-	char			*status_html = NULL, *cmd_html = NULL, *new_html;
-	gdouble			progress;
-	char			cur_sz[FMT_SCALED_STRSIZE];
-	char			tot_sz[FMT_SCALED_STRSIZE];
-	char			*xtp_prefix;
-
-	DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
-
-	/* All actions wil take this form:
-	 * xxxt://class/seskey
-	 */
-	xtp_prefix = g_strdup_printf("%s%d/%s/",
-	    XT_XTP_STR, XT_XTP_DL, dl_session_key);
-
-	stat = webkit_download_get_status(dl->download);
-
-	switch (stat) {
-	case WEBKIT_DOWNLOAD_STATUS_FINISHED:
-		status_html = g_strdup_printf("Finished");
-		cmd_html = g_strdup_printf(
-		    "<a href='%s%d/%d'>Remove</a>",
-		    xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
-		break;
-	case WEBKIT_DOWNLOAD_STATUS_STARTED:
-		/* gather size info */
-		progress = 100 * webkit_download_get_progress(dl->download);
-
-		fmt_scaled(
-		    webkit_download_get_current_size(dl->download), cur_sz);
-		fmt_scaled(
-		    webkit_download_get_total_size(dl->download), tot_sz);
-
-		status_html = g_strdup_printf(
-		    "<div style='width: 100%%' align='center'>"
-		    "<div class='progress-outer'>"
-		    "<div class='progress-inner' style='width: %.2f%%'>"
-		    "</div></div></div>"
-		    "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
-		    progress, cur_sz, tot_sz, progress);
-
-		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
-		    xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
-
-		break;
-		/* LLL */
-	case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
-		status_html = g_strdup_printf("Cancelled");
-		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
-		    xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
-		break;
-	case WEBKIT_DOWNLOAD_STATUS_ERROR:
-		status_html = g_strdup_printf("Error!");
-		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
-		    xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
-		break;
-	case WEBKIT_DOWNLOAD_STATUS_CREATED:
-		cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
-		    xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
-		status_html = g_strdup_printf("Starting");
-		break;
-	default:
-		show_oops(t, "%s: unknown download status", __func__);
-	};
-
-	new_html = g_strdup_printf(
-	    "%s\n<tr><td>%s</td><td>%s</td>"
-	    "<td style='text-align:center'>%s</td></tr>\n",
-	    html, basename((char *)webkit_download_get_destination_uri(dl->download)),
-	    status_html, cmd_html);
-	g_free(html);
-
-	if (status_html)
-		g_free(status_html);
-
-	if (cmd_html)
-		g_free(cmd_html);
-
-	g_free(xtp_prefix);
-
-	return new_html;
-}
-
 /*
  * update all download tabs apart from one. Pass NULL if
  * you want to update all.
@@ -3976,315 +3644,6 @@ update_download_tabs(struct tab *apart_from)
 	}
 }
 
-/*
- * update all cookie tabs apart from one. Pass NULL if
- * you want to update all.
- */
-void
-update_cookie_tabs(struct tab *apart_from)
-{
-	struct tab			*t;
-	if (!updating_cl_tabs) {
-		updating_cl_tabs = 1; /* stop infinite recursion */
-		TAILQ_FOREACH(t, &tabs, entry)
-			if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
-			    && (t != apart_from))
-				xtp_page_cl(t, NULL);
-		updating_cl_tabs = 0;
-	}
-}
-
-/*
- * update all history tabs apart from one. Pass NULL if
- * you want to update all.
- */
-void
-update_history_tabs(struct tab *apart_from)
-{
-	struct tab			*t;
-
-	if (!updating_hl_tabs) {
-		updating_hl_tabs = 1; /* stop infinite recursion */
-		TAILQ_FOREACH(t, &tabs, entry)
-			if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
-			    && (t != apart_from))
-				xtp_page_hl(t, NULL);
-		updating_hl_tabs = 0;
-	}
-}
-
-/* cookie management XTP page */
-int
-xtp_page_cl(struct tab *t, struct karg *args)
-{
-	char			*body, *page, *tmp;
-	int			i = 1; /* all ids start 1 */
-	GSList			*sc, *pc, *pc_start;
-	SoupCookie		*c;
-	char			*type, *table_headers, *last_domain;
-
-	DNPRINTF(XT_D_CMD, "%s", __func__);
-
-	if (t == NULL) {
-		show_oops(NULL, "%s invalid parameters", __func__);
-		return (1);
-	}
-
-	/* Generate a new session key */
-	if (!updating_cl_tabs)
-		generate_xtp_session_key(&cl_session_key);
-
-	/* table headers */
-	table_headers = g_strdup_printf("<table><tr>"
-	    "<th>Type</th>"
-	    "<th>Name</th>"
-	    "<th style='width:200px'>Value</th>"
-	    "<th>Path</th>"
-	    "<th>Expires</th>"
-	    "<th>Secure</th>"
-	    "<th>HTTP<br />only</th>"
-	    "<th style='width:40px'>Rm</th></tr>\n");
-
-	sc = soup_cookie_jar_all_cookies(s_cookiejar);
-	pc = soup_cookie_jar_all_cookies(p_cookiejar);
-	pc_start = pc;
-
-	body = NULL;
-	last_domain = strdup("");
-	for (; sc; sc = sc->next) {
-		c = sc->data;
-
-		if (strcmp(last_domain, c->domain) != 0) {
-			/* new domain */
-			free(last_domain);
-			last_domain = strdup(c->domain);
-
-			if (body != NULL) {
-				tmp = body;
-				body = g_strdup_printf("%s</table>"
-				    "<h2>%s</h2>%s\n",
-				    body, c->domain, table_headers);
-				g_free(tmp);
-			} else {
-				/* first domain */
-				body = g_strdup_printf("<h2>%s</h2>%s\n",
-				    c->domain, table_headers);
-			}
-		}
-
-		type = "Session";
-		for (pc = pc_start; pc; pc = pc->next)
-			if (soup_cookie_equal(pc->data, c)) {
-				type = "Session + Persistent";
-				break;
-			}
-
-		tmp = body;
-		body = g_strdup_printf(
-		    "%s\n<tr>"
-		    "<td>%s</td>"
-		    "<td style='word-wrap:normal'>%s</td>"
-		    "<td>"
-		    "  <textarea rows='4'>%s</textarea>"
-		    "</td>"
-		    "<td>%s</td>"
-		    "<td>%s</td>"
-		    "<td>%d</td>"
-		    "<td>%d</td>"
-		    "<td style='text-align:center'>"
-		    "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
-		    body,
-		    type,
-		    c->name,
-		    c->value,
-		    c->path,
-		    c->expires ?
-		        soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
-		    c->secure,
-		    c->http_only,
-
-		    XT_XTP_STR,
-		    XT_XTP_CL,
-		    cl_session_key,
-		    XT_XTP_CL_REMOVE,
-		    i
-		    );
-
-		g_free(tmp);
-		i++;
-	}
-
-	soup_cookies_free(sc);
-	soup_cookies_free(pc);
-
-	/* small message if there are none */
-	if (i == 1) {
-		body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
-		    "colspan='8'>No Cookies</td></tr>\n", table_headers);
-	}
-	tmp = body;
-	body = g_strdup_printf("%s</table>", body);
-	g_free(tmp);
-
-	page = get_html_page("Cookie Jar", body, "", TRUE);
-	g_free(body);
-	g_free(table_headers);
-	g_free(last_domain);
-
-	load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
-	update_cookie_tabs(t);
-
-	g_free(page);
-
-	return (0);
-}
-
-int
-xtp_page_hl(struct tab *t, struct karg *args)
-{
-	char			*body, *page, *tmp;
-	struct history		*h;
-	int			i = 1; /* all ids start 1 */
-
-	DNPRINTF(XT_D_CMD, "%s", __func__);
-
-	if (t == NULL) {
-		show_oops(NULL, "%s invalid parameters", __func__);
-		return (1);
-	}
-
-	/* Generate a new session key */
-	if (!updating_hl_tabs)
-		generate_xtp_session_key(&hl_session_key);
-
-	/* body */
-	body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
-	    "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
-
-	RB_FOREACH_REVERSE(h, history_list, &hl) {
-		tmp = body;
-		body = g_strdup_printf(
-		    "%s\n<tr>"
-		    "<td><a href='%s'>%s</a></td>"
-		    "<td>%s</td>"
-		    "<td style='text-align: center'>"
-		    "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
-		    body, h->uri, h->uri, h->title,
-		    XT_XTP_STR, XT_XTP_HL, hl_session_key,
-		    XT_XTP_HL_REMOVE, i);
-
-		g_free(tmp);
-		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='3'>No History</td></tr>\n", body);
-		g_free(tmp);
-	}
-
-	tmp = body;
-	body = g_strdup_printf("%s</table>", body);
-	g_free(tmp);
-
-	page = get_html_page("History", body, "", TRUE);
-	g_free(body);
-
-	/*
-	 * update all history manager tabs as the xtp session
-	 * key has now changed. No need to update the current tab.
-	 * Already did that above.
-	 */
-	update_history_tabs(t);
-
-	load_webkit_string(t, page, XT_URI_ABOUT_HISTORY);
-	g_free(page);
-
-	return (0);
-}
-
-/*
- * Generate a web page detailing the status of any downloads
- */
-int
-xtp_page_dl(struct tab *t, struct karg *args)
-{
-	struct download		*dl;
-	char			*body, *page, *tmp;
-	char			*ref;
-	int			n_dl = 1;
-
-	DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
-
-	if (t == NULL) {
-		show_oops(NULL, "%s invalid parameters", __func__);
-		return (1);
-	}
-
-	/*
-	 * Generate a new session key for next page instance.
-	 * This only happens for the top level call to xtp_page_dl()
-	 * in which case updating_dl_tabs is 0.
-	 */
-	if (!updating_dl_tabs)
-		generate_xtp_session_key(&dl_session_key);
-
-	/* header - with refresh so as to update */
-	if (refresh_interval >= 1)
-		ref = g_strdup_printf(
-		    "<meta http-equiv='refresh' content='%u"
-		    ";url=%s%d/%s/%d' />\n",
-		    refresh_interval,
-		    XT_XTP_STR,
-		    XT_XTP_DL,
-		    dl_session_key,
-		    XT_XTP_DL_LIST);
-	else
-		ref = g_strdup("");
-
-	body = g_strdup_printf("<div align='center'>"
-	    "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
-	    "</p><table><tr><th style='width: 60%%'>"
-	    "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
-	    XT_XTP_STR, XT_XTP_DL, dl_session_key, XT_XTP_DL_LIST);
-
-	RB_FOREACH_REVERSE(dl, download_list, &downloads) {
-		body = xtp_page_dl_row(t, body, dl);
-		n_dl++;
-	}
-
-	/* message if no downloads in list */
-	if (n_dl == 1) {
-		tmp = body;
-		body = g_strdup_printf("%s\n<tr><td colspan='3'"
-		    " style='text-align: center'>"
-		    "No downloads</td></tr>\n", body);
-		g_free(tmp);
-	}
-
-	tmp = body;
-	body = g_strdup_printf("%s</table></div>", body);
-	g_free(tmp);
-
-	page = get_html_page("Downloads", body, ref, 1);
-	g_free(ref);
-	g_free(body);
-
-	/*
-	 * update all download manager tabs as the xtp session
-	 * key has now changed. No need to update the current tab.
-	 * Already did that above.
-	 */
-	update_download_tabs(t);
-
-	load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
-	g_free(page);
-
-	return (0);
-}
-
 int
 search(struct tab *t, struct karg *args)
 {
@@ -5392,119 +4751,6 @@ xtp_handle_hl(struct tab *t, uint8_t cmd, int id)
 	xtp_page_hl(t, NULL);
 }
 
-/* remove a favorite */
-void
-remove_favorite(struct tab *t, int index)
-{
-	char			file[PATH_MAX], *title, *uri = NULL;
-	char			*new_favs, *tmp;
-	FILE			*f;
-	int			i;
-	size_t			len, lineno;
-
-	/* open favorites */
-	snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
-
-	if ((f = fopen(file, "r")) == NULL) {
-		show_oops(t, "%s: can't open favorites: %s",
-		    __func__, strerror(errno));
-		return;
-	}
-
-	/* build a string which will become the new favroites file */
-	new_favs = g_strdup("");
-
-	for (i = 1;;) {
-		if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
-			if (feof(f) || ferror(f))
-				break;
-		/* XXX THIS IS NOT THE RIGHT HEURISTIC */
-		if (len == 0) {
-			free(title);
-			title = NULL;
-			continue;
-		}
-
-		if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
-			if (feof(f) || ferror(f)) {
-				show_oops(t, "%s: can't parse favorites %s",
-				    __func__, strerror(errno));
-				goto clean;
-			}
-		}
-
-		/* as long as this isn't the one we are deleting add to file */
-		if (i != index) {
-			tmp = new_favs;
-			new_favs = g_strdup_printf("%s%s\n%s\n",
-			    new_favs, title, uri);
-			g_free(tmp);
-		}
-
-		free(uri);
-		uri = NULL;
-		free(title);
-		title = NULL;
-		i++;
-	}
-	fclose(f);
-
-	/* write back new favorites file */
-	if ((f = fopen(file, "w")) == NULL) {
-		show_oops(t, "%s: can't open favorites: %s",
-		    __func__, strerror(errno));
-		goto clean;
-	}
-
-	if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
-		show_oops(t, "%s: can't fwrite"); /* shut gcc up */
-	fclose(f);
-
-clean:
-	if (uri)
-		free(uri);
-	if (title)
-		free(title);
-
-	g_free(new_favs);
-}
-
-void
-xtp_handle_fl(struct tab *t, uint8_t cmd, int arg)
-{
-	switch (cmd) {
-	case XT_XTP_FL_LIST:
-		/* nothing, just the below call to xtp_page_fl() */
-		break;
-	case XT_XTP_FL_REMOVE:
-		remove_favorite(t, arg);
-		break;
-	default:
-		show_oops(t, "%s: invalid favorites command", __func__);
-		break;
-	};
-
-	xtp_page_fl(t, NULL);
-}
-
-void
-xtp_handle_cl(struct tab *t, uint8_t cmd, int arg)
-{
-	switch (cmd) {
-	case XT_XTP_CL_LIST:
-		/* nothing, just xtp_page_cl() */
-		break;
-	case XT_XTP_CL_REMOVE:
-		remove_cookie(arg);
-		break;
-	default:
-		show_oops(t, "%s: unknown cookie xtp command", __func__);
-		break;
-	};
-
-	xtp_page_cl(t, NULL);
-}
-
 /* link an XTP class to it's session key and handler function */
 struct xtp_despatch {
 	uint8_t			xtp_class;
@@ -5512,85 +4758,6 @@ struct xtp_despatch {
 	void			(*handle_func)(struct tab *, uint8_t, int);
 };
 
-struct xtp_despatch		xtp_despatches[] = {
-	{ XT_XTP_DL, &dl_session_key, xtp_handle_dl },
-	{ XT_XTP_HL, &hl_session_key, xtp_handle_hl },
-	{ XT_XTP_FL, &fl_session_key, xtp_handle_fl },
-	{ XT_XTP_CL, &cl_session_key, xtp_handle_cl },
-	{ XT_XTP_INVALID, NULL, NULL }
-};
-
-/*
- * is the url xtp protocol? (xxxt://)
- * if so, parse and despatch correct bahvior
- */
-int
-parse_xtp_url(struct tab *t, const char *url)
-{
-	char			*dup = NULL, *p, *last = NULL;
-	uint8_t			n_tokens = 0;
-	char			*tokens[4] = {NULL, NULL, NULL, ""};
-	struct xtp_despatch	*dsp, *dsp_match = NULL;
-	uint8_t			req_class;
-	int			ret = FALSE;
-
-	/*
-	 * tokens array meaning:
-	 *   tokens[0] = class
-	 *   tokens[1] = session key
-	 *   tokens[2] = action
-	 *   tokens[3] = optional argument
-	 */
-
-	DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url);
-
-	if (strncmp(url, XT_XTP_STR, strlen(XT_XTP_STR)))
-		goto clean;
-
-	dup = g_strdup(url + strlen(XT_XTP_STR));
-
-	/* split out the url */
-	for ((p = strtok_r(dup, "/", &last)); p;
-	    (p = strtok_r(NULL, "/", &last))) {
-		if (n_tokens < 4)
-			tokens[n_tokens++] = p;
-	}
-
-	/* should be atleast three fields 'class/seskey/command/arg' */
-	if (n_tokens < 3)
-		goto clean;
-
-	dsp = xtp_despatches;
-	req_class = atoi(tokens[0]);
-	while (dsp->xtp_class) {
-		if (dsp->xtp_class == req_class) {
-			dsp_match = dsp;
-			break;
-		}
-		dsp++;
-	}
-
-	/* did we find one atall? */
-	if (dsp_match == NULL) {
-		show_oops(t, "%s: no matching xtp despatch found", __func__);
-		goto clean;
-	}
-
-	/* check session key and call despatch function */
-	if (validate_xtp_session_key(t, *(dsp_match->session_key), tokens[1])) {
-		ret = TRUE; /* all is well, this was a valid xtp request */
-		dsp_match->handle_func(t, atoi(tokens[2]), atoi(tokens[3]));
-	}
-
-clean:
-	if (dup)
-		g_free(dup);
-
-	return (ret);
-}
-
-
-
 void
 activate_uri_entry_cb(GtkWidget* entry, struct tab *t)
 {
@@ -9890,3 +9057,149 @@ main(int argc, char *argv[])
 
 	return (0);
 }
+
+int
+xtp_page_hl(struct tab *t, struct karg *args)
+{
+	char			*body, *page, *tmp;
+	struct history		*h;
+	int			i = 1; /* all ids start 1 */
+
+	DNPRINTF(XT_D_CMD, "%s", __func__);
+
+	if (t == NULL) {
+		show_oops(NULL, "%s invalid parameters", __func__);
+		return (1);
+	}
+
+	/* Generate a new session key */
+	if (!updating_hl_tabs)
+		generate_xtp_session_key(&hl_session_key);
+
+	/* body */
+	body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
+	    "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
+
+	RB_FOREACH_REVERSE(h, history_list, &hl) {
+		tmp = body;
+		body = g_strdup_printf(
+		    "%s\n<tr>"
+		    "<td><a href='%s'>%s</a></td>"
+		    "<td>%s</td>"
+		    "<td style='text-align: center'>"
+		    "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
+		    body, h->uri, h->uri, h->title,
+		    XT_XTP_STR, XT_XTP_HL, hl_session_key,
+		    XT_XTP_HL_REMOVE, i);
+
+		g_free(tmp);
+		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='3'>No History</td></tr>\n", body);
+		g_free(tmp);
+	}
+
+	tmp = body;
+	body = g_strdup_printf("%s</table>", body);
+	g_free(tmp);
+
+	page = get_html_page("History", body, "", TRUE);
+	g_free(body);
+
+	/*
+	 * update all history manager tabs as the xtp session
+	 * key has now changed. No need to update the current tab.
+	 * Already did that above.
+	 */
+	update_history_tabs(t);
+
+	load_webkit_string(t, page, XT_URI_ABOUT_HISTORY);
+	g_free(page);
+
+	return (0);
+}
+
+/*
+ * Generate a web page detailing the status of any downloads
+ */
+int
+xtp_page_dl(struct tab *t, struct karg *args)
+{
+	struct download		*dl;
+	char			*body, *page, *tmp;
+	char			*ref;
+	int			n_dl = 1;
+
+	DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
+
+	if (t == NULL) {
+		show_oops(NULL, "%s invalid parameters", __func__);
+		return (1);
+	}
+
+	/*
+	 * Generate a new session key for next page instance.
+	 * This only happens for the top level call to xtp_page_dl()
+	 * in which case updating_dl_tabs is 0.
+	 */
+	if (!updating_dl_tabs)
+		generate_xtp_session_key(&dl_session_key);
+
+	/* header - with refresh so as to update */
+	if (refresh_interval >= 1)
+		ref = g_strdup_printf(
+		    "<meta http-equiv='refresh' content='%u"
+		    ";url=%s%d/%s/%d' />\n",
+		    refresh_interval,
+		    XT_XTP_STR,
+		    XT_XTP_DL,
+		    dl_session_key,
+		    XT_XTP_DL_LIST);
+	else
+		ref = g_strdup("");
+
+	body = g_strdup_printf("<div align='center'>"
+	    "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
+	    "</p><table><tr><th style='width: 60%%'>"
+	    "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
+	    XT_XTP_STR, XT_XTP_DL, dl_session_key, XT_XTP_DL_LIST);
+
+	RB_FOREACH_REVERSE(dl, download_list, &downloads) {
+		body = xtp_page_dl_row(t, body, dl);
+		n_dl++;
+	}
+
+	/* message if no downloads in list */
+	if (n_dl == 1) {
+		tmp = body;
+		body = g_strdup_printf("%s\n<tr><td colspan='3'"
+		    " style='text-align: center'>"
+		    "No downloads</td></tr>\n", body);
+		g_free(tmp);
+	}
+
+	tmp = body;
+	body = g_strdup_printf("%s</table></div>", body);
+	g_free(tmp);
+
+	page = get_html_page("Downloads", body, ref, 1);
+	g_free(ref);
+	g_free(body);
+
+	/*
+	 * update all download manager tabs as the xtp session
+	 * key has now changed. No need to update the current tab.
+	 * Already did that above.
+	 */
+	update_download_tabs(t);
+
+	load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
+	g_free(page);
+
+	return (0);
+}
diff --git a/xxxterm.h b/xxxterm.h
index 86d3e46..f2eb0a1 100644
--- a/xxxterm.h
+++ b/xxxterm.h
@@ -133,6 +133,59 @@ extern u_int32_t	swm_debug;
 #define DNPRINTF(n,x...)
 #endif
 
+/* defines */
+#define XT_NAME			("XXXTerm")
+#define XT_DIR			(".xxxterm")
+#define XT_CACHE_DIR		("cache")
+#define XT_CERT_DIR		("certs/")
+#define XT_SESSIONS_DIR		("sessions/")
+#define XT_CONF_FILE		("xxxterm.conf")
+#define XT_FAVS_FILE		("favorites")
+#define XT_QMARKS_FILE		("quickmarks")
+#define XT_SAVED_TABS_FILE	("main_session")
+#define XT_RESTART_TABS_FILE	("restart_tabs")
+#define XT_SOCKET_FILE		("socket")
+#define XT_HISTORY_FILE		("history")
+#define XT_REJECT_FILE		("rejected.txt")
+#define XT_COOKIE_FILE		("cookies.txt")
+#define XT_SAVE_SESSION_ID	("SESSION_NAME=")
+#define XT_SEARCH_FILE		("search_history")
+#define XT_COMMAND_FILE		("command_history")
+#define XT_CB_HANDLED		(TRUE)
+#define XT_CB_PASSTHROUGH	(FALSE)
+#define XT_DOCTYPE		"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
+#define XT_HTML_TAG		"<html xmlns='http://www.w3.org/1999/xhtml'>\n"
+#define XT_DLMAN_REFRESH	"10"
+#define XT_PAGE_STYLE		"<style type='text/css'>\n"		\
+				"td{overflow: hidden;"			\
+				" padding: 2px 2px 2px 2px;"		\
+				" border: 1px solid black;"		\
+				" vertical-align:top;"			\
+				" word-wrap: break-word}\n"		\
+				"tr:hover{background: #ffff99}\n"	\
+				"th{background-color: #cccccc;"		\
+				" border: 1px solid black}\n"		\
+				"table{width: 100%%;"			\
+				" border: 1px black solid;"		\
+				" border-collapse:collapse}\n"		\
+				".progress-outer{"			\
+				"border: 1px solid black;"		\
+				" height: 8px;"				\
+				" width: 90%%}\n"			\
+				".progress-inner{float: left;"		\
+				" height: 8px;"				\
+				" background: green}\n"			\
+				".dlstatus{font-size: small;"		\
+				" text-align: center}\n"		\
+				"</style>\n"
+#define XT_MAX_URL_LENGTH	(4096) /* 1 page is atomic, don't make bigger */
+#define XT_MAX_UNDO_CLOSE_TAB	(32)
+#define XT_RESERVED_CHARS	"$&+,/:;=?@ \"<>#%%{}|^~[]`"
+#define XT_PRINT_EXTRA_MARGIN	10
+#define XT_URL_REGEX		("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
+#define XT_INVALID_MARK		(-1) /* XXX this is a double, maybe use something else, like a nan */
+
+
 #define LENGTH(x)		(sizeof x / sizeof x[0])
 #define CLEAN(mask)		(mask & ~(GDK_MOD2_MASK) &	\
 				    ~(GDK_BUTTON1_MASK) &	\
@@ -179,6 +232,15 @@ extern u_int32_t	swm_debug;
 #define XT_URI_ABOUT_MARCO	("marco")
 #define XT_URI_ABOUT_STARTPAGE	("startpage")
 
+/*
+ * Session IDs.
+ * We use these to prevent people putting xxxt:// URLs on
+ * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
+ */
+#define XT_XTP_SES_KEY_SZ	8
+#define XT_XTP_SES_KEY_HEX_FMT  \
+	"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+
 
 extern int		enable_plugin_whitelist;
 extern int		enable_cookie_whitelist;
@@ -195,6 +257,10 @@ extern int		enable_scripts;
 extern int		enable_localstorage;
 extern int		show_tabs;
 extern int		tabless;
+extern int		updating_cl_tabs;
+extern int		updating_dl_tabs;
+extern int		refresh_interval;
+extern int		updating_hl_tabs;
 extern char		default_script[PATH_MAX];
 extern struct passwd	*pwd;
 extern char		runtime_settings[PATH_MAX];
@@ -202,10 +268,16 @@ extern char		work_dir[PATH_MAX];
 extern SoupCookieJar	*s_cookiejar;
 extern SoupCookieJar	*p_cookiejar;
 
+extern char		*dl_session_key;
+extern char		*hl_session_key;
+extern char		*cl_session_key;
+extern char		*fl_session_key;
+
 struct domain_list;
 extern struct domain_list	c_wl;
 extern struct domain_list	js_wl;
 extern struct domain_list	pl_wl;
+extern struct tab_list		tabs;
 
 struct tab {
 	TAILQ_ENTRY(tab)	entry;
@@ -296,12 +368,26 @@ struct domain {
 	int			handy; /* app use */
 };
 
+struct history {
+	RB_ENTRY(history)	entry;
+	const gchar		*uri;
+	const gchar		*title;
+};
+struct history_list;
+extern struct history_list	hl;
+
 struct karg {
 	int		i;
 	char		*s;
 	int		precount;
 };
 
+struct download {
+	RB_ENTRY(download)	entry;
+	int			id;
+	WebKitDownload		*download;
+	struct tab		*tab;
+};
 
 GtkWidget		*create_window(const gchar *);
 
@@ -351,9 +437,20 @@ gchar		*get_html_page(gchar *title, gchar *, gchar *, bool);
 void		load_webkit_string(struct tab *, const char *, gchar *);
 int		settings_add(char *, char *);
 void		wl_init(void);
+int		history_rb_cmp(struct history *, struct history *);
+int		remove_cookie(int);
+void		xtp_handle_dl(struct tab *, uint8_t, int);
+void		xtp_handle_hl(struct tab *, uint8_t, int);
+int		xtp_page_hl(struct tab *, struct karg *);
+char		*xtp_page_dl_row(struct tab *, char *, struct download *);
+void		update_favorite_tabs(struct tab *);
+void		update_history_tabs(struct tab *);
+void		remove_favorite(struct tab *, int);
+void		setup_inspector(struct tab *);
+int		parse_xtp_url(struct tab *, const char *);
 
 /* hooked functions */
 void		(*_soup_cookie_jar_add_cookie)(SoupCookieJar *, SoupCookie *);
 void		(*_soup_cookie_jar_delete_cookie)(SoupCookieJar *,
 		    SoupCookie *);
-void			setup_inspector(struct tab *);
+