Commit Diff


commit - 87e40628fa517c12135d3edee522705522042f79
commit + de2a69bb424696abd9b1be5232fb7a89fa5f8767
blob - d4aec734906480b830fa6a47629c715a4f34f6fd
blob + 66bce07c12d5faa0dd877834c78b5b4e27585366
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,7 @@
+2021-05-17  Omar Polo  <op@omarpolo.com>
+
+	* telescope.c (handle_imsg_got_meta): ask the user to save a page when it can't be rendered
+
 2021-05-13  Omar Polo  <op@omarpolo.com>
 
 	* gemtext.c (gemtext_free): heuristic to obtain the page title: if no h1 found, try with h2s and h3s
blob - 2b96509fdd0337da41b701583d79bb641267d74e
blob + ba64ad31f7f56325498038b378b001f58c186aea
--- fs.c
+++ fs.c
@@ -25,6 +25,7 @@
 #include <sys/stat.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -38,6 +39,7 @@ static void		 handle_quit(struct imsg*, size_t);
 static void		 handle_bookmark_page(struct imsg*, size_t);
 static void		 handle_save_cert(struct imsg*, size_t);
 static void		 handle_update_cert(struct imsg*, size_t);
+static void		 handle_file_open(struct imsg*, size_t);
 static void		 handle_session_start(struct imsg*, size_t);
 static void		 handle_session_tab(struct imsg*, size_t);
 static void		 handle_session_end(struct imsg*, size_t);
@@ -58,6 +60,7 @@ static imsg_handlerfn *handlers[] = {
 	[IMSG_BOOKMARK_PAGE] = handle_bookmark_page,
 	[IMSG_SAVE_CERT] = handle_save_cert,
 	[IMSG_UPDATE_CERT] = handle_update_cert,
+	[IMSG_FILE_OPEN] = handle_file_open,
 	[IMSG_SESSION_START] = handle_session_start,
 	[IMSG_SESSION_TAB] = handle_session_tab,
 	[IMSG_SESSION_END] = handle_session_end,
@@ -243,6 +246,27 @@ handle_update_cert(struct imsg *imsg, size_t datalen)
 end:
 	imsg_compose(ibuf, IMSG_UPDATE_CERT_OK, imsg->hdr.peerid, 0, -1,
 	    &res, sizeof(res));
+	imsg_flush(ibuf);
+}
+
+static void
+handle_file_open(struct imsg *imsg, size_t datalen)
+{
+	char	*path, *e;
+	int	 fd;
+
+	path = imsg->data;
+	if (path[datalen-1] != '\0')
+		die();
+
+	if ((fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0644)) == -1) {
+		e = strerror(errno);
+		imsg_compose(ibuf, IMSG_FILE_OPENED, imsg->hdr.peerid, 0, -1,
+		    e, strlen(e)+1);
+	} else
+		imsg_compose(ibuf, IMSG_FILE_OPENED, imsg->hdr.peerid, 0, fd,
+		    NULL, 0);
+
 	imsg_flush(ibuf);
 }
 
blob - b993a5dde66ce6b28485127813fbd24171780b1f
blob + 3a18f3c466982fafb2d0d4480a63006e8548207b
--- sandbox.c
+++ sandbox.c
@@ -33,7 +33,7 @@ sandbox_network_process(void)
 void
 sandbox_ui_process(void)
 {
-	if (pledge("stdio tty", NULL) == -1)
+	if (pledge("stdio tty recvfd", NULL) == -1)
 		err(1, "pledge");
 }
 
@@ -42,7 +42,7 @@ sandbox_fs_process(void)
 {
 	char path[PATH_MAX];
 
-        if (unveil("/tmp", "r") == -1)
+	if (unveil("/tmp", "rwc") == -1)
 		err(1, "unveil");
 
 	strlcpy(path, getenv("HOME"), sizeof(path));
@@ -55,7 +55,7 @@ sandbox_fs_process(void)
 	if (unveil(path, "rwc") == -1)
 		err(1, "unveil");
 
-	if (pledge("stdio rpath wpath cpath", NULL) == -1)
+	if (pledge("stdio rpath wpath cpath sendfd", NULL) == -1)
 		err(1, "pledge");
 }
 
blob - 90a6debb5669b160daddb376844cec7410a01f0e
blob + 1eee5e55de6e7494f46e248eec4d0c7c1e2e74f9
--- telescope.c
+++ telescope.c
@@ -29,6 +29,9 @@ static void		 handle_check_cert_user_choice(int, unsig
 static void		 handle_maybe_save_new_cert(int, unsigned int);
 static void		 handle_imsg_got_code(struct imsg*, size_t);
 static void		 handle_imsg_got_meta(struct imsg*, size_t);
+static void		 handle_maybe_save_page(int, unsigned int);
+static void		 handle_save_page_path(const char *, unsigned int);
+static void		 handle_imsg_file_opened(struct imsg*, size_t);
 static void		 handle_imsg_buf(struct imsg*, size_t);
 static void		 handle_imsg_eof(struct imsg*, size_t);
 static void		 handle_imsg_bookmark_ok(struct imsg*, size_t);
@@ -48,6 +51,7 @@ static imsg_handlerfn *handlers[] = {
 	[IMSG_BOOKMARK_OK] = handle_imsg_bookmark_ok,
 	[IMSG_SAVE_CERT_OK] = handle_imsg_save_cert_ok,
 	[IMSG_UPDATE_CERT_OK] = handle_imsg_update_cert_ok,
+	[IMSG_FILE_OPENED] = handle_imsg_file_opened,
 };
 
 static struct ohash	certs;
@@ -261,6 +265,8 @@ handle_imsg_got_meta(struct imsg *imsg, size_t datalen
 			imsg_flush(netibuf);
 		} else {
 			load_page_from_str(tab, err_pages[UNKNOWN_TYPE_OR_CSET]);
+			ui_yornp("Can't display page, wanna save?",
+			    handle_maybe_save_page, tab->id);
 		}
 	} else if (tab->code < 40) { /* 3x */
 		tab->redirect_count++;
@@ -274,17 +280,87 @@ handle_imsg_got_meta(struct imsg *imsg, size_t datalen
 	} else { /* 4x, 5x & 6x */
 		load_page_from_str(tab, err_pages[tab->code]);
 	}
+}
+
+static void
+handle_maybe_save_page(int dosave, unsigned int tabid)
+{
+	if (dosave)
+		ui_read("Save to path", handle_save_page_path, tabid);
+	else
+		stop_tab(tab_by_id(tabid));
+}
+
+static void
+handle_save_page_path(const char *path, unsigned int tabid)
+{
+	struct tab *tab;
+
+	if (path == NULL) {
+		stop_tab(tab_by_id(tabid));
+		return;
+	}
+
+	tab = tab_by_id(tabid);
+	tab->path = strdup(path);
+
+	imsg_compose(fsibuf, IMSG_FILE_OPEN, tabid, 0, -1, path, strlen(path)+1);
+	imsg_flush(fsibuf);
 }
 
 static void
+handle_imsg_file_opened(struct imsg *imsg, size_t datalen)
+{
+	struct tab	*tab;
+	char		*page;
+	const char	*e;
+	int		 l;
+
+	tab = tab_by_id(imsg->hdr.peerid);
+
+	if (imsg->fd == -1) {
+		stop_tab(tab);
+
+		e = imsg->data;
+		if (e[datalen-1] != '\0')
+			die();
+		l = asprintf(&page, "# Can't open file\n\n> %s: %s\n",
+		    tab->path, e);
+		if (l == -1)
+			die();
+		load_page_from_str(tab, page);
+		free(page);
+	} else {
+		tab->fd = imsg->fd;
+		imsg_compose(netibuf, IMSG_PROCEED, tab->id, 0, -1, NULL, 0);
+		imsg_flush(netibuf);
+	}
+}
+
+static void
 handle_imsg_buf(struct imsg *imsg, size_t datalen)
 {
 	struct tab	*tab;
+	int		 l;
+	char		*page;
 
 	tab = tab_by_id(imsg->hdr.peerid);
 
-	if (!tab->window.page.parse(&tab->window.page, imsg->data, datalen))
-		die();
+	tab->bytes += datalen;
+	if (tab->fd == -1) {
+		if (!tab->window.page.parse(&tab->window.page,
+		    imsg->data, datalen))
+			die();
+	} else {
+		write(tab->fd, imsg->data, datalen);
+		l = asprintf(&page, "Writing \"%s\"... (%zu bytes)\n",
+		    tab->path,
+		    tab->bytes);
+		if (l == -1)
+			die();
+		load_page_from_str(tab, page);
+		free(page);
+	}
 
 	ui_on_tab_refresh(tab);
 }
@@ -293,11 +369,29 @@ static void
 handle_imsg_eof(struct imsg *imsg, size_t datalen)
 {
 	struct tab	*tab;
+	int		 l;
+	char		*page;
 
 	tab = tab_by_id(imsg->hdr.peerid);
-	if (!tab->window.page.free(&tab->window.page))
-		die();
+
+	if (tab->fd == -1) {
+		if (!tab->window.page.free(&tab->window.page))
+			die();
+	} else {
+		l = asprintf(&page, "Wrote %s (%zu bytes)\n",
+		    tab->path,
+		    tab->bytes);
+		if (l == -1)
+			die();
+		load_page_from_str(tab, page);
+		free(page);
 
+		close(tab->fd);
+		tab->fd = -1;
+		free(tab->path);
+		tab->path = NULL;
+	}
+
 	ui_on_tab_refresh(tab);
 	ui_on_tab_loaded(tab);
 }
@@ -395,6 +489,13 @@ do_load_url(struct tab *tab, const char *url)
 	struct phos_uri	 uri;
 	struct proto	*p;
 	char		*t;
+
+	if (tab->fd != -1) {
+		close(tab->fd);
+		tab->fd = -1;
+		free(tab->path);
+		tab->path = NULL;
+	}
 
 	tab->trust = TS_UNKNOWN;
 
@@ -467,6 +568,14 @@ stop_tab(struct tab *tab)
 {
 	imsg_compose(netibuf, IMSG_STOP, tab->id, 0, -1, NULL, 0);
 	imsg_flush(netibuf);
+
+	if (tab->fd != -1) {
+		close(tab->fd);
+		tab->fd = -1;
+		free(tab->path);
+		tab->path = NULL;
+		load_page_from_str(tab, "Stopped.\n");
+	}
 }
 
 void
blob - 6aaa32596257531a4c76d8aa7119178979bc477f
blob + 0d150af562162b1190f37e5c5f9b35276daf95be
--- telescope.h
+++ telescope.h
@@ -50,6 +50,9 @@ enum imsg_type {
 	IMSG_UPDATE_CERT,
 	IMSG_UPDATE_CERT_OK,
 
+	IMSG_FILE_OPEN,
+	IMSG_FILE_OPENED,
+
 	IMSG_SESSION_START,
 	IMSG_SESSION_TAB,
 	IMSG_SESSION_END,
@@ -168,6 +171,10 @@ struct tab {
 	short			 loading_anim;
 	short			 loading_anim_step;
 	struct event		 loadingev;
+
+	int			 fd;
+	size_t			 bytes;
+	char			*path;
 };
 
 struct proto {
@@ -264,6 +271,7 @@ int		 ui_init(int, char * const*);
 void		 ui_on_tab_loaded(struct tab*);
 void		 ui_on_tab_refresh(struct tab*);
 void		 ui_require_input(struct tab*, int);
+void		 ui_read(const char*, void(*)(const char*, unsigned int), unsigned int);
 void		 ui_yornp(const char*, void (*)(int, unsigned int), unsigned int);
 void		 ui_notify(const char*, ...) __attribute__((format(printf, 1, 2)));
 void		 ui_end(void);
blob - e9a5a08e440f3bfa00e5e6c38f60412caa0a5da2
blob + dd0d3ccaeec27d5f8b16a8cb253e518622c2c3b5
--- ui.c
+++ ui.c
@@ -68,6 +68,9 @@ static void		 lu_select(void);
 static void		 bp_select(void);
 static void		 yornp_self_insert(void);
 static void		 yornp_abort(void);
+static void		 read_self_insert(void);
+static void		 read_abort(void);
+static void		 read_select(void);
 
 static struct vline	*nth_line(struct window*, size_t);
 static struct tab	*current_tab(void);
@@ -127,6 +130,9 @@ static char	keybuf[64];
 static void (*yornp_cb)(int, unsigned int);
 static unsigned int yornp_data;
 
+static void (*read_cb)(const char*, unsigned int);
+static unsigned int read_data;
+
 struct kmap global_map,
 	minibuffer_map,
 	*current_map,
@@ -134,7 +140,8 @@ struct kmap global_map,
 
 static struct histhead eecmd_history,
 	ir_history,
-	lu_history;
+	lu_history,
+	read_history;
 
 static int	in_minibuffer;
 
@@ -1121,6 +1128,32 @@ yornp_abort(void)
 {
 	exit_minibuffer();
 	yornp_cb(0, yornp_data);
+}
+
+static void
+read_self_insert(void)
+{
+	if (thiskey.meta || !unicode_isgraph(thiskey.cp)) {
+		global_key_unbound();
+		return;
+	}
+
+	minibuffer_self_insert();
+}
+
+static void
+read_abort(void)
+{
+	exit_minibuffer();
+	read_cb(NULL, read_data);
+}
+
+static void
+read_select(void)
+{
+        exit_minibuffer();
+	minibuffer_hist_save_entry();
+	read_cb(ministate.buf, read_data);
 }
 
 static struct vline *
@@ -1872,6 +1905,7 @@ new_tab(const char *url)
 		event_loopbreak();
 		return NULL;
 	}
+	tab->fd = -1;
 
 	TAILQ_INIT(&tab->hist.head);
 
@@ -2045,6 +2079,26 @@ ui_yornp(const char *prompt, void (*fn)(int, unsigned 
 }
 
 void
+ui_read(const char *prompt, void (*fn)(const char*, unsigned int),
+    unsigned int data)
+{
+	size_t len;
+
+	if (in_minibuffer)
+		return;
+
+	read_cb = fn;
+	read_data = data;
+	enter_minibuffer(read_self_insert, read_select, read_abort,
+	    &read_history);
+
+	len = sizeof(ministate.prompt);
+	strlcpy(ministate.prompt, prompt, len);
+	strlcat(ministate.prompt, ": ", len);
+	redraw_tab(current_tab());
+}
+
+void
 ui_notify(const char *fmt, ...)
 {
 	va_list ap;